stxxl-1.4.1/misc/fileheader.txt000644 001411 000144 00000000751 12350112610 016253 0ustar00tbusers000000 000000 /*************************************************************************** * PATH/TO/FILE.EXT * * (optional) short description * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 200X Joe XXL Coder * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ stxxl-1.4.1/misc/remove-unless000755 001411 000144 00000000250 12350112610 016146 0ustar00tbusers000000 000000 #!/bin/sh test $# = 3 || exit 1 file="$1" bn=`dirname "$file"`/`basename "$1" "$2"` predep="$bn$3" if [ ! -e "$predep" ]; then echo "rm -f $file" rm -f "$file" fi stxxl-1.4.1/misc/concat-lines000755 001411 000144 00000001541 12350112610 015725 0ustar00tbusers000000 000000 #!/usr/bin/perl ############################################################################ # misc/concat-lines # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2007 Johannes Singler # Copyright (C) 2010 Andreas Beckmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ # Concatenate the lines of file $ARGV[0] which both satisfy regular expression $ARGV[1] $regex = $ARGV[0]; shift; $former = ''; while($line = <>) { if(!($line =~ /$regex/) || !($former =~ /$regex/)) { print("\n"); } else { print("\t"); } chomp($line); print($line); $former = $line; } print("\n"); stxxl-1.4.1/misc/analyze-include-dependencies.pl000755 001411 000144 00000015670 12350112610 021500 0ustar00tbusers000000 000000 #!/usr/bin/perl -w ############################################################################ # misc/analyze-include-dependencies.pl # # A tool to analyze #include dependencies and find unwanted cycles. # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2007-2008 Andreas Beckmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ use File::Basename; $debug = 0; $fakeheaders = 'fakeinclude'; # fake headers currently needed to prevent canonical name errors: # backward_warning.h -> backward/backward_warning.h # io.h -> #nothing # platform.hpp -> boost/thread/detail/platform.hpp # thread_data.hpp -> boost/thread/pthread/thread_data.hpp # thread_heap_alloc.hpp -> boost/thread/detail/thread_heap_alloc.hpp # vstring.tcc -> ext/vstring.tcc $mcstl = 0; $mcstlpath = 'c++'; $stxxl = 1; $stxxlpath = 'include'; #$CXX = 'g++-4.2'; $CXX = 'g++-4.4 -std=c++0x'; $cxxtarget = 'foo-bar-gnu'; $cxxheaderpath = undef; $gcctargetheaderpath = undef; @includepath = (); push @includepath, $fakeheaders if $fakeheaders; push @includepath, $mcstlpath if $mcstl; push @includepath, $stxxlpath if $stxxl; %breakloops = qw( libio.h _G_config.h sys/cdefs.h features.h sys/ucontext.h signal.h debug/formatter.h debug/debug.h debug/bitset bitset debug/deque deque debug/list list debug/map map debug/set set debug/vector vector bits/sstream.tcc sstream debug/hash_map ext/hash_map debug/hash_set ext/hash_set debug/unordered_map unordered_map debug/unordered_set unordered_set xmmintrin.h emmintrin.h ext/vstring.h vstring.tcc wctype.h wchar.h stdatomic.h cstdatomic algorithm parallel/algorithm numeric parallel/numeric bits/stl_algobase.h parallel/algobase.h parallel/partition.h parallel/sort.h boost/type_traits/is_class.hpp boost/type_traits/is_scalar.hpp boost/type_traits/msvc/remove_cv.hpp boost/type_traits/is_pointer.hpp boost/preprocessor/list/fold_left.hpp boost/preprocessor/control/while.hpp boost/preprocessor/list/fold_right.hpp boost/preprocessor/control/while.hpp ); %seen = (); @todo = (); %in = (); %out = (); $out{'MISSING'} = []; %canonical = (); sub get_file_list($;@) { my $path = shift; my @patterns = @_; @patterns = ('*') unless scalar @patterns; my @l; foreach my $p (@patterns){ foreach (glob "$path/$p") { if (-f $_) { my $x = $path; $x =~ s/([+])/\\$1/g; s|^$x/||; push @l, $_; } } } print "GLOB $path @patterns: @l\n" if $debug; return @l; } sub get_cxx_include_paths($) { my $cxx = shift; open CXXOUT, "$cxx -E -v -xc++ /dev/null 2>&1 >/dev/null |" or die $!; my $ok = 0; while () { chomp; $cxxtarget = $1 if /^Target: (.*)/; $ok = 1 if /^#include .\.\.\.. search starts here:$/; $ok = 0 if /^End of search list\.$/; if ($ok && s/^ //) { next if /backward/; push @includepath, $_; unless ($cxxheaderpath) { $cxxheaderpath = $_ if m|/c\+\+|; } unless ($gcctargetheaderpath) { $gcctargetheaderpath = $_ if (m|/$cxxtarget/| && ! m|/c\+\+|); } } } close CXXOUT; print "TARGET: \t$cxxtarget\n"; print "HEADERS c++: \t$cxxheaderpath\n"; print "HEADERS gcc: \t$gcctargetheaderpath\n"; print "INCLUDES: \t@includepath\n"; } sub find_header($;$) { my $header = shift; my $relpath = dirname(shift || '.'); foreach $_ (@includepath, $relpath) { my $file = "$_/$header"; if (-f $file) { if (exists $canonical{$file} && $canonical{$file} ne $header) { print "CANONICAL MISMATCH: $file $header $canonical{$file}\n"; } $canonical{$file} = $header; print "FOUND: $header as $file\n" if $debug; return [ $header, "$file" ]; } } print "NOT FOUND: $header\n"; return [$header, undef]; } sub parse_header($) { my $arg = shift; my $header = $$arg[0]; my $file = $$arg[1]; return if $seen{$file || $header}; $seen{$file || $header} = 1; if ($file) { print "PROCESSING: $header \t($file)\n"; print "OUCH: $header\n" if exists $$out{$header}; $out{$header} = []; open HEADER,"<$file" or die $!; while ($_ =
) { if (/^\s*#\s*include\s*["<]([^">]*)[">]/) { my $dep_header = $1; print "DEP: $header \t$dep_header\n"; push @{$out{$header}}, $dep_header; push @{$in{$dep_header}}, $header; push @todo, find_header($dep_header, $file); } } close HEADER; } else { print "NOT FOUND: $header\n"; push @{$out{$header}}, 'MISSING'; push @{$in{'MISSING'}}, $header; } } get_cxx_include_paths($CXX); @cxxheaders = get_file_list($cxxheaderpath); #@cxxbwheaders = get_file_list("$cxxheaderpath/backward"); push @cxxbwheaders, get_file_list($cxxheaderpath, "backward/*"); @cxxextheaders = get_file_list($cxxheaderpath, 'ext/*'); @cxxbitsheaders = get_file_list($cxxheaderpath, 'bits/*'); @cxxtargetheaders = get_file_list("$cxxheaderpath/$cxxtarget", '*', 'bits/*'); #@cxxtr1headers = get_file_list($cxxheaderpath, 'tr1/*', 'tr1_impl/*'); @gcctargetheaders = get_file_list($gcctargetheaderpath, '*.h', 'bits/*.h'); if ($mcstl) { @mcstlheaders = get_file_list($mcstlpath); @mcstlmetaheaders = get_file_list($mcstlpath, 'meta/*'); @mcstlbitsheaders = get_file_list($mcstlpath, 'bits/*'); push @todo, find_header($_) foreach ( sort(@mcstlheaders), sort(@mcstlmetaheaders), sort(@mcstlbitsheaders), ); } if ($stxxl) { @stxxlheaders = get_file_list($stxxlpath, '*', 'stxxl/*'); @stxxlbitsheaders = get_file_list($stxxlpath, 'bits/*', 'stxxl/bits/*', 'stxxl/bits/*/*'); push @todo, find_header($_) foreach ( sort(@stxxlheaders), sort(@stxxlbitsheaders), ); } push @todo, find_header($_) foreach ( sort(@cxxheaders), sort(@cxxbwheaders), sort(@cxxextheaders), sort(@cxxbitsheaders), sort(@cxxtargetheaders), #sort(@cxxtr1headers), sort(@gcctargetheaders), ); while (@todo) { parse_header(shift @todo); } %odgr = (); @zodgr = (); if ($debug) { foreach (sort keys %out) { print "\t$_\n\t=> (".(scalar @{$out{$_}}).")\t"; foreach (@{$out{$_}}) { print " $_"; } print "\n\t<= (".(scalar @{$in{$_}}).")\t"; foreach (@{$in{$_}}) { print " $_"; } print "\n"; } } foreach (sort keys %out) { $odgr{$_} = scalar @{$out{$_}}; push @zodgr, $_ if $odgr{$_} == 0; } while (my ($h, $l) = each %breakloops) { next unless exists $out{$h}; foreach (@{$out{$h}}) { if ($l eq $_) { print "BREAK LOOP: $h --> $l\n"; --$odgr{$h}; push @zodgr, $h if $odgr{$h} == 0; $_ .= ".UNLOOP"; } } foreach (@{$in{$l}}) { if ($h eq $_) { $_ .= ".UNLOOP"; } } } print "TOPSORT:\n"; while (@zodgr) { $curr = shift @zodgr; next unless exists $out{$curr}; print "\t$curr"; foreach (@{$in{$curr}}) { --$odgr{$_}; push @zodgr, $_ if $odgr{$_} == 0; print " $_(".$odgr{$_}.")" if $debug; } print "\n"; delete $out{$curr}; } print "CYCLIC(".(scalar keys %out)."):\n"; foreach (sort keys %out) { print "\t$_($odgr{$_}):"; foreach (@{$out{$_}}) { print " $_" if exists $out{$_}; } print "\n"; } stxxl-1.4.1/misc/do-release.txt000644 001411 000144 00000005310 12404624022 016204 0ustar00tbusers000000 000000 -*- mode: org -*- ############################################################################ # misc/do-release.txt # # Notes about the procedure of publishing a release. # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ #+STARTUP: showall * Prerelease Checks ** Run misc/analyze-source.pl (-w) to uncrustify everything. ** Check that the library passes all build tests on all supported systems. ** Check that the CHANGELOG and TODO is up to date. * Release Procedure ** Update CHANGELOG -> change from (unreleased) to (release date) [file:../CHANGELOG] ** Update the following files with the correct version number: [file:../CMakeLists.txt] -> STXXL_VERSION_* [file:../Doxyfile] -> PROJECT_NUMBER ** Git commit and add signed tag git commit -am "stxxl-1.4.x" git tag -s -a 1.4.x -m "Final release 1.4.x" git push git push --tags ** Package final source tarball and zip git reset --hard master git clean -d -f -x mkdir build; cd build cmake .. make package_source # check tarball tar tvvzf stxxl-1.4.x.tar.gz # move stxxl-1.4.x.tar.gz out of build ** Generate stand-alone doxygen tarball: # check doxygen version! -> 1.8.5 doxygen --version git reset --hard master git clean -d -f -x mv include/stxxl/bits/config.h.in include/stxxl/bits/config.h doxygen # optimize pngs optipng -o7 doxygen-html/*.png # tag HTML with version and link sed -i "s@
  • Generated@
  • STXXL 1.4.x - Generated@" doxygen-html/*.html tar czf stxxl-1.4.x-doxygen.tar.gz doxygen-html # check tarball tar tvvzf stxxl-1.4.x-doxygen.tar.gz # move stxxl-1.4.x-doxygen.tar.gz out of build ** Upload both tarballs to sourceforge ** Update website with new versions ** Let website generator create tag/master for current git repository. - copy tag/master into git website repository - optipng -o7 tag/1.4.x/*.png * Post-Release Procedure ** Update CHANGELOG -> change to next version 1.4.99 (unreleased) [file:../CHANGELOG] ** Update the following files with the new prerelease version number: [file:../CMakeLists.txt] -> STXXL_VERSION_* [file:../Doxyfile] -> PROJECT_NUMBER ** Git commit and add signed tag git commit -am "Tagging trunk with unreleased 1.4-dev" * Announcements ** Post announcement messages on sourceforge "forum" and "news" sections ** Post announcement on freecode.net stxxl-1.4.1/misc/diskbench.mk000644 001411 000144 00000020632 12350112610 015705 0ustar00tbusers000000 000000 ############################################################################ # misc/diskbench.mk # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2008-2011 Andreas Beckmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ HOST ?= unknown FILE_SIZE ?= $(or $(SIZE),100) # GiB BLOCK_SIZE ?= $(or $(STEP),256) # MiB BATCH_SIZE ?= 1 # blocks DIRECT_IO ?= yes # unset to disable O_DIRECT SYNC_IO ?= no # set to 'yes' to enable O_SYNC|O_DSYNC|O_RSYNC FILE_TYPE ?= syscall FILE_RESIZE ?= no disk2file ?= /stxxl/sd$1/stxxl DISKS_1by1 ?= a b c d DISKS_a ?= a DISKS_ab ?= a b DISKS_abcd ?= a b c d DISKS_a_ro ?= a FLAGS_a_ro ?= R DISKS ?= abcd $(DISKS_1by1) ab a_ro SIP_NUM_BLOCKS ?= 0 SIP_CHUNK_BLOCKS?= 1 SIP_BLOCK_SIZE ?= 0 DISKBENCH_TITLE ?= STXXL Disk Benchmark $(DISKNAME) B=$(strip $(BATCH_SIZE))x$(call format_block_size,$(BLOCK_SIZE)) @ $(HOST) DISKAVG_TITLE ?= STXXL Disk Benchmark $(DISKNAME) @ $(HOST)$(if $(DISKNAME2),\n$(DISKNAME2)) ifndef MISC_BINDIR MISC_BINDIR := $(dir $(lastword $(MAKEFILE_LIST))) endif DISKBENCH_BINDIR?= $(MISC_BINDIR)/../io DISKBENCH ?= benchmark_disks.stxxl.bin SCATTERINPLACE ?= iobench_scatter_in_place.stxxl.bin ECHO ?= echo pipefail ?= set -o pipefail; $(foreach d,$(DISKS_1by1),$(eval DISKS_$d ?= $d)) ifeq ($(SHELL),/bin/sh) SHELL = bash endif DISKBENCH_FLAGS += $(if $(filter y yes Y YES,$(DIRECT_IO)),,--no-direct) DISKBENCH_FLAGS += $(if $(filter y yes Y YES,$(SYNC_IO)),--sync) DISKBENCH_FLAGS += --file-type=$(strip $(FILE_TYPE)) DISKBENCH_FLAGS += $(if $(filter y yes Y YES,$(FILE_RESIZE)),--resize) define do-some-disks $(if $(filter ???,$(strip $(BLOCK_SIZE))),$(error ERROR: BLOCK_SIZE=$(strip $(BLOCK_SIZE)))) -$(pipefail) \ $(if $(IOSTAT_PLOT_RECORD_DATA),$(IOSTAT_PLOT_RECORD_DATA) -p $(@:.log=)) \ $(DISKBENCH_BINDIR)/$(DISKBENCH) $(DISKBENCH_FLAGS) 0 $(strip $(FILE_SIZE)) $(strip $(BLOCK_SIZE)) $(strip $(BATCH_SIZE)) $(FLAGS_$*) $(FLAGS_EX) $(foreach d,$(DISKS_$*),$(call disk2file,$d)) | tee $@ endef define do-some-disks-sip -$(pipefail) \ $(if $(IOSTAT_PLOT_RECORD_DATA),$(IOSTAT_PLOT_RECORD_DATA) -p $(@:.log=)) \ $(DISKBENCH_BINDIR)/$(SCATTERINPLACE) $(strip $(SIP_NUM_BLOCKS)) $(strip $(SIP_CHUNK_BLOCKS)) $(strip $(SIP_BLOCK_SIZE)) $(foreach d,$(DISKS_$*),$(call disk2file,$d)) | tee $@ endef $(HOST)-%.cr.log: $(if $(keep-old-file),,$(RM) $(foreach d,$(DISKS_$*),$(call disk2file,$d))) $(do-some-disks) $(HOST)-%.crx.log: FLAGS_EX = W $(HOST)-%.crx.log: $(if $(keep-old-file),,$(RM) $(foreach d,$(DISKS_$*),$(call disk2file,$d))) $(do-some-disks) # interleaved write-read-test $(HOST)-%.wr.log: $(do-some-disks) # scanning write $(HOST)-%.wrx.log: FLAGS_EX = W $(HOST)-%.wrx.log: $(do-some-disks) # scanning read $(HOST)-%.rdx.log: FLAGS_EX = R $(HOST)-%.rdx.log: $(do-some-disks) # scatter-in-place $(HOST)-%.sip.log: $(do-some-disks-sip) all: crx wr ex crx: $(foreach d,$(DISKS_1by1),$(HOST)-$d.crx.log) cr: $(foreach d,$(DISKS_1by1),$(HOST)-$d.cr.log) cr+: $(foreach d,$(DISKS),$(HOST)-$d.crx.log) wr: $(foreach d,$(DISKS),$(HOST)-$d.wr.log) wrx: $(foreach d,$(DISKS_1by1),$(HOST)-$d.wrx.log) wr+: $(foreach d,$(DISKS),$(HOST)-$d.wrx.log) rdx: $(foreach d,$(DISKS_1by1),$(HOST)-$d.rdx.log) rd+: $(foreach d,$(DISKS),$(HOST)-$d.rdx.log) ex: $(foreach d,$(DISKS_1by1),$(HOST)-$d.wrx.log $(HOST)-$d.rdx.log) ex+: $(foreach d,$(DISKS),$(HOST)-$d.wrx.log $(HOST)-$d.rdx.log) sip: $(foreach d,$(DISKS_1by1),$(HOST)-$d.sip.log) all-sizes-targets ?= cr+ wr ex+ all-sizes: for d in $(wildcard 0016iMB 0064MB 0004MB 0256MB 0001MB 1024MB *MB) ; do make -C $$d $(all-sizes-targets) ; done all-sizes-raw: keep-old-file=1 all-sizes-raw: all-sizes plot: $(HOST).gnuplot gnuplot $< dotplot: $(HOST).d.gnuplot gnuplot $< avgplot: $(HOST)-avg.gnuplot gnuplot $< avg3plot: $(HOST)-avg3.gnuplot gnuplot $< # $1 = logfile, $2 = column extract_average = $(if $(wildcard $1),$(shell grep ' Average over ' $1 | awk '{ print $$($2+1) }'),......) # $1 = logfile, $2 = disk, $3 = column, $4 = label # (does not plot if avg = nan) define plotline $(if $(wildcard $1),$(if $(filter nan,$(call extract_average,$1,$3)),,$(ECHO) ' "$1" using ($$3/1024):($$$3) w l title "$2 $4 ($(call extract_average,$1,$3))", \' >> $@)) endef # $1 = logfile, $2 = disk define plotline-cr1 $(call plotline,$1,$2,7,cr1) endef define plotline-cr $(call plotline,$1,$2,7,cr) endef define plotline-crx $(call plotline,$1,$2,7,crx) endef define plotline-wr $(call plotline,$1,$2,7,wr) endef define plotline-rd $(call plotline,$1,$2,14,rd) endef define plotline-wrx $(call plotline,$1,$2,7,wrx) endef define plotline-rdx $(call plotline,$1,$2,14,rdx) endef # $1 = disk letter disk2label ?= sd$1 disks2label ?= sd[$1] DISKNAME ?= unknown disk DISKNAME2 ?=# optional second line describing the test environment PLOTXMAX ?= 475 PLOTYMAX ?= 120 AVGPLOTYMAX ?= $(PLOTYMAX) fmt_block_size_2560000B ?= 2.5 fmt_block_size_12800000B ?= 12.5 fmt_block_size_51200000B ?= 50 format_block_size = $(or $(fmt_block_size_$(strip $1)),$(strip $1))MiB $(HOST).gnuplot: $(MAKEFILE_LIST) $(wildcard *.log) $(RM) $@ $(ECHO) 'set title "$(DISKBENCH_TITLE)"' >> $@ $(ECHO) 'set xlabel "Disk offset [GiB]"' >> $@ $(ECHO) 'set ylabel "Bandwidth per disk [MiB/s]"' >> $@ $(ECHO) '' >> $@ $(ECHO) 'plot [0:$(PLOTXMAX)] [0:$(PLOTYMAX)] \' >> $@ $(foreach d,$(DISKS_1by1),\ $(call plotline-cr1,$(HOST)-$d.cr1.log,$(call disk2label,$d)) \ $(call plotline-crx,$(HOST)-$d.crx.log,$(call disk2label,$d)) \ $(call plotline-cr,$(HOST)-$d.cr.log,$(call disk2label,$d)) \ $(call plotline-wr,$(HOST)-$d.wr1.log,$(call disk2label,$d)) \ $(call plotline-rd,$(HOST)-$d.wr1.log,$(call disk2label,$d)) \ $(call plotline-wr,$(HOST)-$d.wr.log,$(call disk2label,$d)) \ $(call plotline-rd,$(HOST)-$d.wr.log,$(call disk2label,$d)) \ $(call plotline-wrx,$(HOST)-$d.wrx.log,$(call disk2label,$d)) \ $(call plotline-rdx,$(HOST)-$d.rdx.log,$(call disk2label,$d)) \ ) $(foreach d,$(filter-out $(DISKS_1by1),$(DISKS)),\ $(call plotline-crx,$(HOST)-$d.crx.log,$(call disks2label,$d)) \ $(call plotline-wr,$(HOST)-$d.wr.log,$(call disks2label,$d)) \ $(call plotline-rd,$(HOST)-$d.wr.log,$(call disks2label,$d)) \ $(call plotline-wrx,$(HOST)-$d.wrx.log,$(call disks2label,$d)) \ $(call plotline-rdx,$(HOST)-$d.rdx.log,$(call disks2label,$d)) \ ) $(ECHO) ' "nothing" notitle' >> $@ $(ECHO) '' >> $@ $(ECHO) 'pause -1' >> $@ $(ECHO) '' >> $@ $(ECHO) 'set title "$(call GNUPLOT_PS_STRING_ESCAPE,$(DISKBENCH_TITLE))"' >> $@ $(ECHO) 'set term postscript enhanced $(GNUPLOT_PS_COLOR) 10' >> $@ $(ECHO) 'set output "$(HOST).ps"' >> $@ $(ECHO) '$(GNUPLOTFILEINFO)' >> $@ $(ECHO) 'replot' >> $@ $(HOST).d.gnuplot: $(HOST).gnuplot sed -e 's/ w l / w d lw 2 /' $< > $@ $(HOST)-avg.dat: $(MISC_BINDIR)/diskbench-avgdat.sh $(wildcard *KB/*.log *MB/*.log) $(MISC_BINDIR)/diskbench-avgdat.sh $(wildcard *KB *MB) > $@ $(HOST)-avg.gnuplot: $(HOST)-avg.dat $(MAKEFILE_LIST) $(RM) $@ $(ECHO) 'set title "$(DISKAVG_TITLE)"' >> $@ $(ECHO) 'set xlabel "Block Size [MiB]"' >> $@ $(ECHO) 'set ylabel "Average Sequential Bandwidth [MiB/s]"' >> $@ $(ECHO) 'set key bottom' >> $@ $(ECHO) '' >> $@ $(ECHO) 'plot [] [0:$(AVGPLOTYMAX)] \' >> $@ $(ECHO) ' "$(HOST)-avg.dat" using 0:2:xtic(1) w lp lt 1 pt 1 title "crx", \' >> $@ $(ECHO) ' "$(HOST)-avg.dat" using 0:3:xtic(1) w lp lt 2 pt 2 title "wr", \' >> $@ $(ECHO) ' "$(HOST)-avg.dat" using 0:4:xtic(1) w lp lt 3 pt 3 title "rd", \' >> $@ $(ECHO) ' "$(HOST)-avg.dat" using 0:5:xtic(1) w lp lt 4 pt 4 title "wrx", \' >> $@ $(ECHO) ' "$(HOST)-avg.dat" using 0:6:xtic(1) w lp lt 5 pt 5 title "rdx", \' >> $@ $(ECHO) ' "nothing" notitle' >> $@ $(ECHO) '' >> $@ $(ECHO) 'pause -1' >> $@ $(ECHO) '' >> $@ $(ECHO) 'set term png size 800,600' >> $@ $(ECHO) 'set output "$(HOST)-avg.png"' >> $@ $(ECHO) 'replot' >> $@ $(ECHO) '' >> $@ $(ECHO) 'set title "$(call GNUPLOT_PS_STRING_ESCAPE,$(DISKAVG_TITLE))"' >> $@ $(ECHO) 'set term postscript enhanced $(GNUPLOT_PS_COLOR)' >> $@ $(ECHO) 'set output "$(HOST)-avg.ps"' >> $@ $(ECHO) '$(GNUPLOTFILEINFO)' >> $@ $(ECHO) 'replot' >> $@ $(HOST)-avg3.gnuplot: $(HOST)-avg.gnuplot grep -v -E '0:[34]:xtic' $< | sed -e 's/"crx"/"create"/g;s/"wrx"/"write"/g;s/"rdx"/"read"/g;/set output/s/-avg\./-avg3./g' > $@ -include iostat-plot.mk stxxl-1.4.1/misc/record-load-iostat000755 001411 000144 00000002775 12350112610 017054 0ustar00tbusers000000 000000 #!/bin/sh ############################################################################ # misc/record-load-iostat # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2008 Andreas Beckmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ # Usage: $0 [ -p outfileprefix ] command PID_IOSTAT="" PID_LOAD="" RETVAL="1" cleanup() { if [ -n "$PID_IOSTAT" ] || [ -n "$PID_LOAD" ]; then kill $PID_IOSTAT $PID_LOAD fi exit $RETVAL } loadavg() { while true do cat /proc/loadavg sleep 1 done } trap cleanup 1 2 3 15 OUTFILEPREFIX=iostat_`hostname`_`date +%F--%H-%M` if [ "$1" = "-p" ]; then OUTFILEPREFIX="$2" shift shift fi if [ -z "$DONT_WAIT_FOR_LOW_LOAD" ]; then # wait at most 120 seconds for load to drop below 1.00 loop="" load=`cut -d. -f1 /proc/loadavg` echo -n "Waiting for load to drop below 1.00 " >/dev/tty while [ "$load" != "0" ] && [ "$loop" != "........................" ] do echo -n "." >/dev/tty sleep 5 loop="$loop." load=`cut -d. -f1 /proc/loadavg` done echo " ==> `cut -d' ' -f1 /proc/loadavg`" >/dev/tty fi #run command passed as the argument concurrently to iostat #dump I/O stat every second iostat -x -m -t $IOSTAT_FLAGS 1 >$OUTFILEPREFIX.iostat & PID_IOSTAT=$! loadavg >$OUTFILEPREFIX.loadavg & PID_LOAD=$! "$@" RETVAL=$? cleanup exit $RETVAL stxxl-1.4.1/misc/analyze-source.pl000755 001411 000144 00000030417 12410750556 016743 0ustar00tbusers000000 000000 #!/usr/bin/perl -w ############################################################################ # misc/analyze-source.pl # # Perl script to test source header files, license headers and write # AUTHORS from copyright statements. # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013-2014 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ # print multiple email addresses my $email_multimap = 0; # launch emacsen for each error my $launch_emacs = 0; # write changes to files (dangerous!) my $write_changes = 0; # function testing whether to uncrustify a path sub filter_uncrustify($) { my ($path) = @_; return 0 if $path =~ m"^include/stxxl/bits/containers/matrix"; # misformats config.h.in! return 0 if $path =~ m"^include/stxxl/bits/config.h.in"; # doesnt work with comments return 0 if $path =~ m"^doc/"; return 1; } use strict; use warnings; use Text::Diff; use File::stat; my %includemap; my %authormap; sub expect_error($$$$) { my ($path,$ln,$str,$expect) = @_; print("Bad header line $ln in $path\n"); print("Expected $expect\n"); print("Got $str\n"); system("emacsclient -n $path") if $launch_emacs; } sub expect($$\$$) { my ($path,$ln,$str,$expect) = @_; if ($$str ne $expect) { expect_error($path,$ln,$$str,$expect); $$str = $expect; } } sub expect_re($$\$$) { my ($path,$ln,$str,$expect) = @_; if ($$str !~ m/$expect/) { expect_error($path,$ln,$$str,"/$expect/"); } } # check equality of two arrays sub array_equal { my ($a1ref,$a2ref) = @_; my @a1 = @{$a1ref}; my @a2 = @{$a2ref}; return 0 if scalar(@a1) != scalar(@a2); for my $i (0..scalar(@a1)-1) { return 0 if $a1[$i] ne $a2[$i]; } return 1; } # run $text through a external pipe (@program) sub filter_program { my $text = shift; my @program = @_; # fork and read output my $child1 = open(my $fh, "-|") // die("$0: fork: $!"); if ($child1 == 0) { # fork and print text my $child2 = open(STDIN, "-|") // die("$0: fork: $!"); if ($child2 == 0) { print $text; exit; } else { exec(@program) or die("$0: exec: $!"); } } else { my @output = <$fh>; close($fh) or warn("$0: close: $!"); return @output; } } sub process_cpp { my ($path) = @_; # special files return if $path eq "tools/benchmarks/app_config.h"; # check permissions my $st = stat($path) or die("Cannot stat() file $path: $!"); if ($st->mode & 0133) { print("Wrong mode ".sprintf("%o", $st->mode)." on $path\n"); if ($write_changes) { chmod(0644, $path) or die("Cannot chmod() file $path: $!"); } } # read file open(F, $path) or die("Cannot read file $path: $!"); my @data = ; close(F); my @origdata = @data; # put all #include lines into the includemap foreach my $ln (@data) { if ($ln =~ m!\s*#\s*include\s*([<"]\S+[">])!) { $includemap{$1}{$path} = 1; } } # check #include "stxxl..." use { foreach my $ln (@data) { if ($ln =~ m@\s*#\s*include\s*"stxxl\S+"@) { print("#include \"stxxl...\" found in $path\n"); print $ln."\n"; system("emacsclient -n $path") if $launch_emacs; } } } # check for assert() in test cases if ($path =~ /^test/) { foreach my $ln (@data) { if ($ln =~ m!assert\(!) { print("found assert() in test $path\n"); } } } # check for \brief doxygen commands { foreach my $ln (@data) { if ($ln =~ m!\\brief!) { print("found brief command in $path\n"); system("emacsclient -n $path") if $launch_emacs; } } } # check for double underscores { foreach my $ln (@data) { next if $ln =~ /^\s*#(if|elif|define|error)/; next if $path eq "include/stxxl/bits/common/types.h"; if ($ln =~ m@\s__(?!(gnu_parallel|gnu_cxx|glibcxx|typeof__|attribute__|sync_add_and_fetch|FILE__|LINE__|FUNCTION__))@) { print("double-underscore found in $path\n"); print $ln."\n"; system("emacsclient -n $path") if $launch_emacs; } } } # check source header my $i = 0; if ($data[$i] =~ m!// -.*- mode:!) { ++$i; } # skip emacs mode line expect($path, $i, $data[$i], "/".('*'x75)."\n"); ++$i; expect($path, $i, $data[$i], " * $path\n"); ++$i; expect($path, $i, $data[$i], " *\n"); ++$i; # skip over comment while ($data[$i] !~ /^ \* Part of the STXXL/) { expect_re($path, $i, $data[$i], '^ \*( .*)?\n$'); return unless ++$i < @data; } # check "Part of STXXL" expect($path, $i-1, $data[$i-1], " *\n"); expect($path, $i, $data[$i], " * Part of the STXXL. See http://stxxl.sourceforge.net\n"); ++$i; expect($path, $i, $data[$i], " *\n"); ++$i; # read authors while ($data[$i] =~ /^ \* Copyright \(C\) ([0-9-]+(, [0-9-]+)*) (?[^0-9<]+)( <(?[^>]+)>)?\n/) { #print "Author: $+{name} - $+{mail}\n"; $authormap{$+{name}}{$+{mail} || ""} = 1; return unless ++$i < @data; } # otherwise check license expect($path, $i, $data[$i], " *\n"); ++$i; expect($path, $i, $data[$i], " * Distributed under the Boost Software License, Version 1.0.\n"); ++$i; expect($path, $i, $data[$i], " * (See accompanying file LICENSE_1_0.txt or copy at\n"); ++$i; expect($path, $i, $data[$i], " * http://www.boost.org/LICENSE_1_0.txt)\n"); ++$i; expect($path, $i, $data[$i], " ".('*'x74)."/\n"); ++$i; # check include guard name if ($path =~ m!^include/stxxl/bits/.*\.(h|h.in)$!) { expect($path, $i, $data[$i], "\n"); ++$i; # construct include guard macro name: STXXL_FILE_NAME_HEADER my $guard = $path; $guard =~ s!include/stxxl/bits/!stxxl/!; $guard =~ tr!/!_!; $guard =~ s!\.h(\.in)?$!!; $guard = uc($guard)."_HEADER"; #print $guard."\n"; expect($path, $i, $data[$i], "#ifndef $guard\n"); ++$i; expect($path, $i, $data[$i], "#define $guard\n"); ++$i; my $n = scalar(@data)-1; if ($data[$n] =~ m!// vim:!) { --$n; } # skip vim expect($path, $n, $data[$n], "#endif // !$guard\n"); } # run uncrustify if in filter if (filter_uncrustify($path)) { my $data = join("", @data); my @uncrust = filter_program($data, "uncrustify", "-q", "-c", "misc/uncrustify.cfg", "-l", "CPP"); # manually add blank line after "namespace xyz {" and before "} // namespace xyz" my $namespace = 0; for(my $i = 0; $i < @uncrust-1; ++$i) { if ($uncrust[$i] =~ m!^namespace \S+ {!) { splice(@uncrust, $i+1, 0, "\n"); ++$namespace; } if ($uncrust[$i] =~ m!^} +// namespace!) { splice(@uncrust, $i, 0, "\n"); ++$i; --$namespace; } } if ($namespace != 0) { print "$path\n"; print " NAMESPACE MISMATCH!\n"; system("emacsclient -n $path") if $launch_emacs; } if (!array_equal(\@data,\@uncrust)) { print "$path\n"; print diff(\@data, \@uncrust); @data = @uncrust; system("emacsclient -n $path") if $launch_emacs; } } if ($write_changes && !array_equal(\@data,\@origdata)) { open(F, "> $path") or die("Cannot write $path: $!"); print(F join("", @data)); close(F); } } sub process_pl_cmake { my ($path) = @_; # check permissions if ($path !~ /\.pl$/) { my $st = stat($path) or die("Cannot stat() file $path: $!"); if ($st->mode & 0133) { print("Wrong mode ".sprintf("%o", $st->mode)." on $path\n"); if ($write_changes) { chmod(0644, $path) or die("Cannot chmod() file $path: $!"); } } } # read file open(F, $path) or die("Cannot read file $path: $!"); my @data = ; close(F); # check source header my $i = 0; if ($data[$i] =~ m/#!/) { ++$i; } # bash line expect($path, $i, $data[$i], ('#'x76)."\n"); ++$i; expect($path, $i, $data[$i], "# $path\n"); ++$i; expect($path, $i, $data[$i], "#\n"); ++$i; # skip over comment while ($data[$i] !~ /^# Part of the STXXL/) { expect_re($path, $i, $data[$i], '^#( .*)?\n$'); return unless ++$i < @data; } # check "Part of STXXL" expect($path, $i-1, $data[$i-1], "#\n"); expect($path, $i, $data[$i], "# Part of the STXXL. See http://stxxl.sourceforge.net\n"); ++$i; expect($path, $i, $data[$i], "#\n"); ++$i; # read authors while ($data[$i] =~ /^# Copyright \(C\) ([0-9-]+(, [0-9-]+)*) (?[^0-9<]+)( <(?[^>]+)>)?\n/) { #print "Author: $+{name} - $+{mail}\n"; $authormap{$+{name}}{$+{mail} || ""} = 1; return unless ++$i < @data; } # otherwise check license expect($path, $i, $data[$i], "#\n"); ++$i; expect($path, $i, $data[$i], "# Distributed under the Boost Software License, Version 1.0.\n"); ++$i; expect($path, $i, $data[$i], "# (See accompanying file LICENSE_1_0.txt or copy at\n"); ++$i; expect($path, $i, $data[$i], "# http://www.boost.org/LICENSE_1_0.txt)\n"); ++$i; expect($path, $i, $data[$i], ('#'x76)."\n"); ++$i; } ### Main ### foreach my $arg (@ARGV) { if ($arg eq "-w") { $write_changes = 1; } elsif ($arg eq "-e") { $launch_emacs = 1; } elsif ($arg eq "-m") { $email_multimap = 1; } else { print "Unknown parameter: $arg\n"; } } (-e "include/stxxl.h") or die("Please run this script in the STXXL source base directory."); use File::Find; my @filelist; find(sub { !-d && push(@filelist, $File::Find::name) }, "."); foreach my $file (@filelist) { $file =~ s!./!! or die("File does not start ./"); if ($file =~ /\.(h|cpp|h.in)$/) { process_cpp($file); } elsif ($file =~ m!^doc/[^/]*\.dox$!) { process_cpp($file); } elsif ($file =~ m!^include/stxxl/[^/]*$!) { process_cpp($file); } elsif ($file =~ m!^include/stxxl/[^/]*$!) { process_cpp($file); } elsif ($file =~ m!\.pl$!) { process_pl_cmake($file); } elsif ($file =~ m!/CMakeLists\.txt$!) { process_pl_cmake($file); } # recognize further files elsif ($file =~ m!^[^/]*$!) { # files in source root } elsif ($file =~ m!^\.git/!) { } elsif ($file =~ m!^misc/!) { } elsif ($file =~ m!^doc/images/.*\.(png|pdf|svg|xmi)$!) { } elsif ($file =~ m!^doc/[^/]*\.(xml|css|bib)$!) { } elsif ($file =~ m!^doxygen-html!) { } elsif ($file =~ m!README$!) { } else { print "Unknown file type $file\n"; } } # print includes to includemap.txt if (0) { print "Writing includemap:\n"; foreach my $inc (sort keys %includemap) { print "$inc => ".scalar(keys %{$includemap{$inc}})." ["; print join(",", sort keys %{$includemap{$inc}}). "]\n"; } } # check includemap for C-style headers { my @cheaders = qw(assert.h ctype.h errno.h fenv.h float.h inttypes.h limits.h locale.h math.h signal.h stdarg.h stddef.h stdlib.h stdio.h string.h time.h); foreach my $ch (@cheaders) { $ch = "<$ch>"; next if !$includemap{$ch}; print "Replace c-style header $ch in\n"; print " [".join(",", sort keys %{$includemap{$ch}}). "]\n"; } } # print authors to AUTHORS print "Writing AUTHORS:\n"; open(A, "> AUTHORS"); foreach my $a (sort keys %authormap) { my $mail = $authormap{$a}; if ($email_multimap) { $mail = join(",", sort keys %{$mail}); } else { $mail = (sort keys(%{$mail}))[0]; # pick first } $mail = $mail ? " <$mail>" : ""; print " $a$mail\n"; print A "$a$mail\n"; } close(A); stxxl-1.4.1/misc/iostat-plot.mk000644 001411 000144 00000016030 12350112610 016227 0ustar00tbusers000000 000000 ############################################################################ # misc/iostat-plot.mk # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2008-2011 Andreas Beckmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ # # to record the data, include this file from your Makefile # and use a target like # # my_results.out: # $(IOSTAT_PLOT_RECORD_DATA) -p $(@:.out=) \ # my_program arg1 ... argn > $@ # # and then run # # make my_results.out # make my_results.io.plot # make my_results.cpu.plot # empty ?=# space ?= $(empty) $(empty) comma ?= , ifndef IOSTAT_PLOT_BINDIR IOSTAT_PLOT_BINDIR := $(dir $(lastword $(MAKEFILE_LIST))) endif IOSTAT_PLOT_RECORD_DATA ?= $(IOSTAT_PLOT_BINDIR)/record-load-iostat IOSTAT_PLOT_CONCAT_LINES ?= $(IOSTAT_PLOT_BINDIR)/concat-lines IOSTAT_PLOT_FLOATING_AVERAGE ?= $(IOSTAT_PLOT_BINDIR)/floating-average IOSTAT_PLOT_LINE_IDENTIFIER ?= ^sd IOSTAT_PLOT_LINE_IGNORE_PATTERN ?= IOSTAT_PLOT_CPU_LINE_IDENTIFIER ?= ^$(space)$(space)$(space) IOSTAT_PLOT_DISKS ?= 8 IOSTAT_PLOT_CPUS ?= 8 IOSTAT_PLOT_AVERAGE ?= 1 IOSTAT_PLOT_AVERAGE.io ?= $(IOSTAT_PLOT_AVERAGE) IOSTAT_PLOT_AVERAGE.cpu ?= $(IOSTAT_PLOT_AVERAGE) IOSTAT_PLOT_LOAD_REDUCE ?= 0 IOSTAT_PLOT_DISK_LIST ?= sda sdb sdc sdd sde sdf sdg sdh sdi sdj IOSTAT_PLOT_IO_WITH_UTILIZATION ?= no IOSTAT_PLOT_Y_LABEL.io ?= Bandwidth [MiB/s] IOSTAT_PLOT_Y_LABEL.cpu ?= CPU Usage [%] GNUPLOT_PS_COLOR ?= color solid GNUPLOT_PS_STRING_ESCAPE ?= $(subst _,\\_,$(subst @,\\@,$(subst ~,\\~,$1))) GNUPLOTFILEINFO ?= set label "$(call GNUPLOT_PS_STRING_ESCAPE,$(HOST):$(patsubst $(HOME)/%,~/%,$(CURDIR))/)" at character 0,-1 font ",6" ECHO ?= echo # $1 = iostat file # (12) Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util # with -p: Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util define get-deviceline __deviceline := $(shell grep ^Device $1 | head -n 1) endef offset_read = $(if $(filter rMB/s,$(word 6,$(__deviceline))),6,8) offset_write = $(if $(filter wMB/s,$(word 7,$(__deviceline))),7,9) offset_util = $(if $(filter %util,$(word 12,$(__deviceline))),12,14) # $1 = first, $2 = increment, $3 = last define gnuplot-column-sequence-sum $(subst $(space),+,$(patsubst %,$$%,$(shell seq $1 $2 $3))) endef # $1 = io | cpu, $2 = numdisks (io) | numcpus (cpu) define template-iostat-gnuplot $(RM) $@ $(ECHO) 'set title "$(subst _, ,$*) (avg=$(IOSTAT_PLOT_AVERAGE.$(strip $1)))"' >> $@ $(ECHO) 'set xlabel "Time [s]"' >> $@ $(ECHO) 'set ylabel "$(IOSTAT_PLOT_Y_LABEL.$(strip $1))"' >> $@ $(if $(and $(filter io,$1),$(filter yes,$(IOSTAT_PLOT_IO_WITH_UTILIZATION))), $(ECHO) 'set y2label "Utilization [%]"' >> $@ $(ECHO) 'set ytics nomirror' >> $@ $(ECHO) 'set y2tics' >> $@ ,$(if $(wildcard $*.waitlog), $(ECHO) 'set y2label "Wait Time [s]"' >> $@ $(ECHO) 'set ytics nomirror' >> $@ $(ECHO) 'set y2tics' >> $@ )) # $(ECHO) 'set style data linespoints' >> $@ $(ECHO) 'set style data lines' >> $@ $(ECHO) 'set macros' >> $@ $(ECHO) 'set pointsize 0.4' >> $@ # $(ECHO) 'set samples 1000' >> $@ $(ECHO) 'set key top left' >> $@ $(ECHO) 'set yrange [0:]' >> $@ $(ECHO) '' >> $@ $(if $(filter io,$1), $(ECHO) 'read = "$(call gnuplot-column-sequence-sum,2,4,$(shell expr $2 '*' 4))"' >> $@ $(ECHO) 'write = "$(call gnuplot-column-sequence-sum,3,4,$(shell expr $2 '*' 4))"' >> $@ $(ECHO) 'utilize = "($(call gnuplot-column-sequence-sum,4,4,$(shell expr $2 '*' 4))) / $2"' >> $@ $(ECHO) '' >> $@ $(ECHO) 'plot \' >> $@ $(ECHO) ' "$<" using 0:(@read + @write) title "Read + Write" ls 3$(comma) \' >> $@ $(ECHO) ' "$<" using 0:(@read) title "Read" ls 2$(comma) \' >> $@ $(ECHO) ' "$<" using 0:(@write) title "Write" ls 1$(comma) \' >> $@ $(if $(filter yes,$(IOSTAT_PLOT_IO_WITH_UTILIZATION)), $(ECHO) ' "$<" using 0:(@utilize) title "Utilization" ls 5 axes x1y2$(comma) \' >> $@ ,$(if $(wildcard $*.waitlog), $(ECHO) ' "$*.waitlog" using 1:4 title "Wait Read" ls 5 axes x1y2$(comma) \' >> $@ $(ECHO) ' "$*.waitlog" using 1:5 title "Wait Write" ls 4 axes x1y2$(comma) \' >> $@ )) $(ECHO) ' "not.existing.dummy" using 8:15 notitle' >> $@ ) $(if $(filter cpu,$1), $(ECHO) 'plot \' >> $@ $(ECHO) ' "$(word 2,$^)" using 0:(100*($$1-$(IOSTAT_PLOT_LOAD_REDUCE))/$(strip $2)) title "Load (100% = $2)" ls 5$(comma) \' >> $@ $(ECHO) ' "$<" using 0:($$1+$$2+$$3+$$4) title "Total" ls 4$(comma) \' >> $@ $(ECHO) ' "$<" using 0:2 title "Nice" ls 6$(comma) \' >> $@ $(ECHO) ' "$<" using 0:4 title "Wait" ls 2$(comma) \' >> $@ $(ECHO) ' "$<" using 0:1 title "User" ls 1$(comma) \' >> $@ $(ECHO) ' "$<" using 0:3 title "System" ls 3$(comma) \' >> $@ $(ECHO) ' "$(word 2,$^)" using 0:(100*($$1-$(IOSTAT_PLOT_LOAD_REDUCE))/$(strip $2)) notitle ls 5$(comma) \' >> $@ $(ECHO) ' "not.existing.dummy" using 8:15 notitle' >> $@ ) $(ECHO) '' >> $@ $(ECHO) 'pause -1' >> $@ $(ECHO) '' >> $@ $(ECHO) 'set terminal postscript enhanced $(GNUPLOT_PS_COLOR) 10' >> $@ $(ECHO) 'set output "$*.$(strip $1).eps"' >> $@ $(ECHO) '$(GNUPLOTFILEINFO)' >> $@ $(ECHO) 'replot' >> $@ $(ECHO) '' >> $@ endef define iostat-to-dat $(eval $(call get-deviceline,$<)) $(pipefail) \ cat $< | \ $(if $(IOSTAT_PLOT_LINE_IGNORE_PATTERN),grep -v '$(IOSTAT_PLOT_LINE_IGNORE_PATTERN)' |) \ awk '{ print $$1 " " $$$(offset_read) " " $$$(offset_write) " " $$$(offset_util) }' | \ $(IOSTAT_PLOT_CONCAT_LINES) '$(IOSTAT_PLOT_LINE_IDENTIFIER)' | \ grep '$(IOSTAT_PLOT_LINE_IDENTIFIER)' | \ tail -n +2 | \ $(IOSTAT_PLOT_FLOATING_AVERAGE) $(IOSTAT_PLOT_AVERAGE.io) > $@ endef %.io-$(IOSTAT_PLOT_AVERAGE.io).dat: %.iostat $(MAKEFILE_LIST) $(iostat-to-dat) define per-disk-plot-template %.$(disk).io-$$(IOSTAT_PLOT_AVERAGE.io).dat: IOSTAT_PLOT_LINE_IDENTIFIER=^$(disk) %.$(disk).io-$$(IOSTAT_PLOT_AVERAGE.io).plot: IOSTAT_PLOT_DISKS=1 %.$(disk).io-$$(IOSTAT_PLOT_AVERAGE.io).plot: IOSTAT_PLOT_IO_WITH_UTILIZATION=yes %.$(disk).io-$$(IOSTAT_PLOT_AVERAGE.io).dat: %.iostat $$(MAKEFILE_LIST) $$(iostat-to-dat) endef $(foreach disk, $(IOSTAT_PLOT_DISK_LIST),$(eval $(per-disk-plot-template))) %.cpu-$(IOSTAT_PLOT_AVERAGE.cpu).dat: %.iostat $(MAKEFILE_LIST) $(pipefail) \ grep "$(IOSTAT_PLOT_CPU_LINE_IDENTIFIER)" $< | \ tail -n +2 | \ $(IOSTAT_PLOT_FLOATING_AVERAGE) $(IOSTAT_PLOT_AVERAGE.cpu) > $@ %.io-$(IOSTAT_PLOT_AVERAGE.io).plot: %.io-$(IOSTAT_PLOT_AVERAGE.io).dat $(MAKEFILE_LIST) $(call template-iostat-gnuplot,io,$(IOSTAT_PLOT_DISKS)) %.io.plot: %.io-$(IOSTAT_PLOT_AVERAGE.io).plot @$(ECHO) Your plot file is: $< %.cpu-$(IOSTAT_PLOT_AVERAGE.cpu).plot: %.cpu-$(IOSTAT_PLOT_AVERAGE.cpu).dat %.loadavg $(MAKEFILE_LIST) $(call template-iostat-gnuplot,cpu,$(IOSTAT_PLOT_CPUS)) %.cpu.plot: %.cpu-$(IOSTAT_PLOT_AVERAGE.cpu).plot @$(ECHO) Your plot file is: $< %.io.xplot: %.io-$(IOSTAT_PLOT_AVERAGE.io).plot gnuplot $< %.cpu.xplot: %.cpu-$(IOSTAT_PLOT_AVERAGE.cpu).plot gnuplot $< .SECONDARY: .DELETE_ON_ERROR: stxxl-1.4.1/misc/cmake/stxxl-version.cmake.in000644 001411 000144 00000000600 12350112610 020747 0ustar00tbusers000000 000000 set(PACKAGE_VERSION "@STXXL_VERSION_STRING@") # Check whether the requested PACKAGE_FIND_VERSION is compatible if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_COMPATIBLE FALSE) else() set(PACKAGE_VERSION_COMPATIBLE TRUE) if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") set(PACKAGE_VERSION_EXACT TRUE) endif() endif() stxxl-1.4.1/misc/cmake/TestLargeFiles.c.cmakein000644 001411 000144 00000001213 12350112610 021123 0ustar00tbusers000000 000000 #cmakedefine _LARGEFILE_SOURCE #cmakedefine _LARGEFILE64_SOURCE #cmakedefine _LARGE_FILES #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} #include #include #include int main(int argc, char **argv) { /* Cause a compile-time error if off_t is smaller than 64 bits, * and make sure we have ftello / fseeko. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ]; FILE *fp = fopen(argv[0],"r"); off_t offset = ftello( fp ); fseeko( fp, offset, SEEK_CUR ); fclose(fp); return 0; } stxxl-1.4.1/misc/cmake/TestWindowsFSeek.c000644 001411 000144 00000000161 12350112610 020051 0ustar00tbusers000000 000000 #include int main() { __int64 off=0; _fseeki64(NULL, off, SEEK_SET); return 0; } stxxl-1.4.1/misc/cmake/stxxl.pc000644 001411 000144 00000000630 12350112610 016204 0ustar00tbusers000000 000000 prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} includedir=${prefix}/@INSTALL_INCLUDE_DIR@ libdir=${exec_prefix}/@INSTALL_LIB_DIR@ Name: stxxl Description: An implementation of the C++ STL interfaces for external (out-of-core) memory. URL: http://stxxl.sourceforge.net Version: @STXXL_VERSION_STRING@ Cflags: -I${includedir} @STXXL_CXX_FLAGS@ Libs: -L${libdir} -l@STXXL_LIBNAME@ @STXXL_EXTRA_LIBRARIES@ stxxl-1.4.1/misc/cmake/stxxl-config.cmake.in000644 001411 000144 00000001352 12350112610 020534 0ustar00tbusers000000 000000 # CMake config file for STXXL # # It defines the following variables # STXXL_CXX_FLAGS - C++ flags for STXXL # STXXL_INCLUDE_DIRS - include directories for STXXL # STXXL_LIBRARIES - libraries to link against # compute paths from current cmake file path get_filename_component(STXXL_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) set(STXXL_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@") # additional compiler flags. these often include -fopenmp! set(STXXL_CXX_FLAGS "@STXXL_CXX_FLAGS@") # load our library dependencies (contains definitions for IMPORTED targets) include("${STXXL_CMAKE_DIR}/stxxl-targets.cmake") # these are IMPORTED targets created by stxxl-targets.cmake, link these with # your program. set(STXXL_LIBRARIES "@STXXL_LIBRARIES@") stxxl-1.4.1/misc/cmake/TestFileOffsetBits.c000644 001411 000144 00000000464 12350112610 020357 0ustar00tbusers000000 000000 #include int main(int argc, char **argv) { /* Cause a compile-time error if off_t is smaller than 64 bits */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ]; return 0; } stxxl-1.4.1/misc/cmake/TestLargeFiles.cmake000644 001411 000144 00000010702 12350112610 020356 0ustar00tbusers000000 000000 # - Define macro to check large file support # # TEST_LARGE_FILES(VARIABLE) # # VARIABLE will be set to true if off_t is 64 bits, and fseeko/ftello present. # This macro will also set defines necessary enable large file support, for instance # _LARGE_FILES # _LARGEFILE_SOURCE # _FILE_OFFSET_BITS 64 # HAVE_FSEEKO # # However, it is YOUR job to make sure these defines are set in a cmakedefine so they # end up in a config.h file that is included in your source if necessary! MACRO(TEST_LARGE_FILES VARIABLE) IF(NOT DEFINED ${VARIABLE}) # On most platforms it is probably overkill to first test the flags for 64-bit off_t, # and then separately fseeko. However, in the future we might have 128-bit filesystems # (ZFS), so it might be dangerous to indiscriminately set e.g. _FILE_OFFSET_BITS=64. MESSAGE(STATUS "Checking for 64-bit off_t") # First check without any special flags TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}" "${CMAKE_MODULE_PATH}/TestFileOffsetBits.c") if(FILE64_OK) MESSAGE(STATUS "Checking for 64-bit off_t - present") endif(FILE64_OK) if(NOT FILE64_OK) # Test with _FILE_OFFSET_BITS=64 TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}" "${CMAKE_MODULE_PATH}/TestFileOffsetBits.c" COMPILE_DEFINITIONS "-D_FILE_OFFSET_BITS=64" ) if(FILE64_OK) MESSAGE(STATUS "Checking for 64-bit off_t - present with _FILE_OFFSET_BITS=64") set(_FILE_OFFSET_BITS 64) endif(FILE64_OK) endif(NOT FILE64_OK) if(NOT FILE64_OK) # Test with _LARGE_FILES TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}" "${CMAKE_MODULE_PATH}/TestFileOffsetBits.c" COMPILE_DEFINITIONS "-D_LARGE_FILES" ) if(FILE64_OK) MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGE_FILES") set(_LARGE_FILES 1) endif(FILE64_OK) endif(NOT FILE64_OK) if(NOT FILE64_OK) # Test with _LARGEFILE_SOURCE TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}" "${CMAKE_MODULE_PATH}/TestFileOffsetBits.c" COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" ) if(FILE64_OK) MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGEFILE_SOURCE") set(_LARGEFILE_SOURCE 1) endif(FILE64_OK) endif(NOT FILE64_OK) if(NOT FILE64_OK) # now check for Windows stuff TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}" "${CMAKE_MODULE_PATH}/TestWindowsFSeek.c") if(FILE64_OK) MESSAGE(STATUS "Checking for 64-bit off_t - present with _fseeki64") set(HAVE__FSEEKI64 1) endif(FILE64_OK) endif(NOT FILE64_OK) if(NOT FILE64_OK) MESSAGE(STATUS "Checking for 64-bit off_t - not present") else(NOT FILE64_OK) # Set the flags we might have determined to be required above configure_file("${CMAKE_MODULE_PATH}/TestLargeFiles.c.cmakein" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c") MESSAGE(STATUS "Checking for fseeko/ftello") # Test if ftello/fseeko are available TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c") if(FSEEKO_COMPILE_OK) MESSAGE(STATUS "Checking for fseeko/ftello - present") endif(FSEEKO_COMPILE_OK) if(NOT FSEEKO_COMPILE_OK) # glibc 2.2 neds _LARGEFILE_SOURCE for fseeko (but not 64-bit off_t...) TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c" COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" ) if(FSEEKO_COMPILE_OK) MESSAGE(STATUS "Checking for fseeko/ftello - present with _LARGEFILE_SOURCE") set(_LARGEFILE_SOURCE 1) endif(FSEEKO_COMPILE_OK) endif(NOT FSEEKO_COMPILE_OK) endif(NOT FILE64_OK) if(FSEEKO_COMPILE_OK) SET(${VARIABLE} 1 CACHE INTERNAL "Result of test for large file support" FORCE) set(HAVE_FSEEKO 1) else(FSEEKO_COMPILE_OK) if (HAVE__FSEEKI64) SET(${VARIABLE} 1 CACHE INTERNAL "Result of test for large file support" FORCE) SET(HAVE__FSEEKI64 1 CACHE INTERNAL "Windows 64-bit fseek" FORCE) else (HAVE__FSEEKI64) MESSAGE(STATUS "Checking for fseeko/ftello - not found") SET(${VARIABLE} 0 CACHE INTERNAL "Result of test for large file support" FORCE) endif (HAVE__FSEEKI64) endif(FSEEKO_COMPILE_OK) ENDIF(NOT DEFINED ${VARIABLE}) ENDMACRO(TEST_LARGE_FILES VARIABLE) stxxl-1.4.1/misc/diskbench-avgdat.sh000755 001411 000144 00000001231 12350112610 017151 0ustar00tbusers000000 000000 #!/bin/sh get_write() { x=`grep "# Average" $1 2>/dev/null | awk '{ print $8 }'` test -n "$x" || x=U test "$x" = "nan" && x=U test "$x" = "0.000" && x=U echo "$x" } get_read() { x=`grep "# Average" $1 2>/dev/null | awk '{ print $15 }'` test -n "$x" || x=U test "$x" = "nan" && x=U test "$x" = "0.000" && x=U echo "$x" } echo "# bs crx wr rd wrx rdx" for mb in "$@" do size=`echo $mb | sed -e 's/^0003/2.5/;s/^0012/12.5/;s/^0*//;s/\([kKMGT]\)B/\1iB/;s/MiB$//'` crx=`get_write $mb/*.crx.log` wr=`get_write $mb/*.wr.log` rd=`get_read $mb/*.wr.log` wrx=`get_write $mb/*.wrx.log` rdx=`get_read $mb/*.rdx.log` echo "$size $crx $wr $rd $wrx $rdx" done stxxl-1.4.1/misc/floating-average000755 001411 000144 00000002576 12350112610 016572 0ustar00tbusers000000 000000 #!/usr/bin/perl -w ############################################################################ # misc/floating-average # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2007 Johannes Singler # Copyright (C) 2010 Andreas Beckmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ # Calculate the floating average over $1 values in all columns # Reading from stdin, writing to stdout # Example: floating-average 3 averaged use strict; my $span = $ARGV[0]; my @sums; my @history; my $count_sum = 1; my $line_number = 0; my $line; while($line = ) { my @parts = split /\s+/, $line; my $c = 0; my $part; foreach $part(@parts) { if($part =~ /^[+-]?\d+\.?\d*([eE][+-]?\d+)?$/) { #is number if($count_sum == $span) { $sums[$c] -= $history[$line_number % $span][$c] || 0; } $history[$line_number % $span][$c] = $part; $sums[$c] += $history[$line_number % $span][$c]; printf("%14.6f ", $sums[$c] / $count_sum); ++$c; } else { print(" $part "); } } print("\n"); if($count_sum < $span) { ++$count_sum; } ++$line_number; } stxxl-1.4.1/misc/valgrind.supp000644 001411 000144 00000000222 12350112610 016132 0ustar00tbusers000000 000000 { libgomp/thread_local_storage Memcheck:Leak fun:calloc fun:allocate_dtv fun:_dl_allocate_tls fun:pthread_create@@GLIBC_2.2.5 } stxxl-1.4.1/misc/uncrustify.cfg000644 001411 000144 00000212562 12411366426 016341 0ustar00tbusers000000 000000 # Uncrustify 0.60 # # General options # # The type of line endings newlines = auto # auto/lf/crlf/cr # The original size of tabs in the input input_tab_size = 8 # number # The size of tabs in the output (only used if align_with_tabs=true) output_tab_size = 8 # number # The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn) string_escape_char = 92 # number # Alternate string escape char for Pawn. Only works right before the quote char. string_escape_char2 = 0 # number # Allow interpreting '>=' and '>>=' as part of a template in 'void f(list>=val);'. # If true (default), 'assert(x<0 && y>=3)' will be broken. # Improvements to template detection may make this option obsolete. tok_split_gte = true # false/true # Control what to do with the UTF-8 BOM (recommend 'remove') utf8_bom = ignore # ignore/add/remove/force # If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 utf8_byte = false # false/true # Force the output encoding to UTF-8 utf8_force = false # false/true # # Indenting # # The number of columns to indent per level. # Usually 2, 3, 4, or 8. indent_columns = 4 # number # The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. # For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level indent_continue = 0 # number # How to use tabs when indenting code # 0=spaces only # 1=indent with tabs to brace level, align with spaces # 2=indent and align with tabs, using spaces when not on a tabstop indent_with_tabs = 0 # number # Comments that are not a brace level are indented with tabs on a tabstop. # Requires indent_with_tabs=2. If false, will use spaces. indent_cmt_with_tabs = false # false/true # Whether to indent strings broken by '\' so that they line up indent_align_string = false # false/true # The number of spaces to indent multi-line XML strings. # Requires indent_align_string=True indent_xml_string = 0 # number # Spaces to indent '{' from level indent_brace = 0 # number # Whether braces are indented to the body level indent_braces = false # false/true # Disabled indenting function braces if indent_braces is true indent_braces_no_func = false # false/true # Disabled indenting class braces if indent_braces is true indent_braces_no_class = false # false/true # Disabled indenting struct braces if indent_braces is true indent_braces_no_struct = false # false/true # Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. indent_brace_parent = false # false/true # Whether the 'namespace' body is indented indent_namespace = false # false/true # The number of spaces to indent a namespace block indent_namespace_level = 0 # number # If the body of the namespace is longer than this number, it won't be indented. # Requires indent_namespace=true. Default=0 (no limit) indent_namespace_limit = 0 # number # Whether the 'extern "C"' body is indented indent_extern = false # false/true # Whether the 'class' body is indented indent_class = true # false/true # Whether to indent the stuff after a leading class colon indent_class_colon = true # false/true # Virtual indent from the ':' for member initializers. Default is 2 indent_ctor_init_leading = 2 # number # Additional indenting for constructor initializer list indent_ctor_init = 0 # number # False=treat 'else\nif' as 'else if' for indenting purposes # True=indent the 'if' one level indent_else_if = false # false/true # Amount to indent variable declarations after a open brace. neg=relative, pos=absolute indent_var_def_blk = 0 # number # Indent continued variable declarations instead of aligning. indent_var_def_cont = true # false/true # True: force indentation of function definition to start in column 1 # False: use the default behavior indent_func_def_force_col1 = false # false/true # True: indent continued function call parameters one indent level # False: align parameters under the open paren indent_func_call_param = false # false/true # Same as indent_func_call_param, but for function defs indent_func_def_param = false # false/true # Same as indent_func_call_param, but for function protos indent_func_proto_param = false # false/true # Same as indent_func_call_param, but for class declarations indent_func_class_param = false # false/true # Same as indent_func_call_param, but for class variable constructors indent_func_ctor_var_param = false # false/true # Same as indent_func_call_param, but for templates indent_template_param = false # false/true # Double the indent for indent_func_xxx_param options indent_func_param_double = false # false/true # Indentation column for standalone 'const' function decl/proto qualifier indent_func_const = 0 # number # Indentation column for standalone 'throw' function decl/proto qualifier indent_func_throw = 0 # number # The number of spaces to indent a continued '->' or '.' # Usually set to 0, 1, or indent_columns. indent_member = 0 # number # Spaces to indent single line ('//') comments on lines before code indent_sing_line_comments = 0 # number # If set, will indent trailing single line ('//') comments relative # to the code instead of trying to keep the same absolute column indent_relative_single_line_comments = false # false/true # Spaces to indent 'case' from 'switch' # Usually 0 or indent_columns. indent_switch_case = 0 # number # Spaces to shift the 'case' line, without affecting any other lines # Usually 0. indent_case_shift = 0 # number # Spaces to indent '{' from 'case'. # By default, the brace will appear under the 'c' in case. # Usually set to 0 or indent_columns. indent_case_brace = 0 # number # Whether to indent comments found in first column indent_col1_comment = false # false/true # How to indent goto labels # >0 : absolute column where 1 is the leftmost column # <=0 : subtract from brace indent indent_label = 1 # number # Same as indent_label, but for access specifiers that are followed by a colon indent_access_spec = -4 # number # Indent the code after an access specifier by one level. # If set, this option forces 'indent_access_spec=0' indent_access_spec_body = false # false/true # If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) indent_paren_nl = false # false/true # Controls the indent of a close paren after a newline. # 0: Indent to body level # 1: Align under the open paren # 2: Indent to the brace level indent_paren_close = 0 # number # Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren indent_comma_paren = false # false/true # Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren indent_bool_paren = false # false/true # If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones indent_first_bool_expr = false # false/true # If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) indent_square_nl = false # false/true # Don't change the relative indent of ESQL/C 'EXEC SQL' bodies indent_preserve_sql = false # false/true # Align continued statements at the '='. Default=True # If FALSE or the '=' is followed by a newline, the next line is indent one tab. indent_align_assign = true # false/true # Indent OC blocks at brace level instead of usual rules. indent_oc_block = false # false/true # Indent OC blocks in a message relative to the parameter name. # 0=use indent_oc_block rules, 1+=spaces to indent indent_oc_block_msg = 0 # number # Minimum indent for subsequent parameters indent_oc_msg_colon = 0 # number # # Spacing options # # Add or remove space around arithmetic operator '+', '-', '/', '*', etc sp_arith = force # ignore/add/remove/force # Add or remove space around assignment operator '=', '+=', etc sp_assign = force # ignore/add/remove/force # Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign sp_cpp_lambda_assign = ignore # ignore/add/remove/force # Add or remove space after the capture specification in C++11 lambda. sp_cpp_lambda_paren = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=' in a prototype sp_assign_default = force # ignore/add/remove/force # Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. sp_before_assign = force # ignore/add/remove/force # Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. sp_after_assign = force # ignore/add/remove/force # Add or remove space around assignment '=' in enum sp_enum_assign = force # ignore/add/remove/force # Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. sp_enum_before_assign = force # ignore/add/remove/force # Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. sp_enum_after_assign = force # ignore/add/remove/force # Add or remove space around preprocessor '##' concatenation operator. Default=Add sp_pp_concat = force # ignore/add/remove/force # Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. sp_pp_stringify = ignore # ignore/add/remove/force # Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. sp_before_pp_stringify = ignore # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||' sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc sp_compare = force # ignore/add/remove/force # Add or remove space inside '(' and ')' sp_inside_paren = remove # ignore/add/remove/force # Add or remove space between nested parens sp_paren_paren = remove # ignore/add/remove/force # Whether to balance spaces inside nested parens sp_balance_nested_parens = false # false/true # Add or remove space between ')' and '{' sp_paren_brace = force # ignore/add/remove/force # Add or remove space before pointer star '*' sp_before_ptr_star = remove # ignore/add/remove/force # Add or remove space before pointer star '*' that isn't followed by a variable name # If set to 'ignore', sp_before_ptr_star is used instead. sp_before_unnamed_ptr_star = remove # ignore/add/remove/force # Add or remove space between pointer stars '*' sp_between_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. sp_after_ptr_star = force # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by a func proto/def. sp_after_ptr_star_func = force # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by an open paren (function types). sp_ptr_star_paren = ignore # ignore/add/remove/force # Add or remove space before a pointer star '*', if followed by a func proto/def. sp_before_ptr_star_func = force # ignore/add/remove/force # Add or remove space before a reference sign '&' sp_before_byref = remove # ignore/add/remove/force # Add or remove space before a reference sign '&' that isn't followed by a variable name # If set to 'ignore', sp_before_byref is used instead. sp_before_unnamed_byref = remove # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. sp_after_byref = force # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by a func proto/def. sp_after_byref_func = force # ignore/add/remove/force # Add or remove space before a reference sign '&', if followed by a func proto/def. sp_before_byref_func = force # ignore/add/remove/force # Add or remove space between type and word. Default=Force sp_after_type = force # ignore/add/remove/force # Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. sp_before_template_paren = ignore # ignore/add/remove/force # Add or remove space in 'template <' vs 'template<'. # If set to ignore, sp_before_angle is used. sp_template_angle = force # ignore/add/remove/force # Add or remove space before '<>' sp_before_angle = remove # ignore/add/remove/force # Add or remove space inside '<' and '>' sp_inside_angle = remove # ignore/add/remove/force # Add or remove space after '<>' sp_after_angle = remove # ignore/add/remove/force # Add or remove space between '<>' and '(' as found in 'new List();' sp_angle_paren = remove # ignore/add/remove/force # Add or remove space between '<>' and a word as in 'List m;' sp_angle_word = force # ignore/add/remove/force # Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add sp_angle_shift = force # ignore/add/remove/force # Permit removal of the space between '>>' in 'foo >' (C++11 only). Default=False # sp_angle_shift cannot remove the space without this option. sp_permit_cpp11_shift = false # false/true # Add or remove space before '(' of 'if', 'for', 'switch', and 'while' sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside if-condition '(' and ')' sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space before if-condition ')'. Overrides sp_inside_sparen. sp_inside_sparen_close = ignore # ignore/add/remove/force # Add or remove space before if-condition '('. Overrides sp_inside_sparen. sp_inside_sparen_open = ignore # ignore/add/remove/force # Add or remove space after ')' of 'if', 'for', 'switch', and 'while' sp_after_sparen = force # ignore/add/remove/force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' sp_sparen_brace = force # ignore/add/remove/force # Add or remove space between 'invariant' and '(' in the D language. sp_invariant_paren = ignore # ignore/add/remove/force # Add or remove space after the ')' in 'invariant (C) c' in the D language. sp_after_invariant_paren = ignore # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while' sp_special_semi = force # ignore/add/remove/force # Add or remove space before ';'. Default=Remove sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements sp_before_semi_for = remove # ignore/add/remove/force # Add or remove space before a semicolon of an empty part of a for statement. sp_before_semi_for_empty = force # ignore/add/remove/force # Add or remove space after ';', except when followed by a comment. Default=Add sp_after_semi = add # ignore/add/remove/force # Add or remove space after ';' in non-empty 'for' statements. Default=Force sp_after_semi_for = force # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; ). sp_after_semi_for_empty = force # ignore/add/remove/force # Add or remove space before '[' (except '[]') sp_before_square = remove # ignore/add/remove/force # Add or remove space before '[]' sp_before_squares = remove # ignore/add/remove/force # Add or remove space inside a non-empty '[' and ']' sp_inside_square = remove # ignore/add/remove/force # Add or remove space after ',' sp_after_comma = force # ignore/add/remove/force # Add or remove space before ',' sp_before_comma = remove # ignore/add/remove/force # Add or remove space between an open paren and comma: '(,' vs '( ,' sp_paren_comma = force # ignore/add/remove/force # Add or remove space before the variadic '...' when preceded by a non-punctuator sp_before_ellipsis = ignore # ignore/add/remove/force # Add or remove space after class ':' sp_after_class_colon = force # ignore/add/remove/force # Add or remove space before class ':' sp_before_class_colon = force # ignore/add/remove/force # Add or remove space before case ':'. Default=Remove sp_before_case_colon = remove # ignore/add/remove/force # Add or remove space between 'operator' and operator sign sp_after_operator = force # ignore/add/remove/force # Add or remove space between the operator symbol and the open paren, as in 'operator ++(' sp_after_operator_sym = force # ignore/add/remove/force # Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' sp_after_cast = remove # ignore/add/remove/force # Add or remove spaces inside cast parens sp_inside_paren_cast = remove # ignore/add/remove/force # Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)' sp_cpp_cast_paren = remove # ignore/add/remove/force # Add or remove space between 'sizeof' and '(' sp_sizeof_paren = remove # ignore/add/remove/force # Add or remove space after the tag keyword (Pawn) sp_after_tag = ignore # ignore/add/remove/force # Add or remove space inside enum '{' and '}' sp_inside_braces_enum = force # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}' sp_inside_braces_struct = force # ignore/add/remove/force # Add or remove space inside '{' and '}' sp_inside_braces = force # ignore/add/remove/force # Add or remove space inside '{}' sp_inside_braces_empty = force # ignore/add/remove/force # Add or remove space between return type and function name # A minimum of 1 is forced except for pointer return types. sp_type_func = force # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration sp_func_proto_paren = remove # ignore/add/remove/force # Add or remove space between function name and '(' on function definition sp_func_def_paren = remove # ignore/add/remove/force # Add or remove space inside empty function '()' sp_inside_fparens = remove # ignore/add/remove/force # Add or remove space inside function '(' and ')' sp_inside_fparen = remove # ignore/add/remove/force # Add or remove space inside the first parens in the function type: 'void (*x)(...)' sp_inside_tparen = ignore # ignore/add/remove/force # Add or remove between the parens in the function type: 'void (*x)(...)' sp_after_tparen_close = ignore # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = remove # ignore/add/remove/force # Add or remove space between ')' and '{' of function sp_fparen_brace = force # ignore/add/remove/force # Add or remove space between function name and '(' on function calls sp_func_call_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function calls without parameters. # If set to 'ignore' (the default), sp_func_call_paren is used. sp_func_call_paren_empty = ignore # ignore/add/remove/force # Add or remove space between the user function name and '(' on function calls # You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. sp_func_call_user_paren = remove # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open paren sp_func_class_paren = remove # ignore/add/remove/force # Add or remove space between 'return' and '(' sp_return_paren = force # ignore/add/remove/force # Add or remove space between '__attribute__' and '(' sp_attribute_paren = force # ignore/add/remove/force # Add or remove space between 'defined' and '(' in '#if defined (FOO)' sp_defined_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and '(' in 'throw (something)' sp_throw_paren = force # ignore/add/remove/force # Add or remove space between 'throw' and anything other than '(' as in '@throw [...];' sp_after_throw = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '(' in 'catch (something) { }' # If set to ignore, sp_before_sparen is used. sp_catch_paren = force # ignore/add/remove/force # Add or remove space between 'version' and '(' in 'version (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_version_paren = ignore # ignore/add/remove/force # Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_scope_paren = ignore # ignore/add/remove/force # Add or remove space between macro and value sp_macro = ignore # ignore/add/remove/force # Add or remove space between macro function ')' and value sp_macro_func = ignore # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line sp_else_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line sp_brace_else = force # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line sp_brace_typedef = force # ignore/add/remove/force # Add or remove space between 'catch' and '{' if on the same line sp_catch_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line sp_brace_catch = force # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line sp_finally_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line sp_brace_finally = force # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line sp_try_brace = force # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line sp_getset_brace = ignore # ignore/add/remove/force # Add or remove space before the '::' operator sp_before_dc = remove # ignore/add/remove/force # Add or remove space after the '::' operator sp_after_dc = remove # ignore/add/remove/force # Add or remove around the D named array initializer ':' operator sp_d_array_colon = ignore # ignore/add/remove/force # Add or remove space after the '!' (not) operator. Default=Remove sp_not = ignore # ignore/add/remove/force # Add or remove space after the '~' (invert) operator. Default=Remove sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) operator. Default=Remove # This does not affect the spacing after a '&' that is part of a type. sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators. Default=Remove sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) operator. Default=Remove # This does not affect the spacing after a '*' that is part of a type. sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove sp_sign = remove # ignore/add/remove/force # Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove sp_incdec = remove # ignore/add/remove/force # Add or remove space before a backslash-newline at the end of a line. Default=Add sp_before_nl_cont = force # ignore/add/remove/force # Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' sp_after_oc_scope = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '-(int) f:(int) x;' vs '-(int) f: (int) x;' sp_after_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '-(int) f: (int) x;' vs '-(int) f : (int) x;' sp_before_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};' sp_after_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};' sp_before_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '[object setValue:1];' vs '[object setValue: 1];' sp_after_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '[object setValue:1];' vs '[object setValue :1];' sp_before_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the (type) in message specs # '-(int)f: (int) x;' vs '-(int)f: (int)x;' sp_after_oc_type = ignore # ignore/add/remove/force # Add or remove space after the first (type) in message specs # '-(int) f:(int)x;' vs '-(int)f:(int)x;' sp_after_oc_return_type = ignore # ignore/add/remove/force # Add or remove space between '@selector' and '(' # '@selector(msgName)' vs '@selector (msgName)' # Also applies to @protocol() constructs sp_after_oc_at_sel = ignore # ignore/add/remove/force # Add or remove space between '@selector(x)' and the following word # '@selector(foo) a:' vs '@selector(foo)a:' sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space inside '@selector' parens # '@selector(foo)' vs '@selector( foo )' # Also applies to @protocol() constructs sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space before a block pointer caret # '^int (int arg){...}' vs. ' ^int (int arg){...}' sp_before_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space after a block pointer caret # '^int (int arg){...}' vs. '^ int (int arg){...}' sp_after_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space between the receiver and selector in a message. # '[receiver selector ...]' sp_after_oc_msg_receiver = ignore # ignore/add/remove/force # Add or remove space after @property. sp_after_oc_property = ignore # ignore/add/remove/force # Add or remove space around the ':' in 'b ? t : f' sp_cond_colon = force # ignore/add/remove/force # Add or remove space around the '?' in 'b ? t : f' sp_cond_question = force # ignore/add/remove/force # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. sp_case_label = force # ignore/add/remove/force # Control the space around the D '..' operator. sp_range = ignore # ignore/add/remove/force # Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java) sp_after_for_colon = ignore # ignore/add/remove/force # Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java) sp_before_for_colon = ignore # ignore/add/remove/force # Control the spacing in 'extern (C)' (D) sp_extern_paren = ignore # ignore/add/remove/force # Control the space after the opening of a C++ comment '// A' vs '//A' sp_cmt_cpp_start = ignore # ignore/add/remove/force # Controls the spaces between #else or #endif and a trailing comment sp_endif_cmt = ignore # ignore/add/remove/force # Controls the spaces after 'new', 'delete', and 'delete[]' sp_after_new = add # ignore/add/remove/force # Controls the spaces before a trailing or embedded comment sp_before_tr_emb_cmt = ignore # ignore/add/remove/force # Number of spaces before a trailing or embedded comment sp_num_before_tr_emb_cmt = 0 # number # Control space between a Java annotation and the open paren. sp_annotation_paren = ignore # ignore/add/remove/force # # Code alignment (not left column spaces/tabs) # # Whether to keep non-indenting tabs align_keep_tabs = false # false/true # Whether to use tabs for aligning align_with_tabs = false # false/true # Whether to bump out to the next tab when aligning align_on_tabstop = false # false/true # Whether to left-align numbers align_number_left = false # false/true # Align variable definitions in prototypes and functions align_func_params = false # false/true # Align parameters in single-line functions that have the same name. # The function names must already be aligned with each other. align_same_func_call_params = false # false/true # The span for aligning variable definitions (0=don't align) align_var_def_span = 0 # number # How to align the star in variable definitions. # 0=Part of the type 'void * foo;' # 1=Part of the variable 'void *foo;' # 2=Dangling 'void *foo;' align_var_def_star_style = 1 # number # How to align the '&' in variable definitions. # 0=Part of the type # 1=Part of the variable # 2=Dangling align_var_def_amp_style = 0 # number # The threshold for aligning variable definitions (0=no limit) align_var_def_thresh = 0 # number # The gap for aligning variable definitions align_var_def_gap = 0 # number # Whether to align the colon in struct bit fields align_var_def_colon = false # false/true # Whether to align any attribute after the variable name align_var_def_attribute = false # false/true # Whether to align inline struct/enum/union variable definitions align_var_def_inline = false # false/true # The span for aligning on '=' in assignments (0=don't align) align_assign_span = 0 # number # The threshold for aligning on '=' in assignments (0=no limit) align_assign_thresh = 0 # number # The span for aligning on '=' in enums (0=don't align) align_enum_equ_span = 0 # number # The threshold for aligning on '=' in enums (0=no limit) align_enum_equ_thresh = 0 # number # The span for aligning struct/union (0=don't align) align_var_struct_span = 0 # number # The threshold for aligning struct/union member definitions (0=no limit) align_var_struct_thresh = 0 # number # The gap for aligning struct/union member definitions align_var_struct_gap = 0 # number # The span for aligning struct initializer values (0=don't align) align_struct_init_span = 0 # number # The minimum space between the type and the synonym of a typedef align_typedef_gap = 0 # number # The span for aligning single-line typedefs (0=don't align) align_typedef_span = 0 # number # How to align typedef'd functions with other typedefs # 0: Don't mix them at all # 1: align the open paren with the types # 2: align the function type name with the other type names align_typedef_func = 0 # number # Controls the positioning of the '*' in typedefs. Just try it. # 0: Align on typedef type, ignore '*' # 1: The '*' is part of type name: typedef int *pint; # 2: The '*' is part of the type, but dangling: typedef int *pint; align_typedef_star_style = 0 # number # Controls the positioning of the '&' in typedefs. Just try it. # 0: Align on typedef type, ignore '&' # 1: The '&' is part of type name: typedef int &pint; # 2: The '&' is part of the type, but dangling: typedef int &pint; align_typedef_amp_style = 0 # number # The span for aligning comments that end lines (0=don't align) align_right_cmt_span = 5 # number # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment align_right_cmt_mix = false # false/true # If a trailing comment is more than this number of columns away from the text it follows, # it will qualify for being aligned. This has to be > 0 to do anything. align_right_cmt_gap = 0 # number # Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) align_right_cmt_at_col = 0 # number # The span for aligning function prototypes (0=don't align) align_func_proto_span = 0 # number # Minimum gap between the return type and the function name. align_func_proto_gap = 0 # number # Align function protos on the 'operator' keyword instead of what follows align_on_operator = false # false/true # Whether to mix aligning prototype and variable declarations. # If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. align_mix_var_proto = false # false/true # Align single-line functions with function prototypes, uses align_func_proto_span align_single_line_func = false # false/true # Aligning the open brace of single-line functions. # Requires align_single_line_func=true, uses align_func_proto_span align_single_line_brace = false # false/true # Gap for align_single_line_brace. align_single_line_brace_gap = 0 # number # The span for aligning ObjC msg spec (0=don't align) align_oc_msg_spec_span = 0 # number # Whether to align macros wrapped with a backslash and a newline. # This will not work right if the macro contains a multi-line comment. align_nl_cont = true # false/true # # Align macro functions and variables together align_pp_define_together = false # false/true # The minimum space between label and value of a preprocessor define align_pp_define_gap = 0 # number # The span for aligning on '#define' bodies (0=don't align) align_pp_define_span = 0 # number # Align lines that start with '<<' with previous '<<'. Default=true align_left_shift = true # false/true # Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) align_oc_msg_colon_span = 0 # number # If true, always align with the first parameter, even if it is too short. align_oc_msg_colon_first = false # false/true # Aligning parameters in an Obj-C '+' or '-' declaration on the ':' align_oc_decl_colon = false # false/true # # Newline adding and removing options # # Whether to collapse empty blocks between '{' and '}' nl_collapse_empty_body = true # false/true # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' nl_assign_leave_one_liners = true # false/true # Don't split one-line braced statements inside a class xx { } body nl_class_leave_one_liners = true # false/true # Don't split one-line enums: 'enum foo { BAR = 15 };' nl_enum_leave_one_liners = true # false/true # Don't split one-line get or set functions nl_getset_leave_one_liners = true # false/true # Don't split one-line function definitions - 'int foo() { return 0; }' nl_func_leave_one_liners = false # false/true # Don't split one-line if/else statements - 'if(a) b++;' nl_if_leave_one_liners = false # false/true # Don't split one-line OC messages nl_oc_msg_leave_one_liner = false # false/true # Add or remove newlines at the start of the file nl_start_of_file = remove # ignore/add/remove/force # The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' nl_start_of_file_min = 0 # number # Add or remove newline at the end of the file nl_end_of_file = force # ignore/add/remove/force # The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') nl_end_of_file_min = 1 # number # Add or remove newline between '=' and '{' nl_assign_brace = ignore # ignore/add/remove/force # Add or remove newline between '=' and '[' (D only) nl_assign_square = ignore # ignore/add/remove/force # Add or remove newline after '= [' (D only). Will also affect the newline before the ']' nl_after_square_assign = ignore # ignore/add/remove/force # The number of blank lines after a block of variable definitions at the top of a function body # 0 = No change (default) nl_func_var_def_blk = 0 # number # The number of newlines before a block of typedefs # 0 = No change (default) nl_typedef_blk_start = 0 # number # The number of newlines after a block of typedefs # 0 = No change (default) nl_typedef_blk_end = 0 # number # The maximum consecutive newlines within a block of typedefs # 0 = No change (default) nl_typedef_blk_in = 0 # number # The number of newlines before a block of variable definitions not at the top of a function body # 0 = No change (default) nl_var_def_blk_start = 0 # number # The number of newlines after a block of variable definitions not at the top of a function body # 0 = No change (default) nl_var_def_blk_end = 0 # number # The maximum consecutive newlines within a block of variable definitions # 0 = No change (default) nl_var_def_blk_in = 0 # number # Add or remove newline between a function call's ')' and '{', as in: # list_for_each(item, &list) { } nl_fcall_brace = ignore # ignore/add/remove/force # Add or remove newline between 'enum' and '{' nl_enum_brace = ignore # ignore/add/remove/force # Add or remove newline between 'struct and '{' nl_struct_brace = ignore # ignore/add/remove/force # Add or remove newline between 'union' and '{' nl_union_brace = ignore # ignore/add/remove/force # Add or remove newline between 'if' and '{' nl_if_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'else' nl_brace_else = ignore # ignore/add/remove/force # Add or remove newline between 'else if' and '{' # If set to ignore, nl_if_brace is used instead nl_elseif_brace = ignore # ignore/add/remove/force # Add or remove newline between 'else' and '{' nl_else_brace = ignore # ignore/add/remove/force # Add or remove newline between 'else' and 'if' nl_else_if = remove # ignore/add/remove/force # Add or remove newline between '}' and 'finally' nl_brace_finally = ignore # ignore/add/remove/force # Add or remove newline between 'finally' and '{' nl_finally_brace = ignore # ignore/add/remove/force # Add or remove newline between 'try' and '{' nl_try_brace = ignore # ignore/add/remove/force # Add or remove newline between get/set and '{' nl_getset_brace = ignore # ignore/add/remove/force # Add or remove newline between 'for' and '{' nl_for_brace = ignore # ignore/add/remove/force # Add or remove newline between 'catch' and '{' nl_catch_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'catch' nl_brace_catch = ignore # ignore/add/remove/force # Add or remove newline between 'while' and '{' nl_while_brace = ignore # ignore/add/remove/force # Add or remove newline between 'scope (x)' and '{' (D) nl_scope_brace = ignore # ignore/add/remove/force # Add or remove newline between 'unittest' and '{' (D) nl_unittest_brace = ignore # ignore/add/remove/force # Add or remove newline between 'version (x)' and '{' (D) nl_version_brace = ignore # ignore/add/remove/force # Add or remove newline between 'using' and '{' nl_using_brace = ignore # ignore/add/remove/force # Add or remove newline between two open or close braces. # Due to general newline/brace handling, REMOVE may not work. nl_brace_brace = force # ignore/add/remove/force # Add or remove newline between 'do' and '{' nl_do_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement nl_brace_while = ignore # ignore/add/remove/force # Add or remove newline between 'switch' and '{' nl_switch_brace = ignore # ignore/add/remove/force # Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. # Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. nl_multi_line_cond = false # false/true # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = false # false/true # Whether to put a newline before 'case' statement nl_before_case = false # false/true # Add or remove newline between ')' and 'throw' nl_before_throw = ignore # ignore/add/remove/force # Whether to put a newline after 'case' statement nl_after_case = false # false/true # Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. nl_case_colon_brace = ignore # ignore/add/remove/force # Newline between namespace and { nl_namespace_brace = remove # ignore/add/remove/force # Add or remove newline between 'template<>' and whatever follows. nl_template_class = add # ignore/add/remove/force # Add or remove newline between 'class' and '{' nl_class_brace = force # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member initialization nl_class_init_args = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a function definition nl_func_type_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name inside a class {} # Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. nl_func_type_name_class = ignore # ignore/add/remove/force # Add or remove newline between function scope and name in a definition # Controls the newline after '::' in 'void A::f() { }' nl_func_scope_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype nl_func_proto_type_name = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' nl_func_paren = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the definition nl_func_def_paren = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function declaration nl_func_decl_start = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function definition nl_func_def_start = ignore # ignore/add/remove/force # Overrides nl_func_decl_start when there is only one parameter. nl_func_decl_start_single = ignore # ignore/add/remove/force # Overrides nl_func_def_start when there is only one parameter. nl_func_def_start_single = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function declaration nl_func_decl_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function definition nl_func_def_args = ignore # ignore/add/remove/force # Add or remove newline before the ')' in a function declaration nl_func_decl_end = ignore # ignore/add/remove/force # Add or remove newline before the ')' in a function definition nl_func_def_end = ignore # ignore/add/remove/force # Overrides nl_func_decl_end when there is only one parameter. nl_func_decl_end_single = ignore # ignore/add/remove/force # Overrides nl_func_def_end when there is only one parameter. nl_func_def_end_single = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function declaration. nl_func_decl_empty = remove # ignore/add/remove/force # Add or remove newline between '()' in a function definition. nl_func_def_empty = remove # ignore/add/remove/force # Whether to put each OC message parameter on a separate line # See nl_oc_msg_leave_one_liner nl_oc_msg_args = false # false/true # Add or remove newline between function signature and '{' nl_fdef_brace = force # ignore/add/remove/force # Add or remove a newline between the return keyword and return expression. nl_return_expr = remove # ignore/add/remove/force # Whether to put a newline after semicolons, except in 'for' statements nl_after_semicolon = true # false/true # Whether to put a newline after brace open. # This also adds a newline before the matching brace close. nl_after_brace_open = true # false/true # If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is # placed between the open brace and a trailing single-line comment. nl_after_brace_open_cmt = false # false/true # Whether to put a newline after a virtual brace open with a non-empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open = false # false/true # Whether to put a newline after a virtual brace open with an empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open_empty = false # false/true # Whether to put a newline after a brace close. # Does not apply if followed by a necessary ';'. nl_after_brace_close = false # false/true # Whether to put a newline after a virtual brace close. # Would add a newline before return in: 'if (foo) a++; return;' nl_after_vbrace_close = false # false/true # Control the newline between the close brace and 'b' in: 'struct { int a; } b;' # Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close nl_brace_struct_var = ignore # ignore/add/remove/force # Whether to alter newlines in '#define' macros nl_define_macro = true # false/true # Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' nl_squeeze_ifdef = false # false/true # Add or remove blank line before 'if' nl_before_if = ignore # ignore/add/remove/force # Add or remove blank line after 'if' statement nl_after_if = ignore # ignore/add/remove/force # Add or remove blank line before 'for' nl_before_for = ignore # ignore/add/remove/force # Add or remove blank line after 'for' statement nl_after_for = ignore # ignore/add/remove/force # Add or remove blank line before 'while' nl_before_while = ignore # ignore/add/remove/force # Add or remove blank line after 'while' statement nl_after_while = ignore # ignore/add/remove/force # Add or remove blank line before 'switch' nl_before_switch = ignore # ignore/add/remove/force # Add or remove blank line after 'switch' statement nl_after_switch = ignore # ignore/add/remove/force # Add or remove blank line before 'do' nl_before_do = ignore # ignore/add/remove/force # Add or remove blank line after 'do/while' statement nl_after_do = ignore # ignore/add/remove/force # Whether to double-space commented-entries in struct/enum nl_ds_struct_enum_cmt = false # false/true # Whether to double-space before the close brace of a struct/union/enum # (lower priority than 'eat_blanks_before_close_brace') nl_ds_struct_enum_close_brace = false # false/true # Add or remove a newline around a class colon. # Related to pos_class_colon, nl_class_init_args, and pos_comma. nl_class_colon = ignore # ignore/add/remove/force # Change simple unbraced if statements into a one-liner # 'if(b)\n i++;' => 'if(b) i++;' nl_create_if_one_liner = false # false/true # Change simple unbraced for statements into a one-liner # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' nl_create_for_one_liner = false # false/true # Change simple unbraced while statements into a one-liner # 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' nl_create_while_one_liner = false # false/true # # Positioning options # # The position of arithmetic operators in wrapped expressions pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of assignment in wrapped expressions. # Do not affect '=' followed by '{' pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of boolean operators in wrapped expressions pos_bool = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of comparison operators in wrapped expressions pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of conditional (b ? t : f) operators in wrapped expressions pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in wrapped expressions pos_comma = trail # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in the constructor initialization list pos_class_comma = trail # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of colons between constructor and member initialization pos_class_colon = lead_force # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # # Line Splitting options # # Try to limit code width to N number of columns code_width = 0 # number # Whether to fully split long 'for' statements at semi-colons ls_for_split_full = true # false/true # Whether to fully split long function protos/calls at commas ls_func_split_full = true # false/true # Whether to split lines as close to code_width as possible and ignore some groupings ls_code_width = false # false/true # # Blank line options # # The maximum consecutive newlines nl_max = 2 # number # The number of newlines after a function prototype, if followed by another function prototype nl_after_func_proto = 0 # number # The number of newlines after a function prototype, if not followed by another function prototype nl_after_func_proto_group = 0 # number # The number of newlines after '}' of a multi-line function body nl_after_func_body = 0 # number # The number of newlines after '}' of a multi-line function body in a class declaration nl_after_func_body_class = 0 # number # The number of newlines after '}' of a single line function body nl_after_func_body_one_liner = 0 # number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. nl_before_block_comment = 0 # number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. nl_before_c_comment = 0 # number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. nl_before_cpp_comment = 0 # number # Whether to force a newline after a multi-line comment. nl_after_multiline_comment = false # false/true # The number of newlines after '}' or ';' of a struct/enum/union definition nl_after_struct = 0 # number # The number of newlines after '}' or ';' of a class definition nl_after_class = 0 # number # The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # Will not change the newline count if after a brace open. # 0 = No change. nl_before_access_spec = 2 # number # The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # 0 = No change. nl_after_access_spec = 1 # number # The number of newlines between a function def and the function comment. # 0 = No change. nl_comment_func_def = 0 # number # The number of newlines after a try-catch-finally block that isn't followed by a brace close. # 0 = No change. nl_after_try_catch_finally = 0 # number # The number of newlines before and after a property, indexer or event decl. # 0 = No change. nl_around_cs_property = 0 # number # The number of newlines between the get/set/add/remove handlers in C#. # 0 = No change. nl_between_get_set = 0 # number # Add or remove newline between C# property and the '{' nl_property_brace = ignore # ignore/add/remove/force # Whether to remove blank lines after '{' eat_blanks_after_open_brace = true # false/true # Whether to remove blank lines before '}' eat_blanks_before_close_brace = true # false/true # How aggressively to remove extra newlines not in preproc. # 0: No change # 1: Remove most newlines not handled by other config # 2: Remove all newlines and reformat completely by config nl_remove_extra_newlines = 0 # number # Whether to put a blank line before 'return' statements, unless after an open brace. nl_before_return = false # false/true # Whether to put a blank line after 'return' statements, unless followed by a close brace. nl_after_return = false # false/true # Whether to put a newline after a Java annotation statement. # Only affects annotations that are after a newline. nl_after_annotation = ignore # ignore/add/remove/force # Controls the newline between two annotations. nl_between_annotation = ignore # ignore/add/remove/force # # Code modifying options (non-whitespace) # # Add or remove braces on single-line 'do' statement mod_full_brace_do = ignore # ignore/add/remove/force # Add or remove braces on single-line 'for' statement mod_full_brace_for = ignore # ignore/add/remove/force # Add or remove braces on single-line function definitions. (Pawn) mod_full_brace_function = ignore # ignore/add/remove/force # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. mod_full_brace_if = ignore # ignore/add/remove/force # Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. # If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. mod_full_brace_if_chain = false # false/true # Don't remove braces around statements that span N newlines mod_full_brace_nl = 0 # number # Add or remove braces on single-line 'while' statement mod_full_brace_while = ignore # ignore/add/remove/force # Add or remove braces on single-line 'using ()' statement mod_full_brace_using = ignore # ignore/add/remove/force # Add or remove unnecessary paren on 'return' statement mod_paren_on_return = ignore # ignore/add/remove/force # Whether to change optional semicolons to real semicolons mod_pawn_semicolon = false # false/true # Add parens on 'while' and 'if' statement around bools mod_full_paren_if_bool = false # false/true # Whether to remove superfluous semicolons mod_remove_extra_semicolon = true # false/true # If a function body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 0 # number # If a switch body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_switch_closebrace_comment = 0 # number # If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after # the #endif, a comment will be added. mod_add_long_ifdef_endif_comment = 0 # number # If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after # the #else, a comment will be added. mod_add_long_ifdef_else_comment = 0 # number # If TRUE, will sort consecutive single-line 'import' statements [Java, D] mod_sort_import = false # false/true # If TRUE, will sort consecutive single-line 'using' statements [C#] mod_sort_using = false # false/true # If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. mod_sort_include = false # false/true # If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. mod_move_case_break = false # false/true # Will add or remove the braces around a fully braced case statement. # Will only remove the braces if there are no variable declarations in the block. mod_case_brace = ignore # ignore/add/remove/force # If TRUE, it will remove a void 'return;' that appears as the last statement in a function. mod_remove_empty_return = false # false/true # # Comment modifications # # Try to wrap comments at cmt_width columns cmt_width = 0 # number # Set the comment reflow mode (default: 0) # 0: no reflowing (apart from the line wrapping due to cmt_width) # 1: no touching at all # 2: full reflow cmt_reflow_mode = 0 # number # If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars. # Default is true. cmt_indent_multi = false # false/true # Whether to group c-comments that look like they are in a block cmt_c_group = false # false/true # Whether to put an empty '/*' on the first line of the combined c-comment cmt_c_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined c-comment cmt_c_nl_end = false # false/true # Whether to group cpp-comments that look like they are in a block cmt_cpp_group = false # false/true # Whether to put an empty '/*' on the first line of the combined cpp-comment cmt_cpp_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined cpp-comment cmt_cpp_nl_end = false # false/true # Whether to change cpp-comments into c-comments cmt_cpp_to_c = false # false/true # Whether to put a star on subsequent comment lines cmt_star_cont = false # false/true # The number of spaces to insert at the start of subsequent comment lines cmt_sp_before_star_cont = 0 # number # The number of spaces to insert after the star on subsequent comment lines cmt_sp_after_star_cont = 0 # number # For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of # the comment are the same length. Default=True cmt_multi_check_last = true # false/true # The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_header = "" # string # The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_footer = "" # string # The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. # Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. # Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } cmt_insert_func_header = "" # string # The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. # Will substitute $(class) with the class name. cmt_insert_class_header = "" # string # The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment. # Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. cmt_insert_oc_msg_header = "" # string # If a preprocessor is encountered when stepping backwards from a function name, then # this option decides whether the comment should be inserted. # Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. cmt_insert_before_preproc = false # false/true # # Preprocessor options # # Control indent of preprocessors inside #if blocks at brace level 0 pp_indent = ignore # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) pp_indent_at_level = false # false/true # If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. pp_indent_count = 1 # number # Add or remove space after # based on pp_level of #if blocks pp_space = ignore # ignore/add/remove/force # Sets the number of spaces added with pp_space pp_space_count = 0 # number # The indent for #region and #endregion in C# and '#pragma region' in C/C++ pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion pp_region_indent_code = false # false/true # If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level pp_indent_if = 0 # number # Control whether to indent the code between #if, #else and #endif when not at file-level pp_if_indent_code = false # false/true # Whether to indent '#define' at the brace level (true) or from column 1 (false) pp_define_at_level = false # false/true # You can force a token to be a type with the 'type' option. # Example: # type myfoo1 myfoo2 # # You can create custom macro-based indentation using macro-open, # macro-else and macro-close. # Example: # macro-open BEGIN_TEMPLATE_MESSAGE_MAP # macro-open BEGIN_MESSAGE_MAP # macro-close END_MESSAGE_MAP # # You can assign any keyword to any type with the set option. # set func_call_user _ N_ # # The full syntax description of all custom definition config entries # is shown below: # # define custom tokens as: # - embed whitespace in token using '' escape character, or # put token in quotes # - these: ' " and ` are recognized as quote delimiters # # type token1 token2 token3 ... # ^ optionally specify multiple tokens on a single line # define def_token output_token # ^ output_token is optional, then NULL is assumed # macro-open token # macro-close token # macro-else token # set id token1 token2 ... # ^ optionally specify multiple tokens on a single line # ^ id is one of the names in token_enum.h sans the CT_ prefix, # e.g. PP_PRAGMA # # all tokens are separated by any mix of ',' commas, '=' equal signs # and whitespace (space, tab) # stxxl-1.4.1/doc/tutorial_stream.dox000644 001411 000144 00000057073 12405153572 017214 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_stream.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { namespace stream { /** \page tutorial_stream Tutorial for the Stream Package \author Timo Bingmann (2012-06-11) This page gives a short introduction into the stream package. First the main abstractions are discussed and then some examples on how to utilize the existing algorithms are developed. All example code can be found in \ref examples/stream/stream1.cpp In \ref tutorial_stream_edgesort another example is given, where an existing algorithm is "pipelined". \section stream1 Abstraction, Interface and a Simple Example The stream package is built around the abstract notion of an object being able to produce a sequence of output values. Only three simple operations are necessary: - Retrieval of the current value: prefix \c * operator - Advance to the next value in the sequence: prefix \c ++ operator - Indication of the sequence's end: \c empty() function The most common place object that fits easily into this abstraction is the random generator. Actually, a random generator only requires two operations: it can be queried for its current value and be instructed to calculate/advance to new value. Of course the random sequence should be unbounded, so an \c empty() function would always be false. Nevertheless, this common-place example illustrates the purpose of the stream interface pretty well. All stream objects must support the three operations above, they form the stream algorithm concept. In C++ a class conforms to this concept if it implements the following interface: \code struct stream_object { // Type of the values in the output sequence. typedef output_type value_type; // Retrieval prefix * operator (like dereferencing a pointer or iterator). const value_type& operator* () const; // Prefix increment ++ operator, which advances the stream to the next value. stream_object& operator++ (); // Empty indicator. True if the last ++ operation could not fetch a value. bool empty() const; }; \endcode A very simple stream object that produces the sequence 1,2,3,4,....,1000 is shown in the following snippet: \code struct counter_object { // This stream produces a sequence of integers. typedef int value_type; private: // A class attribute to save the current value. int m_current_value; public: // A constructor to set the initial value to 1. counter_object() : m_current_value(1) { } // The retrieve operator returning the current value. const value_type& operator* () const { return m_current_value; } // Increment operator advancing to the next integer. counter_object& operator++ () { ++m_current_value; return *this; } // Empty indicator, which in this case can check the current value. bool empty() const { return (m_current_value > 1000); } }; \endcode After this verbose interface definition, the actual iteration over a stream object can be done as follows: \code counter_object counter; while (!counter.empty()) { std::cout << *counter << " "; ++counter; } std::cout << std::endl; \endcode For those who like to shorten everything into fewer lines, the above can also be expressed as a for loop: \code for (counter_object cnt; !cnt.empty(); ++cnt) { std::cout << *cnt << " "; } std::cout << std::endl; \endcode Both loops will print the following output: \verbatim 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [...] 995 996 997 998 999 1000 \endverbatim \section stream2 Pipelining: Plugging Stream Objects Together The stream interface is so very useful for external memory algorithms because it represents the concept of sequential access to a stream of individual values. While the simple example above only works with integers, the \c value_type of streams will more often contain complex tuple structs with multiple components. A stream algorithm can then be constructed from multiple stream objects that pass data from one to another. This notion of "plugging together" stream objects is used in the following example to calculate the square of each value of an integer sequence: \code template struct squaring_object { // This stream produces a sequence of integers. typedef int value_type; private: // A reference to another stream of integers, which are our input. InputStream& m_input_stream; // A temporary value buffer to hold the current square for retrieval. value_type m_current_value; public: // A constructor taking another stream of integers as input. squaring_object(InputStream& input_stream) : m_input_stream(input_stream) { if (!m_input_stream.empty()) { m_current_value = *m_input_stream; m_current_value = m_current_value * m_current_value; } } // The retrieve operator returning the square of the input stream. const value_type& operator* () const { return m_current_value; } // Increment operator: handled by incrementing the input stream. squaring_object& operator++ () { ++m_input_stream; if (!m_input_stream.empty()) { m_current_value = *m_input_stream; m_current_value = m_current_value * m_current_value; } return *this; } // Empty indicator: this stream is empty when the input stream is. bool empty() const { return m_input_stream.empty(); } }; \endcode For a beginner in stream object programming, the squaring example contains multiple unexpected, verbose complications. - We wish to allow many different integer sequences as input streams to the squaring class. For this we use template meta-programming and define squaring to take any class as \c InputStream template parameter. As yet, in C++ we cannot syntactically define which concepts the template parameters must fulfill, in this case one would require \c InputStream to implement the stream interface. - After defining the input stream class, one will usually need an instantiated object of that class inside the new stream class. Most common practice is to define references to other streams as class attributes, and have the actual objects be passed to the constructor of the new stream object.
    In the case of the squaring class, any \c InputStream object is accepted by the constructor and a reference is saved into \c m_input_stream. - As second attribute, the squaring class contains m_current_value. The additional temporary value is required in this case because \c operator*() must return a const-reference, so the square must actually be stored in a variable after it is calculated. Now note that the squaring operation in this version is implemented at two places: in the constructor and the \c operator++().
    This is necessary, because the stream concept requires that the first value be immediately available after construction! Therefore it must be calculated in the constructor, and this code is usually a duplicate to the action done in \c operator++(). A real implementation would probably combine the calculation code into a \c process() function and also do additional allocation work in the constructor. An instance of the \c counter_object can be plugged into a \c squaring_object as done in the following example: \code counter_object counter; squaring_object squares(counter); while (!squares.empty()) { std::cout << *squares << " "; ++squares; } std::cout << std::endl; \endcode The example outputs: \verbatim 1 4 9 16 25 36 49 64 81 100 121 144 169 [...] 986049 988036 990025 992016 994009 996004 998001 1000000 \endverbatim \section stream3 Miscellaneous Utilities Provided by the Stream Package The above examples are pure C++ interface manipulations and do not even require STXXL. However, when writing stream algorithms you can take advantage of the utilities provided by the stream package to create complex algorithms. Probably the most useful is the pair of sorting classes, which will be discussed after a few preliminaries. More complex algorithms will most often use tuples as values passed from one stream to another. These tuples wrap all information fields of a specific piece of data. Simple tuples can be created using \c std::pair, tuples with larger number of components can use Boost.Tuple or just plain structs with multiple fields. (In the tuple case, the temporary value inside the stream struct can mostly be avoided.) The stream package contains utilities to plug stream classes together to form complex algorithms. The following few examples are very basic algorithms: Very often the input to a sequence of stream classes comes from an array or other container. In this case one requires an input stream object, which iterates through the container and outputs each element once. STXXL provides iterator2stream for this common purpose: \code std::vector intvector; // (fill intvector) // define stream class iterating over an integer vector typedef stxxl::stream::iterator2stream< std::vector::const_iterator > intstream_type; // instantiate the stream object, iterate from begin to end of intvector. intstream_type intstream (intvector.begin(), intvector.end()); // plug in squaring object after vector iterator stream. squaring_object squares(intstream); \endcode Most important: if the input container is a stxxl::vector, then one should use vector_iterator2stream, because this class will prefetch additional blocks from the vector while processing the stream. \code stxxl::vector intvector; // (fill intvector) // define stream class iterating over an integer STXXL vector typedef stxxl::stream::vector_iterator2stream< stxxl::vector::const_iterator > intstream_type; // instantiate the stream object, iterate from begin to end of intvector using prefetching intstream_type intstream (intvector.begin(), intvector.end()); // plug in squaring object after vector iterator stream. squaring_object squares(intstream); \endcode The opposite to iterator2stream is to collect the output of a sequence of stream objects into a container or stxxl::vector. This operation is called \c materialize and also comes in the general version and a special version for the STXXL-vector, which uses asynchronous writes. This example shows how to materialize a stream into a usual STL vector. \code // construct the squared counter stream counter_object counter; squaring_object squares(counter); // allocate vector of 100 integers std::vector intvector (100); // materialize 100 integers from stream and put into vector stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); \endcode And the only modification needed to support larger data sets is to materialize to an STXXL vector: \code // construct the squared counter stream counter_object counter; squaring_object squares(counter); // allocate STXXL vector of 100 integers stxxl::vector intvector (100); // materialize 100 integers from stream and put into STXXL vector stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); \endcode \section stream4 Sorting As Provided by the Stream Package Maybe the most important set of tools in the stream package is the pairs of sorter classes runs_creator and runs_merger. The general way to sort a sequential input stream is to first consolidate a large number of input items in an internal memory buffer. Then when the buffer is full, it can be sorted in internal memory and subsequently written out to disk. This sorted sequence is then called a run. When the input stream is finished and the sorted output must be produced, theses sorted sequences can efficiently be merged using a tournament tree or similar multi-way comparison structure. (see \ref design_algo_sorting.) STXXL implements this using two stream classes: runs_creator and runs_merger. The following examples shows how to sort the integer sequence 1,2,...,1000 first by the right-most decimal digit, then by its absolute value (yes a somewhat constructed example, but it serves its purpose well.) For all sorters a comparator object is required which tells the sorter which of two objects is the smaller one. This is similar to the requirements of the usual STL, however, the STXXL sorters need to additional functions: \c min_value() and \c max_value() which are used as padding sentinels. These functions return the smallest and highest possible values of the given data type. \code // define comparator class: compare right-most decimal and then absolute value struct CompareMod10 { // comparison operator() returning true if (a < b) inline bool operator() (int a, int b) const { if ((a % 10) == (b % 10)) return a < b; else return (a % 10) < (b % 10); } // smallest possible integer value int min_value() const { return INT_MIN; } // largest possible integer value int max_value() const { return INT_MAX; } }; \endcode All sorters steps require an internal memory buffer. This size can be fixed using a parameter to runs_creator and runs_merger. The following example code instantiates a counter object, plugs this into a runs_creator which is followed by a runs_merger. \code static const int ram_use = 10*1024*1024; // amount of memory to use in runs creation counter_object counter; // the counter stream from first examples // define a runs sorter for the counter stream which order by CompareMod10 object. typedef stxxl::stream::runs_creator rc_counter_type; // instance of CompareMod10 comparator class CompareMod10 comparemod10; // instance of runs_creator which reads the counter stream. rc_counter_type rc_counter (counter, comparemod10, ram_use); // define a runs merger for the sorted runs from rc_counter. typedef stxxl::stream::runs_merger rm_counter_type; // instance of runs_merger which merges sorted runs from rc_counter. rm_counter_type rm_counter (rc_counter.result(), comparemod10, ram_use); // read sorted stream: runs_merger also conforms to the stream interface. while (!rm_counter.empty()) { std::cout << *rm_counter << " "; ++rm_counter; } std::cout << std::endl; \endcode The output of the code above is: \verbatim 10 20 30 40 50 60 70 80 [...] 990 1000 1 11 21 31 41 51 61 [...] 909 919 929 939 949 959 969 979 989 999 \endverbatim Note that in the above example the input of the runs_creator is itself a stream. If however the data is not naturally available as a stream, one can use a variant of runs_creator which accepts input via a \c push() function. This is more useful when using an imperative programming style. Note that the runs_merger does not change. \code static const int ram_use = 10*1024*1024; // amount of memory to use in runs creation // define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. typedef stxxl::stream::runs_creator, CompareMod10> rc_counter_type; // instance of CompareMod10 comparator class. CompareMod10 comparemod10; // instance of runs_creator which waits for input. rc_counter_type rc_counter (comparemod10, ram_use); // write sequence of integers into runs for (int i = 1; i <= 1000; ++i) rc_counter.push(i); // define a runs merger for the sorted runs from rc_counter. typedef stxxl::stream::runs_merger rm_counter_type; // instance of runs_merger which merges sorted runs from rc_counter. rm_counter_type rm_counter (rc_counter.result(), comparemod10, ram_use); // read sorted stream: runs_merger also conforms to the stream interface. while (!rm_counter.empty()) { std::cout << *rm_counter << " "; ++rm_counter; } std::cout << std::endl; \endcode And as the last example in this tutorial we show how to use stxxl::sorter, which combines runs_creator and runs_merger into one object. The sorter has two states: input and output. During input, new elements can be sorted using \c push(). Then to switch to output state, the function \c sort() is called, after which the sorter can be queried using the usual stream interface. \code static const int ram_use = 10*1024*1024; // amount of memory to use in runs creation // define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. typedef stxxl::sorter sr_counter_type; // instance of CompareMod10 comparator class. CompareMod10 comparemod10; // instance of sorter which waits for input. sr_counter_type sr_counter (comparemod10, ram_use); // write sequence of integers into sorter, which creates sorted runs for (int i = 1; i <= 1000; ++i) sr_counter.push(i); // signal sorter that the input stream is finished and switch to output mode. sr_counter.sort(); // read sorted stream: sorter also conforms to the stream interface. while (!sr_counter.empty()) { std::cout << *sr_counter << " "; ++sr_counter; } std::cout << std::endl; \endcode All three examples have the same output. \example examples/stream/stream1.cpp This example code is explain in the \ref tutorial_stream. */ /** \page tutorial_stream_edgesort Generating Random Graphs using Streams \author Roman Dementiev (2007) This page gives an example of how an application using "traditional" containers and algorithms can be converted into using pipelines streaming. The purpose of our example is to generate a huge random directed graph in a sorted edge array representation, i.e. the edges in the edge array must be sorted lexicographically. The definitions of the classes \c edge, \c random_edge and \c edge_cmp are in the following code listing. \code struct edge { // edge class int src, dst; // nodes edge() {} edge(int src_, int dst_): src(src_), dst(dst_) {} bool operator == (const edge & b) const { return src == b.src && dst == b.dst; } }; struct random_edge { // random edge generator functor edge operator () () const { edge Edge(random() - 1, random() - 1); while(Edge.dst == Edge.src) Edge.dst = random() - 1 ; // no self-loops return Edge; } }; struct edge_cmp { // edge comparison functor edge min_value() const { return edge(std::numeric_limits::min(),0); }; edge max_value() const { return edge(std::numeric_limits::max(),0); }; bool operator () (const edge & a, const edge & b) const { return a.src < b.src || (a.src == b.src && a.dst < b.dst); } }; \endcode A straightforward procedure to generate the graph is to: 1) generate a sequence of random edges, 2) sort the sequence, 3) remove duplicate edges from it. If we ignore definitions of helper classes the STL/STXXL code of the algorithm implementation is only five lines long: \code // create vector stxxl::vector ExtEdgeVec(10000000000ULL); // generate random edges stxxl::generate(ExtEdgeVec.begin(), ExtEdgeVec.end(), random_edge()); // sort edges by source vertex stxxl::sort(ExtEdgeVec.begin(), ExtEdgeVec.end(), edge_cmp(), 512*1024*1024); // unify equal edges stxxl::vector::iterator NewEnd = std::unique(ExtEdgeVec.begin(), ExtEdgeVec.end()); ExtEdgeVec.resize(NewEnd - ExtEdgeVec.begin()); \endcode Line 2 creates an STXXL external memory vector with 10 billion edges. Line 4 fills the vector with random edges (stxxl::generate from the STL is used). In the next line the STXXL external memory sorter sorts randomly generated edges using 512 megabytes of internal memory. The lexicographical order is defined by functor \c my_cmp, stxxl::sort also requires the comparison functor to provide upper and lower bounds for the elements being sorted. Line 8 deletes duplicate edges in the external memory vector with the help of the STL \c std::unique algorithm. The \c NewEnd vector iterator points to the right boundary of the range without duplicates. Finally (in the last line), we chop the vector at the \c NewEnd boundary. Now we count the number of I/Os performed by this example: external vector construction takes no I/Os; filling with random values requires a scan --- \f$ N/DB \f$ I/Os; sorting will take \f$ 4N/DB \f$ I/Os; duplicate removal needs no more than \f$ 2N/DB \f$ I/Os; chopping a vector is I/O-free. The total number of I/Os is \f$ 7N/DB \f$. # Pipelined random graph generation Now we "pipeline" the random graph generation example shown in the previous chapter. The data flow graph of the algorithm is presented in the following figure: \image html pipeline_randomgraph_small.png "Pipeline of Random Graph Generator" The following code listing shows the pipelined code of the algorithm, the definitions of \c edge, \c random_edge, and \c edge_cmp are assumed to be available from the listing in the previous section. \code class random_edge_stream { stxxl::int64 counter; edge current; random_edge_stream(); public: typedef edge value_type; random_edge_stream(stxxl::int64 elements) : counter(elements), current(random_edge()()) { } const edge & operator * () const { return current; } const edge * operator ->() const { return ¤t; } random_edge_stream & operator ++ () { --counter; current = random_edge()(); return *this; } bool empty() const { return counter == 0; } }; random_edge_stream RandomStream(10000000000ULL); typedef stxxl::stream::sort sorted_stream; sorted_stream SortedStream(RandomStream, edge_cmp(), 512*1024*1024); typedef stxxl::stream::unique unique_stream_type; unique_stream_type UniqueStream(SortedStream); stxxl::vector ExtEdgeVec(10000000000ULL); stxxl::vector::iterator NewEnd = stxxl::stream::materialize(UniqueStream, ExtEdgeVec.begin()); ExtEdgeVec.resize(NewEnd - ExtEdgeVec.begin()); \endcode Since the sorter of the streaming layer accepts an \c stream input, we do not need to output the random edges. Rather, we generate them on the fly. The \c random_edge_stream object (model of \c stream) supplies the sorter with a stream of random edges. We define the type of the sorter node as \c sorted_stream; it is parameterized by the type of the input stream and the type of the comparison function object. Then a \c SortedStream object is created and its input is attached to the \c RandomStream object's output. The internal memory consumption of the sorter stream object is limited to 512 MB. The \c UniqueStream object filters the duplicates in its input edge stream. The generic \c stream::unique stream class stems from the STXXL library. The stream::materialize function records the content of the \c UniqueStream into the external memory vector. As in the previous non-pipelined version, we cut the vector at the \c NewEnd boundary. Let us count the number of I/Os the program performs: random edge generation by \c RandomStream costs no I/O; sorting in \c SortedStream needs to store the sorted runs and read them again to merge --- \f$ 2N/DB \f$ I/Os; \c UniqueStream deletes duplicates on the fly, it does not need any I/O; and materializing the final output can cost up to \f$ N/DB \f$ I/Os. All in all, the program only incurs \f$ 3N/DB \f$ I/Os, compared to \f$ 7N/DB \f$ for the non-pipelined code. */ } // namespace stream } // namespace stxxl stxxl-1.4.1/doc/tutorial_matrix.dox000644 001411 000144 00000007470 12405153572 017221 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_matrix.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_matrix STXXL Matrix This section introduces into the STXXL matrix container (to learn more about the structure of stxxl::matrix, see section \ref design_matrix). ### Create a STXXL Matrix Before using a STXXL matrix, we initially have to define and then to instantiate a matrix object. Two template parameters are required to define a stxxl::matrix container. ValueType defines the type of the contained objects (must be a POD with no references to internal memory) and BlockSizeLength specifies the side length of the square submatrices (blocks). BlockSizeLength is given in bits and must be a multiple of k assuming a valueType of k bits. The block schedular is used for swapping of blocks and provide blocks for temporary storage and expects the type of swappable_blocks to manage as a parameter. Can be some specialized subclass. We used matrix_swappable_block as a subclass which holds the same template parameters as the aforementioned stxxl::matrix container. \code // Define integer matrix // template paramter typedef stxxl::block_scheduler > block_schedular_type; // template paramter typedef stxxl::matrix matrix_type; // construct block schedular object which uses 64 MiB internal memory for caching block_schedular_type my_bs(64*1024*1024); \endcode Instanciate three new height x width (3 x 3 in this example) matrices A, B and C using a block schedular. \n Note that all values are initially set to zero. \code int height = 3; int width = 3; matrix_type A(my_bs, height, width); // creates a new matrix A of given dimensions. Elements' values are set to zero. matrix_type B(my_bs, height, width); // B matrix_type C(my_bs, height, width); // C \endcode ### Insert / Access elements To insert and access elements, the STXXL matrix container intends different iterators. Iterate for example row-by-row beginning with the top row can be done with the row_major_iterator. The operator * accesses a single element the iterator points to just now. \code typedef matrix_type::row_major_iterator row_iterator; // let it_A point to the first element of matrix A and advance till the very last element of matrix A is reached for (row_iterator it_A = A.begin(); it_A != A.end(); ++it_A) { *it_A = 1; // set current matrix element to 1 } \endcode ### Determine size of matrix To detect the height and width of a given matrix C, we can call: \code std::cout << "height: " << C.get_height() << " - " << "width: " << C.get_width() << std::endl; \endcode ### Matrix Operations The STXXL Matrix container provides the following arithmetic operations: - Addition, Substraction and Multiplication of two matrices A and B. \code C = A + B; C = A - B; C = A * B; \endcode - Transposition and Zeroing of a matrix C. \code C.transpose(); C.set_zero(); \endcode ### A minimal working example of STXXL's Matrix (See \ref examples/containers/matrix1.cpp for the sourcecode of the following example). \snippet examples/containers/matrix1.cpp example \example examples/containers/matrix1.cpp This example code is explained in the \ref tutorial_matrix section */ } // namespace stxxl stxxl-1.4.1/doc/references.bib000644 001411 000144 00000043366 12350112610 016044 0ustar00tbusers000000 000000 % -*- fill-column: 1000 -*- @InProceedings{APV02, author = "L. Arge and O. Procopiuc and J. S. Vitter", title = "{Implementing I/O-efficient Data Structures Using TPIE}", booktitle = "10th European Symposium on Algorithms (ESA)", pages = "88--100", year = 2002, volume = 2461, series = "LNCS", publisher = "Springer" } @InProceedings{CraMeh99, author = "A. Crauser and K. Mehlhorn", title = "{LEDA-SM}, extending {LEDA} to Secondary Memory", booktitle = "3rd International Workshop on Algorithmic Engineering (WAE)", pages = "228--242", year = 1999, volume = 1668, series = "LNCS" } @Article{VitShr94both, author = "J. S. Vitter and E. A. M. Shriver", title = "Algorithms for parallel memory, {I/II}", journal = "Algorithmica", year = 1994, volume = 12, number = "2/3", pages = "110--169" } @InProceedings{HutSanVit01b, author = "D. A. Hutchinson and P. Sanders and J. S. Vitter", title = "Duality Between Prefetching and Queued Writing with Parallel Disks", booktitle = "9th European Symposium on Algorithms (ESA)", year = 2001, number = 2161, pages = "62--73", series = "LNCS", publisher = "Springer", } @InProceedings{DemSan03, author = "R. Dementiev and P. Sanders", title = "Asynchronous Parallel Disk Sorting", booktitle = "15th ACM Symposium on Parallelism in Algorithms and Architectures", pages = "138--148", year = 2003, address = "San Diego", } @Manual{tpie_manual, author = "Lars Arge and Rakesh Barve and David Hutchinson and Octavian Procopiuc and Laura Toma and Darren Erik Vengroff and Rajiv Wickeremesinghe", title = "{TPIE}: User manual and reference", year = 2003, month = nov } @InProceedings{DKMS05, author = "R. Dementiev and J. Mehnert and J. K{\"a}rkk{\"a}inen and P. Sanders", title = "{Better External Memory Suffix Array Construction}", booktitle = "Workshop on Algorithm Engineering {\&} Experiments", year = 2005, address = "Vancouver", note = "\url{http://i10www.ira.uka.de/dementiev/files/DKMS05.pdf}" } @InProceedings{VitHut01, author = "J. S. Vitter and D. A. Hutchinson", title = "Distribution sort with randomized cycling", booktitle = "12th ACM-SIAM Symposium on Discrete Algorithms", pages = "77--86", year = 2001 } @Article{BarGroVit97, author = "R. D. Barve and E. F. Grove and J. S. Vitter", title = "Simple Randomized Mergesort on Parallel Disks", journal = "Parallel Computing", year = 1997, volume = 23, number = 4, pages = "601--631", annote = "auch TR CS-1996-15, Duke" } @Book{BicShaw2003, author = {L.F. Bic and A.C. Shaw}, title = {Operating Systems Principles}, publisher = {Pearson Education}, year = {2003}, } @PhdThesis{ZehPhd, author = {Norbert Ralf Zeh}, title = {I/O Efficient Algorithms for Shortest Path Related Problems}, school = {Carleton University, Ottawa}, year = {2002}, month = apr, } @InProceedings{ChiEtAl95, author = "Y.-J. Chiang and M. T. Goodrich and E. F. Grove and R. Tamasia and D. E. Vengroff and J. S. Vitter", title = "External memory graph algorithms", pages = "139--149", booktitle = "6th Annual {ACM}-{SIAM} Symposium on Discrete Algorithms", year = 1995 } @Article{San00b, author = "Peter Sanders", title = "Fast Priority Queues for Cached Memory", journal = "ACM Journal of Experimental Algorithmics", volume = 5, year = 2000, } @Book{MSS03, editor = "U. Meyer and P. Sanders and J. Sibeyn", title = "Algorithms for Memory Hierarchies", publisher = "Springer", year = 2003, volume = 2625, series = "LNCS Tutorial" } @InProceedings{Arg95, author = "L. Arge", title = "{The Buffer Tree: A New Technique for Optimal I/O-Algorithms}", number = 955, series = "LNCS", pages = "334--345", booktitle = "4th Workshop on Algorithms and Data Structures", year = 1995, publisher = "Springer" } @Book{Knu98, author = "D. E. Knuth", title = "The Art of Computer Programming---Sorting and Searching", publisher = "Addison Wesley", year = 1998, edition = "2nd", volume = 3, annote = "leftist trees, looser trees" } @Article{Brengel00, author = {Klaus Brengel and Andreas Crauser and Paolo Ferragina and Ulrich Meyer}, title = {An Experimental Study of Priority Queues in External Memory}, journal = {ACM Journal of Experimental Algorithms}, year = {2000}, volume = {5}, number = {17}, } @InProceedings{FJKT97, author = "R. Fadel and K. V. Jakobsen and J. Katajainen and J. Teuhola", title = "External heaps combined with effective buffering", volume = "19-2", series = "Australian Computer Science Communications", pages = "72--78", booktitle = "4th Australasian Theory Symposium", year = 1997, publisher = "Springer" } @Article{BM72, author = {R. Bayer and E. McCreight}, title = {Organization and maintenance of large ordered indices}, journal = {Acta Informatica}, year = {1972}, pages = {173–189}, } @InProceedings{BDIW02, author = {M. Bander and Z. Duan and J. Iacono and J. Wu}, title = {A locality-preserving cache-oblivious dynamic dictionary}, booktitle = {13th Annual ACM-SIAM Symposium On Descrete Algorithms (SODA'02)}, year = {2002}, } @InProceedings{BDB99, author = {Michael A. Olson and Keith Bostic and Margo Seltzer }, title = "{Berkeley DB}", booktitle = "{USENIX Annual Technical Conference}", pages = {183–192}, year = {1999}, month = {June}, } @InProceedings{KalVar01, author = {M. Kallahalla and P. J. Varman}, title = {Optimal prefetching and caching for parallel {I/O} systems}, booktitle = "13th Symposium on Parallel Algorithms and Architectures", pages = "219--228", year = 2001, } @InProceedings{Raj98, author = "S. Rajasekaran", title = "A Framework for Simple Sorting Algorithms on Parallel Disk Systems", booktitle = "10th {ACM} Symposium on Parallel Algorithms and Architectures", pages = "88--98", year = 1998 } @InProceedings{ChaCor02, author = "G. Chaudhry and T. H. Cormen", title = "Getting More From Out-of-Core Columnsort", booktitle = "4th Workshop on Algorithm Engineering and Experiments (ALENEX)", pages = "143--154", year = 2002, number = 2409, series = "LNCS" } @InProceedings{ChaCorWis01, author = "G. Chaudhry and T. H. Cormen and L. F. Wisniewski", title = "Columnsort Lives! An Efficient Out-of-Core Sorting Program", booktitle = "13th {ACM} Symposium on Parallel Algorithms and Architectures", year = 2001, pages = "169--178", } @InProceedings{PaiVar92, author = "V. S. Pai and P. J. Varman", title = "Prefetching with Multiple Disks for External Mergesort: Simulation and Analysis", booktitle = "ICDE", pages = "273--282", year = 1992 } @Article{CaoFelKarLi96, author = "P. Cao and E. W. Felten and A. R. Karlin and K. Li", title = "Implementation and Performance of Integrated Application-Controlled File Caching, Prefetching and Disk Scheduling", journal = "ACM Transactions on Computer Systems", year = 1996, month = Nov, volume = "14", number = 4, pages = "311--343" } @InProceedings{AlbGarLeo98, author = "S. Albers and N. Garg and S. Leonardi", title = "Minimizing Stall Time in Single and Parallel Disk Systems", pages = "454--462", ISBN = "0-89791-962-9", booktitle = "Proceedings of the 30th Annual {ACM} Symposium on Theory of Computing ({STOC}'98)", month = may # "~23--26", publisher = "ACM Press", address = "New York", year = "1998", } @Article{KimKar00, author = "Tracy Kimbrel and Anna R. Karlin", title = "Near-optimal Parallel Prefetching and Caching", journal = "SIAM Journal on Computing", year = "2000", volume = "29", number = 4, pages = "1051--1082" } @InProceedings{NBCGL94, author = "C. Nyberg and T. Barclay and Z. Cvetanovic and J. Gray and D. Lomet", title = "{AlphaSort}: A {RISC} Machine Sort", booktitle = "SIGMOD", pages = "233--242", year = 1994, } @InProceedings{Aga96, author = "R. Agarwal", title = "A super scalar sort algorithm for {RISC} processors", booktitle = "{ACM SIGMOD} International Conference on Management of Data", pages = "240--246", year = 1996, annote = "msd radix sort mentions TLB and branches" } @Misc{NKG00, author = "C. Nyberg and C. Koester and J. Gray", title = "Nsort: A Parallel Sorting Program for {NUMA} and {SMP} Machines", year = 2000, note = "\url{http://www.ordinal.com/lit.html}" } @Misc{Wyl99, author = "J. Wyllie", title = "{SPsort}: How to Sort a Terabyte Quickly", year = 1999, howpublished = {\url{http://research.microsoft.com/barc/SortBenchmark/SPsort.pdf}} } @Article{AggVit88, author = "A. Aggarwal and J. S. Vitter", title = "The Input/Output Complexity of Sorting and Related Problems", journal = "Communications of the ACM", year = 1988, volume = 31, number = 9, pages = "1116--1127", } @MastersThesis{JensThesis, author = {Jens Mehnert}, title = "{External Memory Suffix Array Construction}", school = {University of Saarland, Germany}, year = {2004}, month = {November}, note = "\url{http://algo2.iti.uka.de/dementiev/esuffix/docu/data/diplom.pdf}", } @Book{SKS01, author = "A. Silberschatz and H. F. Korth and S. Sudarshan", title = "Database System Concepts", publisher = "McGraw-Hill", year = 2001, edition = "4th" } @InBook{BillingLarge, author = {Andrew Hume}, title = {Handbook of massive data sets}, chapter = {Billing in the large}, publisher = {Kluwer Academic Publishers}, year = {2002}, pages = {895--909} } @article{Farias2001, title = {Out-of-core rendering of large, unstructured grids}, volume = {21}, issn = {0272-1716}, doi = {10.1109/38.933523}, number = {4}, author = {Farias, R. and Silva, {C.T.}}, year = {2001}, pages = {42--50}, } @Misc{Moore2000, author = {R. W. Moore}, title = {Enabling Petabyte Computing}, howpublished = {\url{http://www.nap.edu/html/whitepapers/ch-48.html}}, year = {2000} } @TechReport{Neu45, author = "Neumann, J. von", title = "First Draft of a Report on the {EDVAC}", institution = "University of Pennsylvania", year = "1945", key = "computers-history", note = "{\url{http://www.histech.rwth-aachen.de/www/quellen/vnedvac.pdf}}", annote = "{\it ~\\ The report that got von Neumann's name associated with the serial, stored-program, general purpose, digital architecture upon which 99.99\% of all computers today are based.}", } @article{Donato2006, title = {Algorithms and Experiments for the Webgraph.}, volume = {10}, number = {2}, author = {Donato, Debora and Laura, Luigi and Leonardi, Stefano and Meyer, Ulrich and Millozzi, Stefano and Sibeyn, Jop F.}, year = {2006}, pages = {219–-236}, }, @article{Patterson2004, title = {Latency lags bandwith}, volume = {47}, number = {10}, author = {Patterson, David A.}, year = {2004}, pages = {71–-75}, } @InProceedings{FLPR99, author = "M. Frigo and C. E. Leiserson and H. Prokop and S. Ramachandran", title = "Cache-Oblivious Algorithms", booktitle = "40th Symposium on Foundations of Computer Science", year = 1999, pages = "285--298" } @inproceedings{ABDHBM02, address = {New York, {NY}, {USA}}, series = {{STOC} '02}, title = {Cache-oblivious priority queue and graph algorithm applications}, isbn = {1-58113-495-9}, doi = {10.1145/509907.509950}, booktitle = {Proceedings of the thiry-fourth annual {ACM} symposium on Theory of computing}, publisher = {{ACM}}, author = {Arge, Lars and Bender, Michael A. and Demaine, Erik D. and Holland-Minkley, Bryan and Munro, J. Ian}, year = {2002}, pages = {268–276}, } @incollection{BFMZ04, series = {Lecture Notes in Computer Science}, title = {Cache-Oblivious Data Structures and Algorithms for Undirected Breadth-First Search and Shortest Paths}, copyright = {2004 Springer-Verlag Berlin Heidelberg}, isbn = {978-3-540-22339-9, 978-3-540-27810-8}, number = {3111}, booktitle = {Algorithm Theory - {SWAT} 2004}, publisher = {Springer Berlin Heidelberg}, author = {Brodal, Gerth Stølting and Fagerberg, Rolf and Meyer, Ulrich and Zeh, Norbert}, month = jan, year = {2004}, pages = {480--492}, } @MastersThesis{ChristianiThesis, title = {Cache-oblivious Graph Algorithms}, year = 2005, author = {Christiani, Frederik Juul}, journal = {Master's thesis, Department of Mathematics and Computer Science (IMADA), University of Southern Denmark, Odense} } @inproceedings{Ajwani2007, title = {Improved external memory {BFS} implementation}, urldate = {2013-02-14}, booktitle = {Proceedings of the Workshop on Algorithm Engineering and Experiments}, author = {Ajwani, Deepak and Meyer, Ulrich and Osipov, Vitaly}, year = {2007}, pages = {3–12}, file = {alx07_001ajwanid.pdf:files/3066/alx07_001ajwanid.pdf:application/pdf} } @InProceedings{BFV04, author = "G. S. Brodal and R. Fagerberg and K. Vinther", title = "Engineering a Cache-Oblivious Sorting Algorithm", booktitle = "6th Workshop on Algorithm Engineering and Experiments", year = 2004 } @Article{Vit01, author = "J. S. Vitter", title = "External memory algorithms and data structures: {D}ealing with {MASSIVE} data", journal = "ACM Computing Surveys", volume = "33", number = "2", pages = "209--271", year = "2001", } @InProceedings{TPIEscientific96, author = {D. E. Vengroff and J. S. Vitter}, title = "{I/O-Efficient Scientific Computation using TPIE}", booktitle = {Goddard Conference on Mass Storage Systems and Technologies}, pages = {553--570}, year = {1996}, volume = {2}, note = {published in NASA Conference Publication 3340}, } @PhdThesis{ChiangPHD, author = {Y.-J. Chiang}, title = {Dynamic and I/O-Efficient Algorithms for Computational Geometry and Graph Algorithms}, school = {Brown University}, year = {1995} } @InProceedings{Bkdtree03, author = {O. Procopiuc and P. K. Agarwal and L. Arge and J. S. Vitter}, title = "{Bkd-tree: A Dynamic Scalable KD-Tree}", booktitle = {Proc. 8th Int'l Symposium on Spatial and Temporal Databases (SSTD '03)}, pages = {46-65} } @InProceedings{DynRTrees99, author = {L. Arge and K. H. Hinrichs and J. Vahrenhold and J. S. Vitter}, title = "{Efficient Bulk Operations on Dynamic R-trees}", booktitle = {1st Workshop on Algorithm Engineering and Experimentation (ALENEX '99)}, pages = {328-348}, year = {1999}, series = {Lecture Notes in Computer Science}, publisher = {Springer-Verlag}, } @InProceedings{CRBtree03, author = {P. K. Agarwal and L. Arge and S. Govindarajan}, title = "{CRB-Tree: An Efficient Indexing Scheme for Range Aggregate Queries}", booktitle = {Proc. 9th Int'l Conference on Database Theory (ICDT '03)}, pages = {143-157}, year = {2003}, } @Article{CraFer02, author = "A. Crauser and P. Ferragina", title = "A Theoretical and Experimental Study on the Construction of Suffix Arrays in External Memory", journal = "Algorithmica", year = 2002, volume = 32, pages = "1-35", number = 1 } @techreport{stepanov94standard, author = "A. A. Stepanov and M. Lee", title = "{The Standard Template Library}", number = "X3J16/94-0095, WG21/N0482", year = "1994", institution = "Silicon Graphics Inc., Hewlett Packard Laboratories" } @book{karlsson2005beyond, title = {Beyond the C++ standard library: an introduction to Boost}, shorttitle = {Beyond the C++ standard library}, publisher = {Pearson Education}, author = {Karlsson, Björn}, year = {2005} } @article{fabri1998design, title = {On the design of {CGAL}, the computational geometry algorithms library}, author = {Fabri, Andreas and Giezeman, Geert-Jan and Kettner, Lutz and Schirra, Stefan and Schönherr, Sven}, year = {1998}, } @InProceedings{Vengroff94, author = {D. E. Vengroff}, title = "{A Transparent Parallel I/O Environment}", booktitle = {Third DAGS Symposium on Parallel Computation}, pages = {117-134}, year = {1994}, address = {Hanover, NH}, month = {July}, } stxxl-1.4.1/doc/tutorial_deque.dox000644 001411 000144 00000007505 12405153572 017017 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_deque.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_deque STXXL Deque This page introduces into the stxxl::deque container (to learn more about the structure of stxxl::deque, see section \ref design_deque). The acronym Deque stands for "double-ended-queue", that means elements can be accessed, inserted and deleted on both ends of the data structure - in contrast to the stxxl::queue (see \ref tutorial_queue), where that's only possible on one of the two endings. ### Creating a STXXL deque To create a stxxl::deque object, only the data value type must be specified: \code typedef stxxl::deque deque; deque my_deque; \endcode See \ref stxxl::deque for additional template parameter details. ### Insert elements Inserting elements is possible at the start (by calling the push_front() function) as well as the end (by calling the push_back() function) of the deque. \code my_deque.push_front(2); my_deque.push_front(11); my_deque.push_back(5); my_deque.push_back(8); // deque now stores: |11|2|5|8| \endcode ### Access elements To return a reference to the element at the start of the deque, call front(), to return a reference to the elemtent at the end of the deque, call back() on a deque instance. \code std::cout << "return 'first' element: " << my_deque.front() << std::endl; // prints 11 std::cout << "return 'last' element: " << my_deque.back() << std::endl; // prints 8 \endcode Accessing elements at random is supported by the STXXL deque with the []-operator like the following. \code std::cout << "random access: " << my_deque[2] << std::endl; // prints 5 \endcode The operations described in this sections are not I/O-efficient as they come with \f$\mathcal{O}(1)\f$ time per I/O-access. To achieve I/O-efficient scanning, the STXXL deque provides different iterators. The simplest iterator can be used as follows: \code // create forward-iterator (which advances from start to end) stxxl::deque_iterator deque_iterator = my_deque.begin(); // access item at current iterator position std::cout << *deque_iterator << std::endl; // move up one step by preincrement ++deque_iterator; \endcode ### Delete elements Deleting elements is possible at both endings of the deque by using pop_front() and pop_back(): \code my_deque.pop_front(); // deque now stores: |2|5|8| my_deque.pop_back(); // deque now stores: |2|5| \endcode ### Determine size / Check whether the deque is empty To determine the size (i.e. the number of elements) of an instance, call size(): \code std::cout << "deque stores: " << my_deque.size() << " elements" << std::endl; \endcode To check if the deque is empty, call empty() which returns true if the deque is empty: \code std::cout << "empty deque? " << my_deque.empty() << std::endl; \endcode ### A minimal example on STXXL's deque (See \ref examples/containers/deque1.cpp for the sourcecode of the following example). \snippet examples/containers/deque1.cpp example See \ref examples/containers/deque2.cpp for the sourcecode of a more comprehensive example. \example examples/containers/deque1.cpp This example code is explained in the \ref tutorial_deque section. \example examples/containers/deque2.cpp This example code is explained in the \ref tutorial_deque section. */ } // namespace stxxl stxxl-1.4.1/doc/tutorial_map.dox000644 001411 000144 00000014250 12405153572 016464 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_map.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_map STXXL Map (B+-tree) This page introduces into the stxxl::map (for further information on the structure you may have a look at \ref design_map). stxxl::map is an external associative container that stores elements formed by a combination of a unique key value and a data value, following a specific order. The map's key values are generally used to sort and uniquely identify the data values, while the data values store the content associated to this key. ### Creating a STXXL Map To create a stxxl::map object, several template parameters are required. The first two parameters KeyType and DataType which is an std::pair in this example are self-explanatory, the third parameter has to be a comparator class which is used to determine whether a key is smaller than another one, the fourth and fifth parameter define the node- and leaf block size. \code #define DATA_NODE_BLOCK_SIZE (4096) #define DATA_LEAF_BLOCK_SIZE (4096) ... // template parameter typedef stxxl::map map_type; // constructor map(node_cache_size_in_bytes, leaf_cache_size_in_bytes) to create map object named my_map map_type my_map((map_type::node_block_type::raw_size) * 3, (map_type::leaf_block_type::raw_size) * 3); \endcode The comparator class has to be defined by hand (and before the map definition above) and looks like: \code struct ComparatorGreater { bool operator () (const int & a, const int & b) const { return a > b; } static int max_value() { return std::numeric_limits::min(); } }; \endcode If CompareGreater()(a,b) is true, then a is smaller than b. CompareType must also provide a static max_value method, that returns a value of type KeyType that is larger than any key stored in map, i.e. for all x in map holds CompareType()(x,CompareType::max_value()) Naturally, we can define a comparator class which returns true if b is smaller than a as follows: \code struct CompareLess { bool operator () (const int & a, const int & b) const { return a::max(); } }; \endcode Note that CompareType must define a strict weak ordering. ### Insert elements Insertion of elements is possible in three different ways: 1. simple insertion \code my_map.insert(std::pair(1, 'a')); my_map.insert(std::pair(2, 'b')); my_map.insert(std::pair(3, 'c')); my_map.insert(std::pair(4, 'd')); \endcode 2. insertion with hint \code map_type::iterator iter = my_map.begin(); my_map.insert(iter, std::pair(5, 'w')); my_map.insert(iter, std::pair(6, 'x')); my_map.insert(iter, std::pair(7, 'y')); my_map.insert(iter, std::pair(8, 'z')); \endcode 3. range insertion \code map_type anothermap((map_type::node_block_type::raw_size) * 3, (map_type::leaf_block_type::raw_size) * 3); anothermap.insert(my_map.begin(),my_map.find('c')); // stores (1, 'a'), (2, 'b'), (3, 'c') \endcode ### Access elements Random access is possible by using the []-operator: \code std::cout << "my_map[4] is " << my_map[4] << std::endl; // prints 'd' \endcode Scanning a stxxl::map by an iterator works like \code // echo every element my_map contains for (iter = my_map.begin(); iter != my_map.end(); ++iter) { std::cout << iter->first << " => " << iter->second << std::endl; } \endcode Hint: To enable leaf prefetching during scanning, call my_map.enable_prefetching() before. In addition, the operations lower_bound() and upper_bound() are available. The function lower_bound(key) returns an iterator which initially points to the first element in the container whose key is not considered to go before key. upper_bound(key) works similar as it returns an iterator which initially points to the first element in the container whose key is considered to go after key. \code map_type::iterator iter_low, iter_up; iter_low = my_map.lower_bound(2); // iter_low points to 2 in this case iter_up = my_map.upper_bound(6); // iter_up points to 5 in this case std::cout << "lower bound " << iter_low->second << " upper bound " << iter_up->second << std::endl; \endcode Note: lower_bound() works nearly equal to upper_bound(), except in the case that the map contains an element with a key equivalent lower_bound(x): In this case lower_bound(x) returns an iterator pointing to that element, whereas upper_bound(x) returns an iterator pointing to the next element. ### Delete elements Removing elements out of the map is possible in three different ways: 1. Erasing by iterator \code map_type::iter iter = my_map.find(7); my_map.erase(iter); \endcode 2. Erasing by key \code my_map.erase(8); \endcode 3. Erasing by range \code iter = my_map.find(3); my_map.erase(iter, my_map.end()); \endcode ### Determine size / Check whether the map is empty To determine the size (i.e. the number of elements) of an instance, call size(): \code std::cout << "number of elements in my_map: " << my_map.size() << std::endl; \endcode To check if the priority queue is empty, call empty() which returns true in case: \code std::cout << "is my_map empty? " << my_map.empty() << std::endl; \endcode ### A minimal working example on STXXL Map (See \ref examples/containers/map1.cpp for the sourcecode of the following example). \snippet examples/containers/map1.cpp example \example examples/containers/map1.cpp This example code is explained in the \ref tutorial_map section */ } // namespace stxxl stxxl-1.4.1/doc/doxygen-extra.css000644 001411 000144 00000001622 12350112610 016542 0ustar00tbusers000000 000000 /* Customization of doxygen's style for STXXL */ /* space out lines slightly */ body { line-height: 1.20em; } /* increase size of code snippets */ div.fragment { padding: 4px 6px; } div.fragment div.line { line-height: 135%; } /* make heading smaller and quit justifying */ h1 { font-size: 16pt; margin: 12pt 0px 8pt 0px; text-align: left; } h2 { font-size: 15pt; margin: 12pt 0px 8pt 0px; text-align: left; } h3 { font-size: 14pt; margin: 12pt 0px 8pt 0px; text-align: left; } h4 { font-size: 14pt; margin: 10pt 0px 6pt 0px; text-align: left; } /* limit maximum width of text content */ div.contents { max-width: 120ex; text-align: justify; hyphens: auto; moz-hyphens: auto; o-hyphens: auto; -ms-hyphens: auto; -webkit-hyphens: auto; } /* make links more blue */ a { color: #3D4A9C; font-weight: normal; text-decoration: none; } .contents a:visited { color: #3D4A9C; } stxxl-1.4.1/doc/tutorial_vector_billing.dox000644 001411 000144 00000013437 12405153572 020717 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_vector_billing.dox * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_vector_billing A Billing System for Phone Calls (stxxl::vector and stxxl::sort) \author Roman Dementiev (2006) The intended audience of this tutorial are developers or researchers who develop applications or implement algorithms processing large data sets which do not fit into the main memory of a computer. They must have basic knowledge in the theory of external memory computing and have working knowledge of C++ and an experience with programming using STL. Familiarity with key concepts of generic programming and C++ template mechanism is assumed. Let us start with a toy but pretty relevant problem: the phone call billing problem. You are given a sequence of event records. Each record has a time stamp (time when the event had happened), type of event ('call begin' or 'call end'), the callers number, and the destination number. The event sequence is time-ordered. Your task is to generate a bill for each subscriber that includes cost of all her calls. The solution is uncomplicated: sort the records by the callers number. Since the sort brings all records of a subscriber together, we \a scan the sorted result computing and summing up the costs of all calls of a particular subscriber. The phone companies record up to 300 million transactions per day. AT&T billing system Gecko \cite BillingLarge has to process databases with about 60 billion records, occupying 2.6 terabytes. Certainly this volume can not be sorted in the main memory of a single computer (Except may be in the main memory of an expensive supercomputer.) Therefore we need to sort those huge data sets out-of-memory. Now we show how STXXL can be useful here, since it can handle large volumes I/O efficiently. # STL Code If you are familiar with STL your the main function of bill generation program will probably look like this: \code int main(int argc, char * argv[]) { if(argc < 4) // check if all parameters are given { // in the command line print_usage(argv[0]); return 0; } // open file with the event log std::fstream in(argv[1], std::ios::in); // create a vector of log entries to read in std::vector v; // read the input file and push the records // into the vector std::copy(std::istream_iterator(in), std::istream_iterator(), std::back_inserter(v)); // sort records by callers number std::sort(v.begin(), v.end(), SortByCaller()); // open bill file for output std::fstream out(argv[3], std::ios::out); // scan the vector and output bills std::for_each(v.begin(), v.end(), ProduceBill(out)); return 0; } \endcode To complete the code we need to define the log entry data type \c LogEntry, input operator \c >> for \c LogEntry, comparison functor \c SortByCaller, unary functor \c ProduceBills used for computing bills, and the \c print_usage function. \snippet examples/algo/phonebills.cpp prolog # Going Large -- Use STXXL In order to make the program I/O efficient we will replace the STL internal memory data structures and algorithms by their STXXL counterparts. The changes are marked with \c //! \code #include //! include STXXL headers // the rest of the code remains the same int main(int argc, char * argv[]) { if(argc < 4) // check if all parameters are given { // in the command line print_usage(argv[0]); return 0; } // open file with the event log std::fstream in(argv[1], std::ios::in); // create a vector of log entries to read in stxxl::vector v; //! use stxxl::vector instead of std::vector // read the input file and push the records // into the vector std::copy(std::istream_iterator(in), std::istream_iterator(), std::back_inserter(v)); // bound the main memory consumption by M // during sorting const unsigned M = atol(argv[2])*1024*1024; //! calculated memory limit M // sort records by callers number stxxl::sort(v.begin(), v.end(), SortByCaller(), M); //! use stxxl::sort instead of std::sort // open bill file for output std::fstream out(argv[3], std::ios::out); // scan the vector and output bills // the last parameter tells how many buffers // to use for overlapping I/O and computation stxxl::for_each(v.begin(), v.end(), ProduceBill(out), 2); //! use stxxl::for_each instead of std::for_each return 0; } \endcode As you note the changes are minimal. Only the namespaces and some memory specific parameters had to be changed. See \ref examples/algo/phonebills.cpp for the full source code. The example program is automatically compiled when building STXXL, refer to \ref install on how to build programs with STXXL. The program \ref examples/algo/phonebills_genlog.cpp can be used to generate logs for processing with the phonebills example. Do not forget to configure you external memory space in file .stxxl. See \ref config. \example examples/algo/phonebills.cpp This example code is explain in \ref tutorial_vector_billing \example examples/algo/phonebills_genlog.cpp This example code is explain in \ref tutorial_vector_billing */ } // namespace stxxl stxxl-1.4.1/doc/mainpage.dox000644 001411 000144 00000012526 12411366426 015552 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/mainpage.dox * * Main page of STXXL doxygen tree. All doc pages should be linked here. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /** \mainpage Welcome to STXXL The core of STXXL is an implementation of the C++ standard template library STL for external memory (out-of-core) computations, i.e., STXXL implements containers and algorithms that can process huge volumes of data that only fit on disks. While the compatibility to the STL supports ease of use and compatibility with existing applications, another design priority is high performance. Here is a selection of STXXL performance features: - transparent support of multiple disks - variable block lengths - overlapping of I/O and computation - prevention of OS file buffering overhead - algorithm pipelining - utilization of multiple processor cores for internal computation See the \subpage introduction "introduction to external memory" for a longer description and our vision. # Getting Started: Building and Tutorial This section will help you if you are using the STXXL for the first time. First you must compile the library. Pick one of the following \subpage install "build instructions", depending on your host system: - \ref install_unix - \ref install_windows Once compiled, you can read the following simple tutorials on how to use STXXL containers and algorithms: - \ref tutorial_vector - \ref tutorial_stack - \ref tutorial_pqueue See the corresponding page for a \subpage tutorial "complete list of all Tutorials and Examples". # Design and More Information We have collected much documentation about the design of STXXL. Even more information is available as academic research papers, technical reports and theses. - \subpage design "Design of STXXL concepts, containers and algorithms" If you plan to contribute code to STXXL, please read the \subpage coding_style and use the \subpage common. # FAQ, Troubleshooting, Bugs and More - \subpage faq - Questions concerning use and development of the STXXL library should be posted to the FORUMS. Please search the forum before posting, your question may have been answered before. - See \ref faq_compilers when compilation fails. - Bugs and pull requests can be reported via Github: http://github.com/stxxl/stxxl. - Check the \ref changelog for recent changes when switching version. - The STXXL source also contains \subpage stxxl_tool "stxxl_tool", a collection of simple tools and benchmarks. # License and Authors \c STXXL is distributed under the Boost Software License, Version 1.0. \n You can find a copy of the license in the accompanying file \ref license or at http://www.boost.org/LICENSE_1_0.txt. \n Many people have contributed to STXXL, see all \ref authors. \subpage textfiles " " */ /** \page textfiles Additional Text Files - \subpage readme - \subpage changelog - \subpage authors - \subpage textfiles_install - \subpage license - \subpage textfiles_todo \page readme README \verbinclude README \page changelog ChangeLog \verbinclude CHANGELOG \page authors AUTHORS The following list of authors have contributed to STXXL: \verbinclude AUTHORS \page textfiles_install INSTALL \verbinclude INSTALL \page license LICENSE_1_0.txt \verbinclude LICENSE_1_0.txt \page textfiles_todo TODO \verbinclude TODO */ // Module Groups are defined here to fix their order: /*! \defgroup stllayer STL-User Layer Layer which groups STL compatible algorithms and containers */ /*! \defgroup streampack Stream Package Package that enables pipelining of consequent sorts and scans of the external data avoiding the saving the intermediate results on the disk, e.g. the output of a sort can be directly fed into a scan procedure without the need to save it on a disk. All components of the package are contained in the \c stxxl::stream namespace. STREAM ALGORITHM CONCEPT (Do not confuse with C++ input/output streams) \verbatim struct stream_algorithm // stream, pipe, whatever { typedef some_type value_type; const value_type & operator * () const; // return current element of the stream stream_algorithm & operator ++ (); // go to next element. precondition: empty() == false bool empty() const; // return true if end of stream is reached }; \endverbatim */ /*! \defgroup mnglayer Block Management Layer Group of classes which help controlling external memory space, managing disks, and allocating and deallocating blocks of external storage. */ /*! \defgroup iolayer I/O Primitives Layer Group of classes which enable abstraction from operating system calls and support system-independent interfaces for asynchronous I/O. */ /*! \defgroup support Common Utilities and Support Classes Supporting classes also useful for applications, see also \ref common . */ stxxl-1.4.1/doc/stxxl_tool.dox000644 001411 000144 00000012607 12405153572 016207 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/stxxl_tool.dox * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page stxxl_tool stxxl_tool Collection \authors Timo Bingmann (2013) The \c stxxl_tool accompanies the source code and also binary distributions of STXXL. It contains multiple subprograms that can be used for benchmarking basic functionality of the library. We describe only some of the benchmarks and functions in this document. \c stxxl_tool has a verbose command line help and descriptions of all subtools are available there. Try \verbatim $ stxxl_tool (prints all available subtools) $ stxxl_tool --help \endverbatim \section create_files To pre-create files one can issue a command similar to: \verbatim $ stxxl_tool create_files 10gib /data01/stxxl.tmp \endverbatim \section benchmark_disks Benchmark Disk Bandwidth Maybe the most important tool for testing the I/O performance of an experimental platform is stxxl_tool benchmark_disks. This subtool will read the disk configuration file (.stxxl) and open all parallel disk files. Then the specified amount of data is written and read on the configured disks using the block manager and file I/O layers. The transfer speed is outputted for every batch for processed blocks. An example run looks as follows: \verbatim $ stxxl_tool benchmark_disks 10gib -b 4 Parameter size set to 10737418240. Option -b, --batch set to 4. [STXXL-MSG] STXXL v1.4.0 (prerelease) [STXXL-MSG] Disk '/data01/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall [STXXL-MSG] Disk '/data02/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall [STXXL-MSG] In total 2 disks are allocated, space: 457763 MiB # Batch size: 33554432 (32.000 MiB) (4 blocks of 8388608 (8.000 MiB) ) using randomized cycling striping Offset 0 MiB: 170.6 MiB/s write, 108.9 MiB/s read Offset 32 MiB: 164.6 MiB/s write, 108.4 MiB/s read Offset 64 MiB: 158.6 MiB/s write, 102.0 MiB/s read Offset 96 MiB: 143.8 MiB/s write, 116.3 MiB/s read Offset 128 MiB: 156.3 MiB/s write, 130.7 MiB/s read Offset 160 MiB: 168.3 MiB/s write, 99.3 MiB/s read Offset 192 MiB: 156.6 MiB/s write, 109.2 MiB/s read Offset 224 MiB: 159.9 MiB/s write, 107.7 MiB/s read Offset 256 MiB: 156.0 MiB/s write, 88.7 MiB/s read Offset 288 MiB: 149.6 MiB/s write, 109.7 MiB/s read Offset 320 MiB: 138.3 MiB/s write, 113.2 MiB/s read [...] \endverbatim \section benchmark_sort Benchmark Sorting Methods The second performance metric of an experimental platform is how fast STXXL can sort on it. This is measured by the stxxl_tool benchmark_sort subtool. This subtool performs stxxl::sort, stxxl::ksort and stream::sort on uint32 pairs, uint64 pairs and a larger struct of 64 bytes. An example run looks as follows: \verbatim $ stxxl_tool benchmark_sort 20gib -M 1gib Parameter size set to 21474836480. Option -M, --ram set to 1073741824. [STXXL-MSG] STXXL v1.4.0 (prerelease) + gnu parallel(20120301) [STXXL-MSG] Disk '/dev/sdb1' is allocated, space: 915527 MiB, I/O implementation: syscall [STXXL-MSG] Disk '/dev/sdc1' is allocated, space: 915527 MiB, I/O implementation: syscall [STXXL-MSG] Disk '/dev/sdd1' is allocated, space: 915527 MiB, I/O implementation: syscall [STXXL-MSG] Disk '/dev/sde1' is allocated, space: 915527 MiB, I/O implementation: syscall [STXXL-MSG] In total 4 disks are allocated, space: 3662109 MiB #!!! running sorting test with pair of uint32 = 8 bytes. # materialize random_stream into vector of size 2684354560 finished in 46.9271 seconds @ 436.421 MiB/s # stxxl::sort vector of size 2684354560 finished in 256.048 seconds @ 79.9849 MiB/s # stxxl::ksort vector of size 2684354560 finished in 356.964 seconds @ 57.3727 MiB/s # stxxl::stream::sort of size 2684354560 finished in 213.102 seconds @ 96.1041 MiB/s #!!! running sorting test with pair of uint64 = 16 bytes. # materialize random_stream into vector of size 1342177280 finished in 95.5571 seconds @ 214.322 MiB/s # stxxl::sort vector of size 1342177280 finished in 222.727 seconds @ 91.9513 MiB/s # stxxl::ksort vector of size 1342177280 finished in 265.456 seconds @ 77.1502 MiB/s # stxxl::stream::sort of size 1342177280 finished in 200.357 seconds @ 102.218 MiB/s #!!! running sorting test with struct of 64 bytes = 64 bytes. # materialize random_stream into vector of size 335544320 finished in 46.9293 seconds @ 436.401 MiB/s # stxxl::sort vector of size 335544320 finished in 215.798 seconds @ 94.9035 MiB/s # stxxl::ksort vector of size 335544320 finished in 222.5 seconds @ 92.0451 MiB/s # stxxl::stream::sort of size 335544320 finished in 112.607 seconds @ 181.871 MiB/s \endverbatim As stxxl::sort and stxxl::ksort perform about 4 read/write steps on the data, the sorting speed is about 1/4 of the scanning speed. On the other hand, stream::sort performs only 2 read/write steps to create a sorted stream from an unsorted one. Thus the stream sorting speed is about 1/2 of scanning speed. */ } // namespace stxxl stxxl-1.4.1/doc/faq.dox000644 001411 000144 00000017103 12424126410 014523 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/faq.dox * * Frequently asked and answered questions * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /** \page faq FAQ - Frequently Asked Questions \section faq_latest Latest version of this FAQ The most recent version of this FAQ can always be found here. \section faq_compilers Supported Compilers and Platforms The following compilers have been tested in different \c STXXL configurations. Other compilers might work, too, but we don't have the resources (systems, compilers or time) to test them. Feedback is welcome. Please note that from STXXL 1.4.0 on, only 64-bit systems are fully supported. Compilation on 32-bit seems to work, but we cannot support it anymore. The compilers marked with '*' are the maintainers' favorite choices and are most thoroughly tested. compiler | supported options ----------------------- | -------------------------- gcc 4.9.1 | stxxl parallel (boost) (c++11) gcc 4.8.3 * | stxxl parallel (boost) (c++11) gcc 4.7.3 | stxxl parallel (boost) (c++0x) gcc 4.6.4 | stxxl parallel (boost) (c++0x) gcc 4.5.4 | stxxl parallel (boost) (c++0x) gcc 4.4.7 | stxxl parallel (boost) (c++0x) gcc 4.3.6 | stxxl (boost) gcc 4.1.2 | stxxl (boost) gcc 3.4.6 | stxxl (boost) gcc 3.3 | unsupported icpc 2015.0.090 * | stxxl (boost) (c++0x) icpc 2013.5.192 * | stxxl (boost) (c++0x) icpc 2011.13.367 | stxxl (boost) (c++0x) clang++ 3.2, 3.3, 3.4.2 | stxxl (boost) (c++0x) mingw-w64 gcc 4.8.3 | stxxl parallel (boost) (c++11) cygwin gcc 4.8.3 | stxxl parallel (boost) (c++11) msvc 2013 12.0 * | stxxl (boost) (c++11) msvc 2012 11.0 | stxxl (boost) (c++0x) msvc 2010 10.0 | stxxl boost required - The option "parallel" uses the __gnu_parallel extensions in some parts of STXXL. For all \c gcc versions >= 4.4 the __gnu_parallel extensions are ON by default. Support for MCSTL (predecessor of __gnu_parallel) was removed in STXXL 1.4.0. - Boost is optional and not recommended on all systems, except MSVC 2010. It provides no advantages on other platforms. \n STXXL has been tested with Boost 1.40.0, 1.42.0 and 1.46.1. Other versions may work, too, but older versions will not get support. - Support for C++0x and C++11 is integrated and automatically detected. No core parts of STXXL require C++11. - All options are automatically detected by CMake. \section faq_credit How can I credit STXXL, and thus foster its development? - For all users: - Sign up at Ohloh and add yourself as an STXXL user / rate STXXL: http://www.ohloh.net/p/stxxl - Rate STXXL at heise Software-Verzeichnis (German): http://www.heise.de/software/download/stxxl/76072 - Rate STXXL at SourceForge: https://sourceforge.net/projects/stxxl/ - For scientific work: Cite the papers mentioned here: http://stxxl.sourceforge.net/ - For industrial users: Tell us the name of your company, so we can use it as a reference. \section faq_nonPODs References to Elements in External Memory Data Structures You should not pass or store references to elements in an external memory data structure. When the reference is used, the block that contains the element may be no longer in internal memory.
    Use/pass an iterator (reference) instead.
    For an \c stxxl::vector with \c n pages and LRU replacement strategy, it can be guaranteed that the last \c n references obtained using \c stxxl::vector::operator[] or dereferencing an iterator are valid. However, if \c n is 1, even a single innocent-looking line like \verbatim std::cout << v[0] << " " << v[1000000] << std::endl; \endverbatim can lead to inconsistent results. \section faq_templateparam Parameterizing STXXL Containers STXXL container types like stxxl::vector can be parameterized only with a value type that is a POD (i. e. no virtual functions, no user-defined copy assignment/destructor, etc.) and does not contain references (including pointers) to internal memory. Usually, "complex" data types do not satisfy this requirements. This is why stxxl::vector > and stxxl::vector > are invalid. If appropriate, use std::vector >, or emulate a two-dimensional array by doing index calculation. \section faq_threadsafe Thread-Safety The I/O and block management layers are thread-safe (since release 1.1.1). The user layer data structures are not thread-safe.
    I.e. you may access different \c STXXL data structures from concurrent threads without problems, but you should not share a data structure between threads (without implementing proper locking yourself).
    This is a design choice, having the data structures thread-safe would mean a significant performance loss. \section faq_diskalloc Disk Allocation on Multiple Disks Q: I have configured several disks to use with STXXL. Why does STXXL fail complaining about the lack of space? According to my calclulations, the space on the disks should be sufficient. A: This may happen if the disks have different size. With the default parameters \c STXXL containers use randomized block-to-disk allocation strategies that distribute data evenly between the disks but ignore the availability of free space on them. Thus when the smallest disk is full, the program will abort because it cannot grow the file on that disk. A2: This round-robin disk allocation is due to the history of STXXL's support for parallel disk algorithms. It would be great if someone would contribute a patch for this issue. This would require adapting stxxl::disk_allocator and stxxl::block_manager to skip full disks when allocating new blocks. \section faq_msclr STXXL in a Microsoft CLR Library From STXXL user Christian, posted in the forum: Precondition: I use STXXL in a Microsoft CLR Library (a special DLL). That means that managed code and native code (e.g. STXXL) have to co-exist in your library. Symptom: Application crashes at process exit, when the DLL is unloaded. Cause: STXXL's singleton classes use the \c atexit() function to destruct themselves at process exit. The exit handling will cause the process to crash at exit (still unclear if it's a bug or a feature of the MS runtime). Solution: 1.) Compiled STXXL static library with \c STXXL_NON_DEFAULT_EXIT_HANDLER defined. 2.) For cleanup, \c stxxl::run_exit_handlers() has now to be called manually. To get this done automatically: Defined a CLI singleton class "Controller": \verbatim public ref class Controller { private: static Controller^ instance = gcnew Controller; Controller(); }; \endverbatim Registered my own cleanup function in Controller's constructor which will manage to call \c stxxl::run_exit_handlers(): \verbatim #pragma managed(push, off) static int myexitfn() { stxxl::run_exit_handlers(); return 0; } #pragma managed(pop) Controller::Controller() { onexit(myexitfn); } \endverbatim */ stxxl-1.4.1/doc/DoxygenLayout.xml000644 001411 000144 00000013252 12350112610 016571 0ustar00tbusers000000 000000 stxxl-1.4.1/doc/install.dox000644 001411 000144 00000071727 12405153572 015446 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/install.dox * * Installation and Linking Instruction for STXXL using CMake * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /** \page install Compilation and Configuration \subpage install_unix \subpage install_windows \subpage install_windows_boost \subpage install_build_options \subpage install_config */ /** \page install_unix Compiling and Installing STXXL on Linux/Unix Variants \author Timo Bingmann (2013) Precondition: Make sure GNU \c make, \c CMake >= 2.6.4, \c git and a C++ compiler are installed. See \ref faq_compilers which systems and compilers are currently supported. There are three methods to use STXXL: - The first is recommended for small prototype projects consisting of only a few source files. It uses the \c local/ directory within the STXXL directory tree. - For larger development projects, the STXXL library can be linked as a library inside a subdirectory. - For distributing independent sources, the STXXL library can be localed using CMake's \c find_package() mechanism. There are quite some \ref install_build_options, which you can use when building STXXL with CMake. \section install_unix_local First-Time Compile and Simple Projects in local/ 1. Clone the STXXL project repository as \b my-project. \verbatim $ git clone http://github.com/stxxl/stxxl.git my-project \endverbatim 2. Compile the STXXL library in a \c build subdirectory, including the example in \c local/. \verbatim $ mkdir my-project/build $ cd my-project/build $ cmake .. $ make \endverbatim 3. Run the example program \c test1 in \c local/ \verbatim (inside my-project/build/) $ cd local $ ./test1 \endverbatim For your own prototype project you can immediately start modifying \c test1.cpp or create a new .cpp in \c local/. The CMake scripts will automatically compile and link all .cpp files in \c local/ correctly with STXXL. The CMake file has many build options (see \ref install_build_options). Maybe the most important are \c BUILD_TESTS and \c BUILD_EXAMPLES. By setting them with "-DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON" on the CMake line, additional subprojects are added to the build. By default, STXXL compiles in Debug mode and includes many assertions and run-time checks, which typically slow down performance dramatically. To use STXXL at **full speed**, please set the build type to Release by adding -DCMAKE_BUILD_TYPE=Release to the cmake line. \section install_unix_subproject Including STXXL as a CMake Subproject The second method is for including STXXL in a larger program as a subproject. This is particularly easy with CMake: one can just \c add_directory(stxxl) in a CMakeLists.txt. The following guide shows how to start a simple CMake project and use STXXL in a subdirectory. The advantage of this method is that the STXXL is a subproject of your's. Thereby it will always be compiled with the same set of CFLAGS or CMAKE_BUILD_TYPE as your project. This is most convenient for developing projects, as the STXXL has a lot of debug code. When running experiments or going into production, the whole project must be built with \c CMAKE_BUILD_TYPE=Release or similar to remove the debug code. 1. Create an empty directory \c "my-project" and clone the STXXL inside it. \verbatim $ mkdir my-project $ cd my-project $ git clone http://github.com/stxxl/stxxl.git \endverbatim 2. Create a file named \c CMakeLists.txt inside your \c my-project folder with the following sample content: \verbatim # CMakeLists.txt example for STXXL project(my-project) cmake_minimum_required(VERSION 2.8) # disallow in-source builds if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") message(SEND_ERROR "In-source builds are not allowed.") endif("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") # enable warnings (always good) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall") # include the STXXL library in my-project add_subdirectory(stxxl) # apply STXXL CXXFLAGS to our configuration set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STXXL_CXX_FLAGS}") # add STXXL includes path include_directories(${STXXL_INCLUDE_DIRS}) # build a program and link it with STXXL. add_executable(project main.cpp) target_link_libraries(project ${STXXL_LIBRARIES}) \endverbatim 3. To show how that this build system works, we now copy the \c test1.cpp from STXXL to the new project as \c main.cpp and build it. \verbatim $ cp stxxl/local/test1.cpp main.cpp $ mkdir build $ cd build $ cmake .. $ make \endverbatim 4. Test the compilation by running \c project \verbatim $ ./project \endverbatim \section install_unix_library Including STXXL as a Library STXXL compiles into a static (and optionally shared) library plus template include files, which may be included in binary distributions. If a binary STXXL package is installed, the following few CMake lines will automatically detect it and configure a program to build with the appropriate CXXFLAGS, include directories and libraries: \verbatim # search for stxxl-config.cmake which contains the library's configuration find_package(STXXL REQUIRED) # print some info (this can be removed) message(STATUS "STXXL_CXX_FLAGS: ${STXXL_CXX_FLAGS}") message(STATUS "STXXL_INCLUDE_DIRS: ${STXXL_INCLUDE_DIRS}") message(STATUS "STXXL_LIBRARIES: ${STXXL_LIBRARIES}") # apply CXXFLAGS to our configuration set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STXXL_CXX_FLAGS}") # add STXXL include directory include_directories(${STXXL_INCLUDE_DIRS}) # create and executable and linke with STXXL add_executable(your_program main.cpp) target_link_libraries(your_program ${STXXL_LIBRARIES}) \endverbatim We have create a simple example project, which contains the lines above and an example program. The source is available via github: \verbatim git clone http://github.com/stxxl/myproject.git \endverbatim See the README at http://github.com/stxxl/myproject # Create a Disk Configuration File For STXXL to function beyond very simple examples, you must define the \link install_config disk configuration file \endlink. The simplest method is to create a file named '.stxxl' the same directory as you execute the program. A basic configuration might be: \verbatim # file path,maximum capacity of the disk,access method disk=/tmp/stxxl,1G,syscall unlink \endverbatim Please see \ref install_config for further available options. # Notes for Linux Distribution Package Maintainers Package maintainers should make sure that \b both Debug and Release static libraries are available via the distribution package system. We currently cannot keep a stable binary interface for the library, thus providing versioned shared libraries is not a good idea. - The CMake build scripts will generate static and exclude shared libraries when run with "-DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF". - Debug libraries are suffixed with "_debug". Due to CMake's system, the library must be compiled once in Debug mode and once in Release mode! (see below) - Running "make install" will correctly install the tree into \c CMAKE_INSTALL_PREFIX. - The binary package should only contain the \c stxxl_tool (since we do not provide shared libraries). Alternatively, the tool can packaged in an additional \c tools package, thus leaving the binary package empty. The \c test1 binary must not be included. - The development package must contain all installed headers, and both "libstxxl.a" and "libstxxl_debug.a". \n The CMake script will also install CMake project files in \c lib/cmake/stxxl , which must be included in the development package. \n We also provide a \c pkg-config file, which installs by default into \c lib/pkgconfig/ as \c stxxl.pc and \c stxxl_debug.pc - Binary Unix packages should not use Boost. A typical build sequence would be \verbatim mkdir debug; cd debug cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_STATIC_LIBS=ON $SRCDIR make -j4 && make install cd .. mkdir release; cd release cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_STATIC_LIBS=ON $SRCDIR make -j4 && make install \endverbatim When building in this order the \b stxxl_tool will be built twice, and the Debug binary will be overwritten with the Release binary. */ /** \page install_build_options Options for Build Configuration STXXL has some optional features and compile switches that can be changed at build time. - Maybe the most important one is switching between \b Debug and \b Release builds. Debug builds are very slow, as STXXL contains many assertions (which are a feature not a bug). With \c cmake the mode is easily defined by using \verbatim $ cmake -DCMAKE_BUILD_TYPE=Debug ... $ cmake -DCMAKE_BUILD_TYPE=Release ... \endverbatim The mode mostly changes CXXFLAGS. - Some parts of STXXL have been parallelized using the __gnu_parallel (aka MCSTL) library. Currently, with CMake one can only use the newer __gnu_parallel library by defining \verbatim $ cmake -DUSE_GNU_PARALLEL=ON ... \endverbatim when building. Parallel is now ON by default for gcc, if it can be detected. The cmake script will check availability of the corresponding header files. - Use Boost for file I/O, multi-threading support and more. Boost is required on Windows for older MSVC versions and is CMake tries to automatically find it. On Linux/Unix it is optional and not recommended, but can be activated using \verbatim $ cmake -DUSE_BOOST=ON ... \endverbatim - STXXL contains many small example programs, which can be built by defining \verbatim $ cmake -DBUILD_EXAMPLES=ON ... \endverbatim - STXXL contains a set of unit tests in \c tests/, which verify most of the libraries functionality. These programs are not built by default, because this makes it easier for STXXL to be used as CMake subproject (\ref install_unix_subproject). The test suite can be built and run using \verbatim $ cmake -DBUILD_TESTS=ON ... $ make test \endverbatim Defining BUILD_TESTS also builds everything in \c examples/. There is also a \c BUILD_EXTRAS configuration flag to build even more, longer running tests. Be advised that the test suite need quite some space on your disks. - CMake can be instructed to use other compilers, by setting, for example \verbatim $ cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ... \endverbatim - CMake can install the library and all public headers into a prefix by running: \verbatim $ cmake -DCMAKE_INSTALL_PREFIX=/some/dir ... $ make install \endverbatim Additionally, the installation subdirectories can be specified using the following options: \verbatim INSTALL_BIN_DIR=bin INSTALL_LIB_DIR=lib INSTALL_INCLUDE_DIR=include INSTALL_PKGCONFIG_DIR=lib/pkgconfig INSTALL_CMAKE_DIR=lib/cmake/stxxl \endverbatim - The CMake script by default only builds a static library. Shared libraries in Linux incur a small overhead for each library call, which should be insignificant for STXXL. However, keeping a stable binary interface for shared libraries is currently not planned. Nevertheless, you can build static and/or shared library via the following switches: \verbatim $ cmake -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON .. \endverbatim - On Unix the pthreads library is used for threading, on Windows the Boost or STL thread library is used. For testing compilation, the STL thread library can optionally also be used on Unix/Linux by setting the \c -DUSE_STD_THREADS=ON switch. - For build testing, the CMake script can check that all header files in \c include/ compile by themselves. This is required since each header must be self-sufficient and include other headers appropriately. These test compiles are trigged with \c -DTRY_COMPILE_HEADERS=ON. - Again for testing, the CMake script can be instructed to run all tests and examples \verbatim # with valgrind by setting $ cmake -DUSE_VALGRIND=ON ... # with gcov for test coverage analysis by setting $ cmake -DUSE_GCOV=ON ... \endverbatim The valgrind option also enables a few additional lines of code that clear memory areas that are intentionally uninitialized. The gcov coverage report can be automatically created using the additional targets \c test-coverage (runs everything) or \c lcov-html to generate the HTML report after running the test suite. */ /** \page install_windows Compiling and Installing STXXL with Visual Studio 2012 and newer (without Boost) \author Timo Bingmann (2013) ## Prerequisites: - Microsoft Visual Studio >= 2012 (VC11). \n For older versions you need Boost, see \ref install_windows_boost. - CMake 2.8, visit http://www.cmake.org/ - (a git client for development on the bleeding edge) ## Unpack STXXL source code - Download the STXXL archive from http://stxxl.sourceforge.net
    or use your favorite git client to clone the development version: http://github.com/stxxl/stxxl.git - Unpack the archive at a convenient location. ## Use CMake to Generate Visual Studio Projects - Open the CMake GUI. - Fill in the source code field with the place of the STXXL source and pick a build directory. - Pressing "Configure" brings up a dialog box: select your Visual Studio version (MSVC 11 here).
    Note that you must select between 32- and 64-bit building here. - Press "Generate" to run the CMake platform checks and to generate the Visual Studio project files. \image html install_windows_cmake.png - After generating the project, CMake presents a list of optional configuration switches. Maybe the most important are \c BUILD_TESTS and \c BUILD_EXAMPLES. By selecting them, additional subprojects are generated to build and run the unit tests for STXXL. ## Use Visual Studio to Build STXXL - Afterwards, use Visual Studio to open the stxxl.sln STXXL "Solution" containing the projects. - Building the ALL_BUILD will first compile the STXXL library sources and then stxxl_tool and the test1 program in \c local/ \image html install_windows_msvc11.png - You can immediately start working with STXXL by modifying the test1.cpp code in the \c local/ directory. - Simply switch Visual Studio into Release mode for building without assertions and extra checks. - To build the examples and test suite open up CMake, enable the check-boxes BUILD_EXAMPLES or BUILD_EXAMPLES and press "Generate" again. This will create many sub-projects in the Visual Studio solution. \note For instruction on configuring disks on Windows, see \ref install_config ## Using STXXL as a Library inside a Project TODO. If you are an experienced MSVC user, please contribute! */ /** \page install_windows_boost Compiling and Installing STXXL with Visual Studio 2010 and Boost \author Timo Bingmann (2013) ## Prerequisites: - Microsoft Visual Studio <= 2010 (VC10). \n For newer versions you \link install_windows don't need Boost \endlink anymore. - CMake 2.8, visit http://www.cmake.org/ - (a git client for development on the bleeding edge) ## Unpack STXXL source code - Download the STXXL archive from http://stxxl.sourceforge.net
    or use your favorite git client to clone the development version: http://github.com/stxxl/stxxl.git - Unpack the archive at a convenient location. ## Download and Install Boost - We recommend the official binaries for MSVC. Visit http://sourceforge.net/projects/boost/files/boost-binaries/ and download the newest package for your compiler, e.g. boost_1_54_0-msvc-10.0-64.exe - When installing the package, make sure to install in directory \n C:\\Boost \n instead of the default location. This is the most easy way to enable the CMake auto-detection macros to find Boost. - The pre-compiled Boost libraries are then located at \n C:\\Boost\\lib64-msvc-10.0 or similar. \n You must rename this directory to just lib for CMake to automatically find it. In summary, CMake only looks for Boost at a few locations: C:\\Boost for headers and C:\\Boost\\lib for library files. While it is possible to instruct CMake to look elsewhere for the necessary files, the easier way is to just rename the directories as required. Otherwise, the Boost location must be specified to CMake by setting the variables \c BOOST_ROOT, \c BOOST_INCLUDEDIR and \c BOOST_LIBRARYDIR in the dialog box each time. ## Use CMake to Generate Visual Studio Projects - Open the CMake GUI. - Fill in the source code field with the place of the STXXL source and pick a build directory. - Pressing "Configure" brings up a dialog box: select your Visual Studio version (MSVC 10 here).
    Note that you must select between 32- and 64-bit building here. - Press "Generate" to run the CMake platform checks and to generate the Visual Studio project files. \image html install_windows_boost_cmake.png - If you get any error about CMake not being able to find Boost, refer to the previous section on the directories which CMake considers! - After generating the project, CMake presents a list of optional configuration switches. Maybe the most important are \c BUILD_TESTS and \c BUILD_EXAMPLES. By selecting them, additional subprojects are generated to build and run the unit tests for STXXL. ## Use Visual Studio to Build STXXL - Afterwards, use Visual Studio to open the stxxl.sln STXXL "Solution" containing the projects. - Building the ALL_BUILD will first compile the STXXL library sources and then stxxl_tool and the test1 program in \c local/ \image html install_windows_boost_msvc10.png - You can immediately start working with STXXL by modifying the test1.cpp code in the \c local/ directory. - Simply switch Visual Studio into Release mode for building without assertions and extra checks. - To build the examples and test suite open up CMake, enable the check-boxes BUILD_EXAMPLES or BUILD_EXAMPLES and press "Generate" again. This will create many sub-projects in the Visual Studio solution. \note For instruction on configuring disks on Windows, see \ref install_config */ /** \page install_config Disk Configuration Files \author Timo Bingmann (2013-2014) A main feature of the STXXL is to take advantage of parallel access to multiple disks. For this, you must define the disk configuration in a text file, using the syntax described below. If no file is found at the locations below, STXXL will by default create a 1000 MiB file in \c /var/tmp/stxxl on Unix or in the user's temp directory on Windows. These are the locations STXXL will look for a disk configuration file on Linux/Unix systems, in order of precedence: - If the environment variable STXXLCFG specifies a file, this is used. - Then the current directory of the program is checked: - first for .stxxl.$HOSTNAME (for host specific configuration), - then for .stxxl (for general configuration). - Then the \c $HOME directory of the current user is checked (usual method): - first for $HOME/.stxxl.$HOSTNAME (for host specific configuration), - then for $HOME/.stxxl (for general configuration). \warning On many Linux distributions the \c $HOSTNAME variable is not exported. For the host specific configuration to work, you must add "export HOSTNAME" to your shell configuration (.bashrc). On Windows systems, STXXL looks for a disk configuration file in the following directories: - If the environment variable STXXLCFG specifies a file, this is used. - Then the current directory of the program is checked: - first for .stxxl.\%COMPUTERNAME\%.txt (for host specific configuration), - then for .stxxl.txt (for general configuration). - Then the \c \%APPDATA\% directory of the current user is checked (usual method): - first for \%APPDATA\%/.stxxl.\%COMPUTERNAME\%.txt (for host specific configuration), - then for \%APPDATA\%/.stxxl.txt (for general configuration). \note In a default Windows 7 installation, \%APPDATA\% is C:\\Users\\\\\AppData\\Roaming \n You can visit your \%APPDATA% directory by simply entering "%APPDATA%" in the Windows Explorer address/location line. \section install_config_format Disk Configuration File Format Each line of the configuration file describes a disk. Lines starting with '#' are comments. A disk description uses the following format: \verbatim disk=,, \endverbatim Description of the parameters: - \ : full disk filename. - In order to access disks STXXL uses file-based access methods (see below). Each disk is represented as a file - If you have a disk that is mounted in Unix to the path /mnt/disk0/, then the correct value for the \c full_disk_filename would be \c /mnt/disk0/some_file_name. \n - If the string contains "###" (three '#'), then these symbols are replaced by the current process id. - \ : maximum capacity of the disk - the following size suffixes are recognized: - \c K, \c M, \c G, \c T, \c P (powers of 10), - \c Ki, \c Mi, \c Gi, \c Ti, \c Pi (powers of 2). - if a number has no suffix, \c M (megabyte) is assumed. - 0 means autogrow, and the file will be deleted afterwards. - \ : \c STXXL has a number of different file access implementations, choose one of them (recommended ones in bold): - \c **syscall** : use \c read and \c write system calls which perform disk transfers directly on user memory pages without superfluous copying (currently the fastest method) - \c **wincall** : on Windows, use direct calls to the Windows API. - \c **linuxaio** : on Linux, use direct syscalls to the native Linux AIO interface. \n The Linux AIO interface has the advantage of keeping an asynchronous queue inside the kernel. Multiple I/O requests are submitted to the kernel at once, thus the kernel can sort then using its disk schedulers and also forward them to the actual disks as asynchronous operations using NCQ (native command queuing) or TCQ (tagged command queueing). - \c memory : keeps all data in RAM, for quicker testing - \c mmap : \c use \c mmap and \c munmap system calls - \c boostfd : access the file using a Boost file descriptor - \c fileperblock_syscall, \c fileperblock_mmap, \c fileperblock_boostfd : same as above, but take a single file per block, using full_disk_filename as file name prefix. Usually provide worse performance than the standard variants, but release freed blocks to the file system immediately. - \c simdisk : simulates timings of the IBM IC35L080AVVA07 disk, full_disk_filename must point to a file on a RAM disk partition with sufficient space - \c wbtl : library-based write-combining (good for writing small blocks onto SSDs), based on \c syscall - \ : additional options for file access implementation. Not all are available for every fileio method. The option order is unimportant. - \c autogrow : enables automatic growth of the file beyond the specified capacity. - \c **direct**, \c nodirect, \c direct=[off/try/on] : disable buffering in system cache by passing O_DIRECT or similar flag to open. \n This is \a recommended as it improves performance, however, not all filesystems support bypassing cache. With \c direct or \c direct=on, STXXL will fail without direct access. With \c nodirect or \c direct=off it is disabled. The default is \c direct=try , which first attempts to open with O_DIRECT and falls back to opening without if it fails. - \c **unlink** (or \c unlink_on_open) : unlink the file from the fs immediately after creation. \n This is possible on Unix system, as the file descriptor is kept open. This method is \b preferred, because even in the case of a program segfault, the file data is cleaned up by the kernel. - \c **delete** (or \c delete_on_exit) : delete file \a after the STXXL program exists \n This is the more conservative version of unlink, which also works on Windows. However, if the program crashes, the file is not deleted. - \c **raw_device** : fail if the opened path is not a raw block device. \n This flag is not required, raw devices are automatically detected. - \c queue=# : assign the disk to a specific I/O request queue and thread. \n Use this for multiple files that reside on the same physical disk. - \c devid=# : assign the disk entry a specific physical device id. \n Usually you can just omit the devid=# option, since disks are enumerated automatically. In sorting and other prefetched operations, the physical device id is used to schedule block transfers from independent devices. Thus you should label files/disks on the same physical devices with the same devid. - \c queue_length=# : specify for linuxaio the desired queue inside the linux kernel using this option. Example: \verbatim disk=/data01/stxxl,500G,syscall unlink disk=/data02/stxxl,300G,syscall unlink \endverbatim On Windows, one usually uses different disk drives and \c wincall. \verbatim disk=c:\stxxl.tmp,700G,wincall delete disk=d:\stxxl.tmp,200G,wincall delete \endverbatim On Linux you can try to take advantage of NCQ + Kernel AIO queues: \verbatim disk=/data01/stxxl,500G,linuxaio unlink disk=/data02/stxxl,300G,linuxaio unlink \endverbatim \section install_config_filesystem Recommended: File System XFS or Raw Block Devices The library benefits from direct transfers from user memory to disk, which saves superfluous copies. We recommend to use the XFS file system, which gives good read and write performance for large files. Note that file creation speed of \c XFS is a bit slower, so that disk files should be precreated for optimal performance. If the filesystems only use is to store one large STXXL disk file, we also recommend to add the following options to the \c mkfs.xfs command to gain maximum performance: \verbatim $ mkfs.xfs -d agcount=1 -l size=512b \endverbatim The following filesystems have been reported not to support direct I/O: \c tmpfs , \c glusterfs . By default, STXXL will first try to use direct I/O (\c O_DIRECT open flag). If that fails, it will print a warning and open the file without \c O_DIRECT. \note It is also possible to use raw disk devices with \c syscall. \n Just use \c disk=/dev/sdb1 or similar. This will of course \b overwrite all data on the partitions! The I/O performance of raw disks is generally more stable and slightly higher than with file systems. \code disk=/dev/sdb1,0,syscall raw_device \endcode The \c raw_device flag is only for verification, STXXL will automatically detect raw block devices and also their size. \section install_config_logfiles Log Files STXXL produces two kinds of log files, a message and an error log. By setting the environment variables \c STXXLLOGFILE and \c STXXLERRLOGFILE, you can configure the location of these files. The default values are \c stxxl.log and \c stxxl.errlog, respectively. \section install_config_precreation Precreating External Memory Files In order to get the maximum performance one can precreate disk files described in the configuration file, before running STXXL applications. A precreation utility is included in the set of STXXL utilities in \c stxxl_tool. Run this utility for each disk you have defined in the disk configuration file: \verbatim $ stxxl_tool create_files // for example: $ stxxl_tool create_files 1GiB /data01/stxxl \endverbatim \section install_config_user User-Supplied disk_config Structures With STXXL >= 1.4.0, the library can also be configured via the user application. All disk configuration is managed by the stxxl::config class, which contains a list of stxxl::disk_config objects. Each stxxl::disk_config object encapsulates one disk= lines from a config file, or one allocated disk. The disk configuration must be supplied to the STXXL library before any other function calls, because the stxxl::config object must be filled before any external memory blocks are allocated by stxxl::block_manager. \code int main() { // get uninitialized config singleton stxxl::config * cfg = stxxl::config::get_instance(); // create a disk_config structure. stxxl::disk_config disk1("/tmp/stxxl.tmp", 100 * 1024 * 1024, "syscall unlink"); disk1.direct = stxxl::disk_config::DIRECT_ON; // force O_DIRECT // add disk to config cfg->add_disk(disk1); // add another disk cfg->add_disk( disk_config("disk=/tmp/stxxl-2.tmp, 10 GiB, syscall unlink") ); // ... add more disks // use STXXL library as usual ... } \endcode */ stxxl-1.4.1/doc/tutorial_stack.dox000644 001411 000144 00000006251 12405153572 017016 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_stack.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_stack STXXL Stack This section introduces into the STXXL stack container (to learn more about the structure of stxxl::stack, see section \ref design_stack). STXXL stacks are last in first out (LIFO), i.e. inserting and extracting elements are only allowed from one end of the container and the element on top is the element added most recently. ### Creating a STXXL stack Before using a STXXL stack, we initially have to define and then to instantiate a stack object. To manage the configuration of the stack type we used the generator template. A minimal configuration is shown below - as one can see the value_type (integer in our case) is the only stricly neccessary parameter. See \ref design_stack_generator for additional configuration parameters and information. \code typedef stxxl::STACK_GENERATOR::result stack; stack my_stack; // create empty stack object \endcode Hint: STXXL stack provides specialized implementations for a certain access pattern. If the access pattern is known before, such a customization might gain a significant speedup. The default stack is stxxl::normal_stack which is the best for a random sequence of push'es and pop's. See \ref design_stack section for more details. ### Insert / Access / Delete elements To insert an element on top of the stack call push(): \code my_stack.push(7); my_stack.push(2); my_stack.push(5); // stack from bottom to top: |7|2|5| \endcode To access the top element call top(): \code std::cout << "element on top of stack is: " << my_stack.top << std::endl; // prints out 5 \endcode To remove the top element call pop(). \code my_stack.pop(); // removes element 5 \endcode ### Determine size / Check whether stack is empty To determine the size (i.e. the number of elements) of stack instance, call size(): \code std::cout << "size of stack: " << my_stack.size() << std::endl; \endcode To check if the stack is empty, call empty() which returns true in case of emptyness: \code // loop till stack is empty while (!my_stack.empty()) { // do something } \endcode ### A minimal working example of STXXL's stack (See \ref examples/containers/stack1.cpp for the sourcecode of the following example). \snippet examples/containers/stack1.cpp example See \ref examples/containers/stack2.cpp for the sourcecode of a more comprehensive example. \example examples/containers/stack1.cpp This example code is explained in the \ref tutorial_stack section \example examples/containers/stack2.cpp This example code is explained in the \ref tutorial_stack section */ } // namespace stxxl-1.4.1/doc/common.dox000644 001411 000144 00000051224 12405153572 015256 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/common.dox * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { //////////////////////////////////////////////////////////////////////////////// /** \page common Common Utilities and Helpers \author Timo Bingmann (2013) A lots of basic utility classes and helper functions have accumulated in STXXL. Try are usually fundamental enough to be also used in an application program. Before implementing a common software utility, please check the list below; it might already exist in STXXL: - \subpage common_io_counter "I/O statistics and performance counter" - \subpage common_random "random number generators" - \subpage common_timer "timestamp and timer function" - \subpage common_simple_vector "a non-growing, non-initializing simple_vector" - \subpage common_counting_ptr "reference counted (shared) objects via counting_ptr" - \subpage common_cmdline "command line parser" - \subpage common_binary_buffer "serialization of variable data structures into blobs" - \subpage common_thread_sync "synchronization primitives for multi-threading" - \subpage common_logging "logging macros" - \subpage common_assert "macros for checking assertions" - \subpage common_throw "macros for throwing exceptions" - \subpage common_types "signed and unsigned integer types" - \subpage common_log2 "calculating log_2(x)" - \subpage common_misc_macros "miscellaneous macros" - \subpage common_misc_funcs "miscellaneous functions" */ //////////////////////////////////////////////////////////////////////////////// /** \page common_random Random Number Generators See also file common/rand.h Measuring the time consumption of program sections are often of great interest. The Stxxl comes with several build-in pseudo random number generators as shown below: \code stxxl::random_number32 rand32; // integer values in [0, 2^32) stxxl::random_number64 rand64; // integer values in [0, 2^64) stxxl::random_uniform_slow urand_slow; // uniform values in [0.0, 1.0) stxxl::random_uniform_fast urand_fast; // uniform values in [0.0, 1.0) stxxl::random_number<> n_rand; // integer values in [0,N) unsigned int random32 = rand32(); stxxl::uint64 random64 = rand64(); double urandom_slow = urand_slow(); double urandom_fast = urand_fast(); unsigned int n_random = n_rand(123456789); STXXL_MSG("random 32 bit number: " << random32); STXXL_MSG("random 64 bit number: " << random64); STXXL_MSG("random number between [0.0, 1.0) (slow): " << urandom_slow); STXXL_MSG("random number between [0.0, 1.0) (fast): " << urandom_fast); STXXL_MSG("random number between [0,123456789): " << n_random); \endcode */ //////////////////////////////////////////////////////////////////////////////// /** \page common_timer Timestamp and Timer Classes See also file common/timer.h Measuring the time certain parts of an algorithm or the entire algorithm consume will often be of great interest. The STXXL provides build-in time measurement class stxxl::timer which can be used as follows: \code #include // make timer class available stxxl::timer Timer; // create Timer object Timer.start(); // code section which shall be measured Timer.stop(); // get results: STXXL_MSG(",easured time: " << (Timer.seconds()) << " (seconds), " << (Timer.mseconds()) << " (milliseconds), " << (Timer.useconds()) << " (microseconds)) Timer.reset(); // reset clock to zero which allows to run start() again \endcode As an alternative, one can also work on the timestamp itself: \code double start = stxxl::timestamp(); // code section to be measured double stop = stxxl::timestamp(); STXXL_MSG("measured time: " << (stop - start) << " seconds."); \endcode */ //////////////////////////////////////////////////////////////////////////////// /** \page common_simple_vector A Non-growing, Non-initializing Simpler Vector For applications where a std::vector is overkill, or one wishes to allocate an uninitialied POD array, the \ref simple_vector is a good method. */ //////////////////////////////////////////////////////////////////////////////// /** \page common_counting_ptr Reference Counted (Shared) Objects Some objects in STXXL are reference counted. This is usually done for large objects, which should not be copied deeply in assignments. Instead, a reference counting pointer is used, which holds a handle while the pointer exists and deletes the object once all pointers go out of scope. Examples are matrices and \ref stream::sorted_runs. The method used is called \ref counting_ptr or intrusive reference counting. This is similar, but not identical to boost or TR1's \c shared_ptr. The \ref counting_ptr is accompanied by \ref counted_object, which contains the actual reference counter (a single integer). A reference counted object must derive from \ref counted_object : \code struct something : public stxxl::counted_object { }; \endcode Code that now wishes to use pointers referencing this object, will typedef an \ref counting_ptr, which is used to increment and decrement the included reference counter automatically. \code typedef stxxl::counting_ptr something_ptr { // create new instance of something something_ptr p1 = new something; { // create a new reference to the same instance (no deep copy!) something_ptr p2 = p1; // this block end will decrement the reference count, but not delete the object } // this block end will delete the object } \endcode The \ref counting_ptr can generally be used like a usual pointer or \c shared_ptr (see the docs for more). There is also \ref const_counting_ptr to return const objects (note that a const \ref counting_ptr will only render the pointer const, not the enclosed object). */ //////////////////////////////////////////////////////////////////////////////// /** \page common_cmdline Command Line Parser STXXL now contains a rather sophisticated command line parser for C++, \ref cmdline_parser, which enables rapid creation of complex command line constructions. Maybe most importantly for application with external memory: the parser will recognize byte sizes with SI/IEC suffixes like '2 GiB' and transform it appropriately. \snippet examples/common/cmdline.cpp example When running the program above without arguments, it will print: \verbatim $ ./cmdline Missing required argument for parameter 'filename' Usage: ./cmdline [options] This may some day be a useful program, which solves many serious problems of the real world and achives global peace. Author: Timo Bingmann Parameters: filename A filename to process Options: -r, --rounds N Run N rounds of the experiment. -s, --size Number of bytes to process. \endverbatim Nice output, notice the line wrapping of the description and formatting of parameters and arguments. These too are wrapped if the description is too long. We now try to give the program some arguments: \verbatim $ ./cmdline -s 2GiB -r 42 /dev/null Option -s, --size set to 2147483648. Option -r, --rounds N set to 42. Parameter filename set to "/dev/null". Command line parsed okay. Parameters: filename (string) "/dev/null" Options: -r, --rounds N (unsigned integer) 42 -s, --size (bytes) 2147483648 \endverbatim The output shows pretty much what happens. The command line parser is by default in a verbose mode outputting all arguments and values parsed. The debug summary shows to have values the corresponding variables were set. One feature worth naming is that the parser also supports lists of strings, i.e. \c std::vector via \ref cmdline_parser::add_param_stringlist() and similar. \example examples/common/cmdline.cpp This example is documented in \ref common_cmdline tutorial. */ //////////////////////////////////////////////////////////////////////////////// /** \page common_binary_buffer Serializing Variable Data Structures with binary_buffer Some applications of STXXL will require variable data structures. Currently there is not much support for this in STXXL. For serializing information into in-memory data blocks, the STXXL provides the helper classes \ref binary_buffer and \ref binary_reader. These provide functions \ref binary_buffer::put<>() to append arbitrary integral data types and \ref binary_reader::get<>() to read these again. Serialization and deserialization of variable data structures are then composed of identical sequences of put()/get(). Additionally, the classes already provide methods to serialize variable length strings (together with their lengths), and thereby also sub-block serialization. These functions are called \ref binary_buffer::put_string() and \ref binary_reader::get_string(). Furthermore, to squeeze small integers into fewer bytes, they classes also contain "varint" encoding, where each byte contains 7 data bits and one continuation bit. These functions are called \ref binary_buffer::put_varint() and \ref binary_reader::get_varint(). The following example fills a binary_buffer with some data elements: \snippet tests/common/test_binary_buffer.cpp serialize And the following binary_reader example deserializes the data elements and check's their content. \snippet tests/common/test_binary_buffer.cpp deserialize */ //////////////////////////////////////////////////////////////////////////////// /** \page common_thread_sync Synchronization Primitives for Multi-Threading To support multi-threading, some parts of STXXL use synchronization primitives to ensure correct results. The primitives are based either on pthreads or on Boost classes. \section mutex Mutexes For simple mutual exclusion contexts, stxxl::mutex objects should be used together with scoped locks: \code class Something { stxxl::mutex m_mtx; void critical_function() { stxxl::scoped_mutex_lock lock(m_mtx); // do something requiring locking } }; \endcode \section semaphore Semaphores Additionally stxxl::semaphore is available if counting is required. \section further Further Primitives: State and OnOff-Switch stxxl::state is a synchronized state switching mechanism? stxxl::onoff_switch is a two state semaphore thing? */ //////////////////////////////////////////////////////////////////////////////// /** \page common_logging Logging Macros STXXL_MSG All STXXL components should output log or trace messages using the following macros. There are two basic methods for logging using ostream syntax: \code // for plain messages STXXL_MSG("text " << var) // for error messages STXXL_ERRMSG("error message " << reason) \endcode For debugging and tracing the following macros can be used for increasing levels of verbosity: \code // level 0 (for current debugging) STXXL_VERBOSE0("text " << var) // level 1,2 and 3 for more verbose debugging level STXXL_VERBOSE1("text " << var) STXXL_VERBOSE2("text " << var) STXXL_VERBOSE3("text " << var) \endcode A method used by some submodule authors to create their own levels of verbosity is to make their own debugging macros: \code #define STXXL_VERBOSE_VECTOR(msg) STXXL_VERBOSE1("vector[" << static_cast(this) << "]::" << msg) \endcode */ //////////////////////////////////////////////////////////////////////////////// /** \page common_assert Macros for Checking Assertions There are quite a bunch of macros for testing assertions. You must be careful to pick the right one depending on when and what you want to assert on. # Compile-time Assertions: STXXL_STATIC_ASSERT To check specific conditions at compile time use \ref STXXL_STATIC_ASSERT. \code struct item { int a,b,c,d; } STXXL_STATIC_ASSERT(sizeof(item) == 4 * sizeof(int)); \endcode # Assertions in Unit Tests: STXXL_CHECK Assertions in unit tests must use the following macros to ensure that the condition is also checked in release builds (where a plain \c "assert()" is void). These \c CHECK function should NOT be used to test return values, since we try to throw exceptions instead of aborting the program. \code // test a condition STXXL_CHECK( 2+2 == 4 ); // test a condition and output a more verbose reason on failure STXXL_CHECK2( 2+2 == 4, "We cannot count!"); \endcode Sometimes one also wants to check that a specific expression \b throws an exception. This checking can be done automatically using a try { } catch {} by using \ref STXXL_CHECK_THROW. # Plain Assertions: assert For the usual assertions, that should be removed in production code for performance, we use the standard \c "assert()" function. However, there is also \ref STXXL_ASSERT(), which can be used as a replacement for \c assert(), when compiler warnings about unused variables or typedefs occur. The issue is that assert() completely removes the code, whereas \ref STXXL_ASSERT() keeps the code encloses it inside \c if(0). */ //////////////////////////////////////////////////////////////////////////////// /** \page common_throw Macros for Throwing Exception The STXXL provides several pre-defined exception macros to detect run-time errors. The basic ones are: - STXXL_THROW(exception_type, error_message) - STXXL_THROW2(exception_type, location, error_message) - STXXL_THROW_ERRNO(exception_type, error_message) - STXXL_THROW_INVALID_ARGUMENT(error_message) - STXXL_THROW_UNREACHABLE(error_message) In addition, we also defined \b conditional throw macros, which check the outcome of a function call: - STXXL_THROW_IF(expr, exception_type, error_message) - STXXL_THROW_NE_0(expr, exception_type, error_message) - STXXL_THROW_EQ_0(expr, exception_type, error_message) - STXXL_THROW_LT_0(expr, exception_type, error_message) For checking system calls which set errno, the following macros are used to also provide strerror information for the user: - STXXL_THROW_ERRNO_IF(expr, exception_type, error_message) - STXXL_THROW_ERRNO_NE_0(expr, exception_type, error_message) - STXXL_THROW_ERRNO_EQ_0(expr, exception_type, error_message) - STXXL_THROW_ERRNO_LT_0(expr, exception_type, error_message) For checking pthread system calls, a special macro is needed, because these do not set errno. Instead they return the errno value: - STXXL_CHECK_PTHREAD_CALL(pthread call) And for WINAPI calls there is a special macro to call GetLastError and format it in a nice way: - STXXL_THROW_WIN_LASTERROR(exception_type, error_message) */ //////////////////////////////////////////////////////////////////////////////// /** \page common_log2 Calculating log2(x) for Integers and at Compile-Time STXXL provides three methods to calculate log2(x), which is often needed for binary trees, etc. The first is during \b compile-time using template meta-programming magic: \code #include std::cout << stxxl::LOG2<10000>::floor << std::endl; std::cout << stxxl::LOG2<10000>::ceil << std::endl; \endcode The second is for calculating log2(x) for \b integer arguments using simple bit shift arithmetic: \code #include std::cout << stxxl::ilog2_floor(10000) << std::endl; std::cout << stxxl::ilog2_ceil(10000) << std::endl; \endcode The third and probably least useful is to use conversion to \b double and \c math.h's facilities: \code #include std::cout << stxxl::log2_floor(10000) << std::endl; std::cout << stxxl::log2_ceil(10000) << std::endl; \endcode */ //////////////////////////////////////////////////////////////////////////////// /** \page common_types Signed and Unsigned Integer Types STXXL provides two very important types: \ref stxxl::int_type and \ref stxxl::unsigned_type. These should be used for general counting and indexing types, as they are defined to be the size of a register on the machines: on 32-bit machines the two types are 4 bytes size, while on a 64-bit machine the two types are 8 bytes in size! The previous types are for general purpose counting. For real 64-bit integers, also on 32-bit machines, STXXL also provides a stxx::uint64 type (independent of other headers). See the file common/types.h \section common_types_uint uint40 and uint48 Unsigned Integer Types When storing file offsets in external memory, one often does not require full 64-bit indexes. Mostly, 40-bit or 48-bit are sufficient, if only < 1 TiB or < 16 TiB of data are processed. If one stores theses integers in five or six bytes, the total I/O volume can be reduced significantly. Since this issue occurs commonly in EM algorithms, STXXL provides two types: stxxl::uint40 and stxxl::uint48. See the file common/uint_types.h */ //////////////////////////////////////////////////////////////////////////////// /** \page common_misc_macros Miscellaneous Macros \section namespaces STXXL_BEGIN_NAMESPACE A long, long time ago, not all compilers supported C++ namespaces, thus STXXL uses the macros \ref STXXL_BEGIN_NAMESPACE and \ref STXXL_END_NAMESPACE, which open and close the \c "namespace stxxl". \section unused STXXL_UNUSED \ref STXXL_UNUSED is not actually a macro. It is a remedy against "unused variable" warnings, for whatever reason. Usage: \code void function(int x) { STXXL_UNUSED(x); } \endcode \section likely LIKELY and UNLIKEY Some compilers have facilities to specify whether a condition is likely or unlikely to be true. This may have consequences on how to layout the assembler code better. \code if (LIKELY(x > 1)) { ... } if (UNLIKELY(x > 8)) { ... } \endcode \section deprecated Deprecated Functions Some compilers can warn the user about deprecated function by tagging them in the source. In STXXL we use the macro \ref STXXL_DEPRECATED(...) to enclose deprecated functions. */ //////////////////////////////////////////////////////////////////////////////// /** \page common_misc_funcs Miscellaneous Functions \section parse_filesize Parsing Filesizes with K, M, G suffixes Since with STXXL one often has to parse large file or disk sizes, there is a function called \ref parse_SI_IEC_size(), which accepts strings like "1 GiB" or "20 TB" as input. See the \ref install_config documentation page on the format of accepted file size strings. */ //////////////////////////////////////////////////////////////////////////////// /** \page common_io_counter I/O Performance Counter The STXXL library provides various I/O performance counters (stxxl::stats class) which can be used to get an extensive set of I/O statistics. They can be accessed as follows: \code // generate stats instance stxxl::stats * Stats = stxxl::stats::get_instance(); // start measurement here stxxl::stats_data stats_begin(*Stats); // some computation ... // substract current stats from stats at the beginning of the measurement std::cout << (stxxl::stats_data(*Stats) - stats_begin); \endcode The Stats ostream holds various measured I/O data: \verbatim STXXL I/O statistics total number of reads : 2 average block size (read) : 2097152 (2.000 MiB) number of bytes read from disks : 4194304 (4.000 MiB) time spent in serving all read requests : 0.062768 s @ 63.7268 MiB/s time spent in reading (parallel read time) : 0.062768 s @ 63.7268 MiB/s total number of writes : 2 average block size (write) : 2097152 (2.000 MiB) number of bytes written to disks : 4194304 (4.000 MiB) time spent in serving all write requests : 0.0495751 s @ 80.6857 MiB/s time spent in writing (parallel write time): 0.0495751 s @ 80.6857 MiB/s time spent in I/O (parallel I/O time) : 0.112343 s @ 71.2104 MiB/s I/O wait time : 0.104572 s I/O wait4read time : 0.054934 s I/O wait4write time : 0.049638 s Time since the last reset : 0.605008 s \endverbatim We can access individual I/O data in contrast to the whole content of Stats ostream by: \code std::cout << Stats->get_written_volume() << std::endl; // print number of bytes written to the disks \endcode \b Hint: There's another statistical parameter which may be in developer's interest: the maximum number of bytes (the peak) allocated in external memory during program run. This parameter can be accessed by: \code stxxl::block_manager * bm = stxxl::block_manager::get_instance(); // lots of external work here... std::cout << "max: " << bm->get_maximum_allocation() << std::endl; // max. number of bytes allocated until now \endcode See \ref stxxl::stats and \ref stxxl::stats_data class reference for all provided individual functions. */ //////////////////////////////////////////////////////////////////////////////// } // namespace stxxl stxxl-1.4.1/doc/tutorial_unordered_map.dox000644 001411 000144 00000007070 12411366426 020536 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_unordered_map.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_unordered_map STXXL Unordered Map (Hash Map) This page introduces the **EXPERIMENTAL** stxxl::unordered_map which can be used in-lieu of std::unordered_map (for further information on the interface, refer to the API \ref stxxl::unordered_map). stxxl::unordered_map is an external memory hash map that stores elements formed by a combination of a unique key value and a data value, without any specific order. The main problem is that a hash map **ITSELF IS NOT VERY EFFICIENT** in external memory, since access to an element requires a random access to disk. **PLEASE CHECK** whether an ordered sequence, as provided by stxxl::map, may not be the better replacement for your application. However, if you are willing to provide **a lot of buffer memory** then the hash map can cache many items in internal memory, and direct hash-based access will be very fast. Also, with SSDs one may be able to reduce the block size. The implementation of the unordered hash_map is experimental, and help for improving, fixing bugs and writing documentation in it is very welcome. If you have an application, please consider **THROUGHLY TESTING** the implementation and patching problems. ### Creating a STXXL Unordered Map To create a stxxl::unordered_map object, several template parameters are required. The first two parameters KeyType and MappedType, which are combined into a std::pair in this example, are self-explanatory, the third parameter is a *hasher class* and the fourth has to be a *comparator class* which is used to determine whether a key is smaller than another one, the fifth and sixth parameters define the subblock- and block size (in subblock items). \snippet examples/containers/unordered_map1.cpp construction The hash function follows the standard std::hash signature, and returns a size_t: \snippet examples/containers/unordered_map1.cpp hash Instead of the **equality comparator** as required by the C++ standard, we require a **less comparator**, because the unordered_map **sorts** bulk insertions by hash value. A simple comparator looks like: \snippet examples/containers/unordered_map1.cpp comparator After construction, the standard operations of an unordered map are available as one would think, see below for a short example of some function. ### Additional Implementation Notes * The implementation contains some TODO items very relevant to performance. A potential heavy user should consider fixing these. * As the btree, the unordered_map must keep an iterator map for updating items when they are swapped out to disk. TODO: write more information. ### A minimal working example on STXXL Unordered Map (See \ref examples/containers/unordered_map1.cpp for the sourcecode of the following example). \snippet examples/containers/unordered_map1.cpp example \example examples/containers/unordered_map1.cpp This example code is explained in the \ref tutorial_unordered_map section */ } // namespace stxxl stxxl-1.4.1/doc/tutorial_sequence.dox000644 001411 000144 00000011036 12405153572 017516 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_sequence.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_sequence STXXL Sequence This page introduces into the stxxl::sequence container (to learn more about the structure of stxxl::sequence, see section \ref stxxl::sequence). In reality, the STXXL sequence container is a STXXL deque container (see \ref design_deque) without random access. Deque stands for "double-ended-queue", that means elements can be accessed, inserted and deleted on both ends of the data structure. Consequently both containers are quite similar - however, the usage varies. ### Create a STXXL sequence To create an empty stxxl::sequence object with own write and prefetch block pool, only the data value type must be specified: \code typedef stxxl::sequence sequence_type; sequence_type my_sequence; \endcode ### Insert elements Inserting elements is possible at the start (by calling the push_front() function) as well as the end (by calling the push_back() function) of the deque (equal to \ref tutorial_deque) \code my_sequence.push_front(2); my_sequence.push_front(11); my_sequence.push_back(5); my_sequence.push_back(8); // sequence now stores: |11|2|5|8| \endcode ### Access elements To return a reference to the element at the start of the sequence, call front(), to return a reference to the elemtent at the end of the sequence, call back() on a deque instance. \code std::cout << "return 'first' element: " << my_sequence.front() << std::endl; // prints 11 std::cout << "return 'last' element: " << my_sequence.back() << std::endl; // prints 8 \endcode Due to the fact that the sequence container does \b not support random access, the sequence can only be accessed in an I/O-efficient way by iterating using streams: either from front to back or in reverse. For this purpose, the sequence provides the public member functions get_stream() and get_reverse_stream(). The preincrement operator ++ let the stream point to the next element in the sequence (depending on the stream direction). Accessing an element the iterator points to is possible by using the prefixed * operator. To check if the end of the sequence container is reached by the stream, the empty() function returns true in such a case. Note that the stream is immutable and therefore read-only, so you can't modify it's members. The subsequent examples illustrate the usage. The forward iterator moves from back to front and may be used as follows: \code // create stream which points to the front element of the sequence sequence_type::stream forward_stream = my_sequence.get_stream(); // advance from front to back of sequence while (!forward_stream.empty()) { std::cout << *forward_stream << " "; ++forward_stream; } \endcode The reverse iterator moves from back to front and may be used as follows: \code // create stream which points to the back element of the sequence sequence_type::reverse_stream reverse_stream = my_sequence.get_reverse_stream(); // advance from back to front of sequence while (!reverse_stream.empty()) { std::cout << *reverse_stream << " "; ++reverse_stream; } \endcode ### Delete elements Removing elements is possible at both endings of the sequence by using pop_front() and pop_back(): \code my_deque.pop_front(); // deque now stores: |2|5|8| my_deque.pop_back(); // deque now stores: |2|5| \endcode ### Determine size / Check whether the sequence is empty To determine the size (i.e. the number of elements) of an instance, call size(): \code std::cout << "sequence stores: " << my_sequence.size() << " elements" << std::endl; \endcode To check if the sequence is empty, call empty() which returns true if the sequence is empty: \code std::cout << "empty sequence? " << my_sequence.empty() << std::endl; \endcode ### A minimal working example on STXXL's sequence (See \ref examples/containers/sequence1.cpp for the sourcecode of the following example). \snippet examples/containers/sequence1.cpp example \example examples/containers/sequence1.cpp This example code is explained in the \ref tutorial_sequence section. */ } // namespace stxxl stxxl-1.4.1/doc/tutorial_vector_buf.dox000644 001411 000144 00000013565 12405153572 020055 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_vector_buf.dox * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_vector_buf Efficient Sequential Reading and Writing to Vectors \author Timo Bingmann (2013) The stxxl::vector is a very versatile container and it allows sequential access loops like the following: \snippet examples/containers/vector_buf.cpp element However, these sequential loops using element access are not very efficient (see the experimental results below). Each \c operator[] is processed by the vector paging mechanism, and returns a writeable reference to the element. Because the reference might be modified, the vector must assume that the accessed page is dirty. Thus the read loop will actually rewrite the whole vector. This effect can be avoided using a const vector& or vector::const_iterator as shown in the following: \snippet examples/containers/vector_buf.cpp iterator This method is already pretty good, but one can achieve even better performance. The problem with iterators is that all accesses still go through the vector's paging algorithms, possibly updating the internal paging algorithm's state. More importantly, the access operators do not use prefetching. For this purpose STXXL provides buffered reading and writing to vector ranges. These utilized asynchronous I/O and will thus will overlap I/O with computation. The two basic classes to efficiently read and write vector are vector_bufreader and vector_bufwriter. Their interface is a combination of the \c stream interface and iostreams. The two classes are more conveniently accessible via vector::bufreader_type and vector::bufwriter_type. \snippet examples/containers/vector_buf.cpp buffered When using vector_bufwriter the vector's size \a should be allocated in advance. However, this is not required: when reaching the end of the vector, the buffered writer will automatically double the vector's size. Thus writing will not produce segfaults; however, doubling may go wrong for huge vectors. Note that the same efficiency can be achieved using stream functions: stream::vector_iterator2stream and stream::materialize also use overlapping I/O. As last method, which is currently supported by STXXL, one can iterate over the vector using C++11's \c auto \c for loop construct: \snippet examples/containers/vector_buf.cpp cxx11 Note that we must construct a buffered reader explicitly, because just stating "vec" would amount to using the usual iterators (with pager). Support for C++11 is still experimental. All source code from this example is available in \ref examples/containers/vector_buf.cpp. The program also check the sum results and measures the time. The following experimental results are from a machine with four disks: \verbatim $ vector_buf 64 [STXXL-MSG] STXXL v1.4.0 (prerelease) [STXXL-MSG] Disk '/data01/stxxl' is allocated, space: 162124 MiB, I/O implementation: syscall_unlink [STXXL-MSG] Disk '/data02/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall_unlink [STXXL-MSG] Disk '/data03/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall_unlink [STXXL-MSG] Disk '/data04/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall_unlink [STXXL-MSG] In total 4 disks are allocated, space: 848770 MiB [STXXL-MSG] Starting vector element access sum: 4393751543808 [STXXL-MSG] Finished vector element access after 848.695 seconds. Processed 128.000 GiB @ 154.439 MiB/s [STXXL-MSG] Starting vector iterator access sum: 4393751543808 [STXXL-MSG] Finished vector iterator access after 540.938 seconds. Processed 128.000 GiB @ 242.305 MiB/s [STXXL-MSG] Starting vector buffered access sum: 4393751543808 [STXXL-MSG] Finished vector buffered access after 441.26 seconds. Processed 128.000 GiB @ 297.040 MiB/s [STXXL-MSG] Starting vector C++11 loop access sum: 4393751543808 [STXXL-MSG] Finished vector C++11 loop access after 440.977 seconds. Processed 128.000 GiB @ 297.231 MiB/s \endverbatim Obviously, buffered access to stxxl::vector most efficient, where using const_iterators is only 18\% slower. Just using element access via operator[] is not a good idea. The difference between the methods grows smaller then using only one disk, because the I/O bandwidth decreases: \verbatim $ vector_buf 64 [STXXL-MSG] STXXL v1.4.0 (prerelease) [STXXL-MSG] Disk '/data01/stxxl' is allocated, space: 162124 MiB, I/O implementation: syscall_unlink [STXXL-MSG] Starting vector element access sum: 4393751543808 [STXXL-MSG] Finished vector element access after 2793.85 seconds. Processed 128.000 GiB @ 46.915 MiB/s [STXXL-MSG] Starting vector iterator access sum: 4393751543808 [STXXL-MSG] Finished vector iterator access after 1770.75 seconds. Processed 128.000 GiB @ 74.020 MiB/s [STXXL-MSG] Starting vector buffered access sum: 4393751543808 [STXXL-MSG] Finished vector buffered access after 1670.13 seconds. Processed 128.000 GiB @ 78.480 MiB/s [STXXL-MSG] Starting vector C++11 loop access sum: 4393751543808 [STXXL-MSG] Finished vector C++11 loop access after 1671.53 seconds. Processed 128.000 GiB @ 78.415 MiB/s \endverbatim As a last note: there is also vector_bufreader_reverse and vector::bufreader_reverse_type for buffered reading in reverse. \example examples/containers/vector_buf.cpp This example code is explained in the \ref tutorial_vector_buf section. \example examples/containers/copy_file.cpp This is an example of how to copy a file to another using STXXL's asynchronous I/O features. */ } // namespace stxxl stxxl-1.4.1/doc/tutorial_queue.dox000644 001411 000144 00000006352 12405153572 017037 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_queue.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_queue STXXL Queue This page introduces into the stxxl::queue Container (to learn more about the structure of stxxl::stack, see section \ref design_queue). ### Creating a STXXL queue Before using a STXXL queue, we initially have to define and then to instantiate a queue object. The implementation holds the head and the tail blocks in the main memory. Prefetch and write block pools might be used to overlap I/O and computation during queue operations. A minimal configuration is shown below - the value_type (integer in our example case) is the only stricly neccessary parameter. The default configuration initializes a write_pool and a prefetch_pool of size 1. \code typedef stxxl::queue queue; // create queue object with default parameters: // write_pool size = ?, prefetch_pool size = 1, blocks2prefetch = number of blocks in the prefetch pool (i.e. 1) queue my_queue; \endcode The STXXL queue implementation provides three different types of constructors to customize your individual caching. See \ref stxxl::queue more details. Additional optional template parameters are block_size, allocation_strategy, size_type, see \ref stxxl::queue for further details. ### Insert / Access / Delete elements To insert a new value at the beginning of the queue, call push(). Accessing elements are possible on both endings of the queue, back() returns the value at the beginning, front() returns the value at the end. Deleting a value by pop() erases the first inserted element. \code my_queue.push(5); // queue now stores: |5| my_queue.push(9); // queue now stores: |9|5| my_queue.push(1); // queue now stores: |1|9|5| x = my_queue.back(); // x = 1 y = my_queue.front(); // y = 5 my_queue.pop(); // queue now stores: |1|9| \endcode ### Determine size / Check whether queue is empty To determine the number of elements a queue currently stores, call size(): \code std::cout << "size of queue: " << my_queue.size() << std::endl; \endcode To check if the queue is empty, call empty() which returns true in that case: \code std::cout << "queue empty? " << my_queue.empty() << std::endl; \endcode ### A minimal working example of STXXL's queue (See \ref examples/containers/queue1.cpp for the sourcecode of the following example). \snippet examples/containers/queue1.cpp example See \ref examples/containers/queue2.cpp for the sourcecode of a more comprehensive example. \example examples/containers/queue1.cpp This example code is explained in the \ref tutorial_queue section. \example examples/containers/queue2.cpp This example code is explained in the \ref tutorial_queue section. */ } // namespace stxxl stxxl-1.4.1/doc/introduction.dox000644 001411 000144 00000036407 12405153572 016515 0ustar00tbusers000000 000000 /*************************************************************************** * doc/introduction.dox * * Most of this is from the old TeX tutorial and papers. * Edited 2013 by Timo Bingmann * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /** \page introduction Introduction to External Memory \author Roman Dementiev, Lutz Kettner, Peter Sanders (2007) Massive data sets arise naturally in many domains. Spatial databases of geographic information systems like GoogleEarth and NASA’s World Wind store terabytes of geographically referenced information that includes the whole Earth. In computer graphics one has to visualize highly complex scenes using only a conventional workstation with limited memory \cite Farias2001. Billing systems of telecommunication companies evaluate terabytes of phone call log files \cite BillingLarge. One is interested in analyzing huge network instances like a web graph \cite Donato2006 or a phone call graph. Search engines like Google and Yahoo provide fast text search in their databases indexing billions of web pages. A precise simulation of the Earth’s climate needs to manipulate with petabytes of data \cite Moore2000. These examples are only a sample of numerous applications that have to process vast amounts of data. The internal memories of computers can keep only a small fraction of these large data sets. During the processing the applications need to access the external memory (e.g. hard disks) very frequently. One such access can be about 106 times slower than a main memory access. Therefore, the disk accesses (I/Os) become the main bottleneck. The data are stored on the magnetic surface of a hard disk that rotates 4200–15,000 times per minute. In order to read or write a designated track of data, the disk controller moves the read/write arm to the position of this track (seek latency). If only a part of the track is needed, there is an additional rotational delay. The total time for such a disk access is an average of 3–10 ms for modern disks. The latency depends on the size and rotational speed of the disk and can hardly be reduced because of the \a mechanical nature of hard disk technology. After placing the read/write arm, the data are streamed at a high speed which is limited only by the surface data density and the MB bandwidth of the I/O interface. This speed is called sustained throughput and achieves up to 80 MB/s nowadays. In order to amortize the high seek latency, one reads or writes the data in blocks. The block size is balanced when the seek latency is a fraction of the sustained transfer time for the block. Good results show blocks containing a full track. For older low-density disks of the early 90s the track capacities were about 16–64 kB. Nowadays, disk tracks have a capacity of several megabytes. Operating systems implement the virtual memory mechanism that extends the working space for applications, mapping an external memory file (page/swap file) to virtual addresses. This idea supports the Random Access Machine model \cite Neu45 in which a program has an infinitely large main memory. With virtual memory the application does not know where its data are located: in the main memory or in the swap file. This abstraction does not have large running time penalties for simple sequential access patterns: the operating system is even able to predict them and to load the data in ahead. For more complicated patterns these remedies are not useful and even counterproductive: the swap file is accessed very frequently; the executable code can be swapped out in favor of unnecessary data; the swap file is highly fragmented and thus many random I/O operations are needed even for scanning. \section introduction_io_model I/O-efficient Algorithms and Models The operating system cannot adapt to complicated access patterns of applications dealing with massive data sets. Therefore, there is a need for explicit handling of external memory accesses. The applications and their underlying algorithms and data structures should care about the pattern and the number of external memory accesses (I/Os) which they cause. Several simple models have been introduced for designing I/O-efficient algorithms and data structures (also called external memory algorithms and data structures). The most popular and realistic model is the Parallel disk model (PDM) of Vitter and Shriver \cite VitShr94both. In this model, I/Os are handled explicitly by the application. An I/O operation transfers a block of \a B consecutive elements from/to a disk to amortize the latency. The application tries to transfer \a D blocks between the main memory of size \a M bytes and \a D independent disks in one I/O step to improve bandwidth, see figure below. The input size is \a N bytes which is (much) larger than \a M. The main complexity metrics of an I/O-efficient algorithm in PDM are the number of I/O steps (main metric) and the number of operations executed by the CPU. If not I/O but a slow internal CPU processing is the limiting factor of the performance of an application, we call such behavior CPU-bound. The PDM has become the standard theoretical model for designing and analyzing I/O-efficient algorithms. For this model, the following matching upper and lower bounds for I/O complexity are known. Scanning a sequence of N items takes \f$ \mathrm{scan}(N) = \Theta(N / (DB)) \f$ I/Os. Sorting a sequence of \a N items takes \f$ \mathrm{sort}(N) = \Theta(N / (DB) \cdot \log_{M/B} (N/M)) \f$ I/Os. Online search among \a N items takes \f$ \mathrm{search}(N) = \Theta(\log_{DB} (N)) \f$ I/Os. \section introduction_memory_hierarchies Memory Hierarchies The PDM measures the transfers between the main memory and the hard disks, however, in modern architectures, the CPU does not access the main memory directly. There are a few levels of faster memory caches in-between (figure below): CPU registers, level one (L2), level two (L2) and even level three (L3) caches. The main memory is cheaper and slower than the caches. Cheap dynamic random access memory, used in the majority of computer systems, has an access latency up to 60 ns whereas L1 has a latency of less than a ns. However, for a streamed access a high bandwidth of several GB/s can be achieved. The discrepancy between the speed of CPUs and the latency of the lower hierarchy levels grows very quickly: the speed of processors is improved by about 55% yearly, the hard disk access latency only by 9% \cite Patterson2004. Therefore, the algorithms that are aware of the memory hierarchy will continue to benefit in the future and the development of such algorithms is an important trend in computer science. \image html pdm_small.png "Schemes of parallel disk model (left) and memory hierarchy (right)" The PDM model only describes a single level in the hierarchy. An algorithm tuned to make a minimum number of I/Os between two particular levels could be I/O-inefficient on other levels. The cache-oblivious model in \cite FLPR99 avoids this problem by not providing the knowledge of the block size \a B and main memory size \a M to the algorithm. The benefit of such an algorithm is that it is I/O-efficient on all levels of the memory hierarchy across many systems without fine tuning for any particular real machine parameters. Many basic algorithms and data structures have been designed for this model (\cite FLPR99, \cite ABDHBM02, \cite BDIW02, \cite BFMZ04). A drawback of cache-oblivious algorithms playing a role in practice is that they are only asymptotically I/O-optimal. The constants hidden in the O-notation of their I/O-complexity are significantly larger than the constants of the corresponding I/O-efficient PDM algorithms (on a particular memory hierarchy level). For instance, a tuned cache-oblivious funnel sort implementation \cite ChristianiThesis is 2.6–4.0 times slower than our I/O-efficient sorter from STXXL (see \ref design_algo_sorting) for out-of-memory inputs \cite Ajwani2007. A similar funnel sort implementation \cite BFV04 is up to two times slower than the I/O-efficient sorter from the TPIE library for large inputs. The reason for this is that these I/O-efficient sorters are highly optimized to minimize the number of transfers between the main memory and the hard disks where the imbalance in the access latency is the largest. Cache-oblivious implementations tend to lose on the inputs, exceeding the main memory size, because they do (a constant factor) more I/Os at the last level of memory hierarchy. In this paper, we concentrate on extremely large out-of-memory inputs, therefore, we will design and implement algorithms and data structures efficient in the PDM. \section introduction_algorithm_engineering Algorithm Engineering for Large Data Sets Theoretically, I/O-efficient algorithms and data structures have been developed for many problem domains: graph algorithms, string processing, computational geometry, etc. (see the surveys \cite MSS03, \cite Vit01). Some of them have been implemented: sorting, matrix multiplication (\cite TPIEscientific96), search trees (\cite ChiangPHD, \cite Bkdtree03, \cite DynRTrees99, \cite CRBtree03), priority queues (\cite Brengel00), text processing (\cite CraFer02). However, only few of the existing I/O-efficient algorithms have been studied experimentally. As new algorithmic results rely on previous ones, researchers, who would like to engineer practical implementations of their ideas and show the feasibility of external memory computation for the solved problem, need to invest much time in the careful design of unimplemented underlying external algorithms and data structures. Additionally, since I/O-efficient algorithms deal with hard disks, a good knowledge of low-level operating system issues is required when implementing details of I/O accesses and file system management. This delays the transfer of theoretical results into practical applications, which will have a tangible impact for industry. Therefore, one of the primary goals of algorithm engineering for large data sets is to create software frameworks and libraries that handle both the low-level I/O details efficiently and in an abstract way, and provide well-engineered and robust implementations of basic external memory algorithms and data structures. \section introduction_stl C++ Standard Template Library The Standard Template Library (STL) \cite stepanov94standard is a C++ library which is included in every C++ compiler distribution. It provides basic data structures (called containers) and algorithms. STL containers are generic and can store any built-in or user data type that supports some elementary operations (e.g. copying and assignment). STL algorithms are not bound to a particular container: an algorithm can be applied to any container that supports the operations required for this algorithm (e.g. random access to its elements). This flexibility significantly reduces the complexity of the library. STL is based on the C++ template mechanism. The flexibility is supported using compile-time polymorphism rather than the object-oriented run-time polymorphism. The run-time polymorphism is implemented in languages like C++ with the help of virtual functions that usually cannot be inlined by C++ compilers. This results in a high per-element penalty of calling a virtual function. In contrast, modern C++ compilers minimize the abstraction penalty of STL inlining many functions. STL containers include: \c std::vector (an unbounded array), \c std::priority queue, \c std::list, \c std::stack, \c std::deque, \c std::set, \c std::multiset (allows duplicate elements), \c std::map (allows mapping from one data item (a key) to another (a value)), \c std::multimap (allows duplicate keys), etc. Containers based on hashing (\c hash_set, \c hash_multiset, \c hash_map and \c hash_multimap) are not yet standardized and distributed as an STL extension. Iterators are an important part of the STL library. An iterator is a kind of handle used to access items stored in data structures. Iterators offer the following operations: read/write the value pointed by the iterator, move to the next/previous element in the container, move forward/backward (random access) by some number of elements. STL provides a large number of algorithms that perform scanning, searching, and sorting. The implementations accept iterators that possess a certain set of operations described above. Thus, the STL algorithms will work on any container with iterators following the requirements. To achieve flexibility, STL algorithms are parameterized with objects, overloading the function operator (operator()). Such objects are called \a functors. A functor can, for instance, define the sorting order for the STL sorting algorithm or keep the state information in functions passed to other functions. Since the type of the functor is a template parameter of an STL algorithm, the function operator does not need to be virtual and can easily be inlined by the compiler, thus avoiding the function call costs. The STL library is well accepted and its generic approach and principles are followed in other famous C++ libraries like Boost \cite karlsson2005beyond and CGAL \cite fabri1998design. \section introduction_goals The Goals of STXXL Several external memory software library projects (LEDA-SM \cite CraMeh99 and TPIE \cite tpie_manual) were started to reduce the gap between theory and practice in external memory computing. They offer frameworks that aim to speed up the process of implementing I/O-efficient algorithms, abstracting away the details of how I/O is performed. Those projects are excellent proofs of EM paradigm, but have some drawbacks which \b impede their practical use. Therefore we started to develop STXXL library, which tries to avoid those obstacles. The objectives of STXXL project (distinguishing it from other libraries): - Offer \b transparent support of parallel disks. This feature although announced has not been implemented in any library. - Implement \b parallel disk algorithms. LEDA-SM and TPIE libraries offer only implementations of single disk EM algorithms. - Make the library able to handle problems of real world size (up to dozens of terabytes). - Improved utilization of computer resources. STXXL explicitly supports \b overlapping between I/O and computation. STXXL implementations of external memory algorithms and data structures benefit from the overlapping of I/O and computation. - STXXL achieves small constant factors in I/O volume. In particular, \b "pipelining" can save more than \b half the number of I/Os performed by many algorithms. - Care about the internal work, improve the in-memory algorithms. Having many disks can hide the latency and increase the I/O bandwidth, s.t. internal work becomes a bottleneck. - Care about operating system overheads. Use unbuffered disk access to avoid superfluous copying of data. - Short development times due to well-known STL-compatible interfaces for external memory algorithms and data structures. STL algorithms can be directly applied to STXXL containers (code reuse); moreover, the I/O complexity of the algorithms remains optimal in most cases. */ stxxl-1.4.1/doc/design.dox000644 001411 000144 00000302655 12423721313 015240 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/design.dox * * Design of STXXL, all of this is from Roman's PhD thesis. * Edited by Timo Bingmann * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Roman Dementiev * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page design Design of STXXL \author Roman Dementiev (2006) STXXL is a layered library consisting of three layers (see following figure). The lowest layer, the Asynchronous I/O primitives layer (AIO layer), abstracts away the details of how asynchronous I/O is performed on a particular operating system. Other existing external memory algorithm libraries only rely on synchronous I/O APIs \cite CraMeh99 or allow reading ahead sequences stored in a file using the POSIX asynchronous I/O API \cite tpie_manual. These libraries also rely on uncontrolled operating system I/O caching and buffering in order to overlap I/O and computation in some way. However, this approach has significant performance penalties for accesses without locality. Unfortunately, the asynchronous I/O APIs are very different for different operating systems (e.g. POSIX AIO and Win32 Overlapped I/O). Therefore, we have introduced the AIO layer to make porting STXXL easy. Porting the whole library to a different platform requires only reimplementing the AIO layer using native file access methods and/or native multithreading mechanisms. \image html layer_diagram.png "The STXXL library structure" STXXL already has several implementations of the AIO layer which use different file access methods under POSIX/UNIX and Windows systems. Porting STXXL to Windows took only a few days. The main efforts were spent for writing the AIO layer using the native Windows calls. Rewriting the thread-related code was easy provided the Boost thread library; its interfaces are similar to POSIX threads. There were little header file and compiler-specific incompatibilities; those were solved by conditional compilation using the C++ preprocessor. The POSIX version of STXXL had run immediately on the all listed operating systems after changing some Linux-specific header file includes to more common POSIX headers. The Block Management layer (BM layer) provides a programming interface emulating the \b parallel disk model. The BM layer provides an abstraction for a fundamental concept in the external memory algorithm design --- a block of elements. The block manager implements block allocation/deallocation, allowing several block-to-disk assignment strategies: striping, randomized striping, randomized cycling, etc. The block management layer provides an implementation of parallel disk buffered writing \cite HutSanVit01b, optimal prefetching \cite HutSanVit01b, and block caching. The implementations are fully asynchronous and designed to explicitly support overlapping between I/O and computation. The top of STXXL consists of two modules. The STL-user layer provides external memory sorting, external memory stack, external memory priority queue, etc. which have (almost) the same interfaces (including syntax and semantics) as their STL counterparts. The Streaming layer provides efficient support for \b pipelining external memory algorithms. Many external memory algorithms, implemented using this layer, can save a factor of 2--3 in I/Os. For example, the algorithms for external memory suffix array construction implemented with this module \cite DKMS05 require only 1/3 of the number of I/Os which must be performed by implementations that use conventional data structures and algorithms (either from STXXL STL-user layer, LEDA-SM, or TPIE). The win is due to an efficient interface that couples the input and the output of the algorithm--components (scans, sorts, etc.). The output from an algorithm is directly fed into another algorithm as input, without needing to store it on the disk in-between. This generic pipelining interface is the first of this kind for external memory algorithms. \section aio_layer The Asynchronous I/O primitives Layer The purpose of the AIO layer is to provide a unified approach to asynchronous I/O. The layer hides details of native asynchronous I/O interfaces of an operating system. Studying the patterns of I/O accesses of external memory algorithms and data structures, we have identified the following functionality that should be provided by the AIO layer: - To issue read and write requests without having to wait for them to be completed. - To wait for the completion of a subset of issued I/O requests. - To wait for the completion of at least one request from a subset of issued I/O requests. - To poll the completion status of any I/O request. - To assign a callback function to an I/O request which is called upon I/O completion (asynchronous notification of completion status), with the ability to co-relate callback events with the issued I/O requests. The AIO layer exposes two user objects: \ref stxxl::file and \ref stxxl::request_ptr. Together with the I/O waiting functions \ref stxxl::wait_all, \ref stxxl::wait_any, and \ref stxxl::poll_any they provide the functionality mentioned above. Using a \ref stxxl::file object, the user can submit asynchronous read and asynchronous write requests (methods \ref stxxl::file::aread and stxxl::file::awrite). These methods return a \ref stxxl::request_ptr object which is used to track the status of the issued request. The AIO layer functions \ref stxxl::wait_all, \ref stxxl::wait_any, and \ref stxxl::poll_any facilitate tracking a set of \ref stxxl::request_ptr s. The last parameter of the methods \ref stxxl::file::aread and \ref stxxl::file::awrite is a reference to a callback function object (callback functor). The functor's \c operator()(request_ptr) method is called when the I/O request is completed. As a part of the AIO layer, the STXXL library provides various I/O performance counters (\ref stxxl::stats class). The class counts the number and the duration of the performed I/O operations as well as the transferred volume. Read and write operations are counted separately. STXXL also measures the time spent by the processing thread(s) waiting for the completions of I/Os (I/O wait time). This metric helps to evaluate the degree and the impact of overlapping between I/O and computation in an application. The following listing shows a simple example of how to use AIO objects to perform asynchronous I/O. All STXXL library objects are defined in the namespace stxxl. For convenience, in line 1 we bring all names from the STXXL namespace to the local scope. In Line 8 a file object \c myfile is constructed. \ref stxxl::syscall_file is an implementation of the STXXL \ref stxxl::file interface which uses UNIX/POSIX \c read and \c write system calls to perform I/O. The file named "storage" in the current directory is opened in read-only mode. In line 9 an asynchronous read of the 1 MB region of the file starting at position 0 is issued. The data will be read into the array \c mybuffer. When the read operation is completed, my_handler::operator() will be called with a pointer to the completed request. The execution stops at line 11 waiting for the completion of the issued read operation. Note that the work done in the function do_something1() is overlapped with reading. When the I/O is finished, one can process the read buffer (line 12) and free it (line 13). \code struct my_handler { // I/O completion handler void operator () (stxxl::request_ptr ptr) { std::cout << "Request '" << *ptr << "' completed." << std::endl; } }; char * mybuffer = new char[1024*1024]; // allocate 1MB buffer stxxl::syscall_file myfile("./storage", stxxl::file::RDONLY); stxxl::request_ptr myreq = myfile.aread(mybuffer, 0, 1024*1024, my_handler()); do_something1(); // do_something1() is overlapped with reading myreq->wait(); // wait for read completion do_something2(mybuffer);// process the read buffer delete [] mybuffer; // free the buffer \endcode \subsection aio_impl AIO Layer Implementations There are several implementation strategies for the STXXL AIO layer. Some asynchronous I/O related APIs (and underlying libraries implementing them) already exist. The most well known framework is POSIX AIO, which has an implementation on almost every UNIX/POSIX system. Its disadvantage is that it has only limited support for I/O completion event mechanism The Linux AIO kernel side implementation (http://freshmeat.net/projects/linux-aio/) of POSIX AIO does not have this deficit, but is not portable since it works under Linux only. The STXXL AIO layer follows a different approach. It does not rely on any asynchronous I/O API. Instead we use synchronous I/O calls running asynchronously in separate threads. For each file there is one read and one write request queue and one thread. The main thread posts requests (invoking \ref stxxl::file::aread() and \ref stxxl::file::awrite() methods) to the file queues. The thread associated with the file executes the requests in FIFO order. This approach is very flexible and it does not suffer from limitations of native asynchronous APIs. Our POSIX implementation of the AIO layer is based on POSIX threads and supports several Unix file access methods: the \c syscall method uses \c read and \c write system calls, the \c mmap method uses memory mapping (\c mmap and \c munmap calls), the \c sim_disk method simulates I/O timings of a hard disk provided a big internal memory. To avoid superfluous copying of data between the user and kernel buffer memory, the \c syscall method has the option to use unbuffered file system access. These file access methods can also be used for raw disk I/O, bypassing the file system. In this case, instead of files, raw device handles are open. The read/write calls using direct access (\c O_DIRECT option) have shown the best performance under Linux. The disadvantage of the \c mmap call is that programs using this method have less control over I/O: In most operating systems 4 KBytes data pages of a mmaped file region are brought to the main memory "lazily", only when they are accessed for the first time. This means if one mmaps a 100 KBytes block and touches only the first and the last element of the block then \b two I/Os are issued by the operating system. This will slow down many I/O-efficient algorithms, since for modern disks the seek time is much longer than the reading of 100 KBytes of contiguous data. The POSIX implementation does not need to be ported to other UNIX compatible systems, since POSIX threads is the standard threading API on all POSIX-compatible operating systems. Our Windows implementation is based on Boost threads, whose interfaces are very similar to POSIX threads. AIO file and request implementation classes are derived from the generic \ref stxxl::file and \ref stxxl::request interface classes with C++ pure virtual functions. These functions are specialized for each access method in implementation classes to define the read, write, wait for I/O completion and other operations. The desired access method implementation for a file is chosen dynamically at running time. One can add the support of an additional access method (e.g. for a DAFS distributed filesystem) just providing classes implementing the \ref stxxl::file and \ref stxxl::request interfaces. We have decided to use the virtual function mechanism in the AIO layer because this mechanism is very flexible and will not sacrifice the performance of the library, since the virtual functions of the AIO layer need to be called only once per \b large chunk of data (i.e. \a B bytes). The inefficiencies of C++ virtual functions are well known. Similar to STL, the higher layers of STXXL do not rely on the running time polymorphism with virtual functions to avoid the high per-element penalties. \section mng_layer The Block-Management Layer The Block-Management (BM) layer provides an implementation of the central concept in I/O efficient algorithms and data structures: a block of elements (\ref stxxl::typed_block object). Besides, it includes a toolbox for allocating, deallocating, buffered writing, prefetching, and caching of blocks. The external memory manager (object \ref stxxl::block_manager) is responsible for allocating and deallocating external memory space on disks. The manager supports four parallel disk allocation strategies: simple striping, fully randomized, simple randomized \cite BarGroVit97, and randomized cycling \cite VitHut01. The BM layer also delivers a set of helper classes that efficiently implement frequently used sequential patterns of interaction with the (parallel disk) external memory. The optimal parallel disk queued writing \cite HutSanVit01b is implemented in the \ref stxxl::buffered_writer class. The class operates on blocks. The \ref stxxl::buf_ostream class is build on top of \ref stxxl::buffered_writer and has a high level interface, similar to the interface of STL output iterators. Analogously, the classes \ref stxxl::block_prefetcher and \ref stxxl::buf_istream contain an implementation of an optimal parallel disk \b prefetching algorithm \cite HutSanVit01b. The helper objects of the BM layer support overlapping between I/O and computation, which means that they are able to perform I/O in the background, while the user thread is doing useful computations. The BM layer views external memory as a set of large AIO files --- one for each disk. We will refer to these files as \b disks. The other approach would be to map a related subset of blocks (e.g. those belonging to the same data structure) to a separate file. This approach has some performance problems. One of them is that since those (numerous) files are created dynamically, during the run of the program, the file system allocates the disk space on demand, that might in turn introduce severe uncontrolled disk space fragmentation. Therefore we have chosen the "one-large-file-per-disk" approach as our major scheme. However, the design of our library does not forbid data structures to store their content in separate user data files (e.g., as an option, \ref stxxl::vector can be mapped to a user file). The external memory manager (object \ref stxxl::block_manager) is responsible for allocating and deallocating external memory space on the disks. The \ref stxxl::block_manager reads information about available disks from the STXXL configuration file. This file contains the location of each disk file, the sizes of the disks, and the file access methods for each disk. When allocating a bunch of blocks, a programmer can specify how the blocks will be assigned to disks, passing an allocation strategy function object. The \ref stxxl::block_manager implements the "first-fit" allocation heuristic \cite BicShaw2003. When an application requests several blocks from a disk, the manager tries to allocate the blocks contiguously. This reduces the bulk access time. On allocation requests, the \ref stxxl::block_manager returns \ref stxxl::BID objects -- Block IDentifiers. An object of the type \ref stxxl::BID describes the physical location of an allocated block, including the disk and offset of a region of storage on disk. One can load or store the data that resides at the location given by the \ref stxxl::BID using asynchronous \c read and \c write methods of a \ref stxxl::typed_block object. The full signature of the STXXL "block of elements" class is \ref stxxl::typed_block The C++ template parameter RawSize defines the total size of the block in bytes. Since block size is not a single global constant in the STXXL namespace, a programmer can simultaneously operate with several block types having different blocks sizes. Such flexibility is often required for good performance. For example, B+-tree leaves might have a size different from the size of the internal nodes. We have made the block size a template parameter and not a member variable for the sake of efficiency. The values of the template parameters are known to the compiler, therefore for the power of two values (a very common choice) it can replace many arithmetic operations, like divisions and multiplications, by more efficient binary shifts. A critical requirement for many external memory data structures is that a block must be able to store links to other blocks. An STXXL block can store \c NRef objects of type \ref stxxl::BID. Additionally, one can equip a block with a field of the type \c InfoType, that can hold some per-block information. Block elements of type \c T can easily be accessed by the array operator [] and via random access iterators. The maximum number of elements available a block depends on the number of links and the sizes of \c T, \c InfoType and \c BID types. This number is accessible as \ref stxxl::typed_block::size. In the following listing, we give an example of how to program block I/O using objects of the BM layer. In line 2 we define the type of block: its size is one megabyte and the type of elements is \c double. The pointer to the only instance of the singleton object \ref stxxl::block_manager is obtained in line 5. Line 6 asks the block manager to allocate 32 blocks in external memory. The new_blocks call writes the allocated BIDs to the output iterator, given by the last parameter. The std::back_inserter iterator adapter will insert the output BIDs at the end of the array \c bids. The manager assigns blocks to disks in a round-robin fashion as the striping() strategy suggests. Line 7 allocates 32 internal memory blocks. The internal memory allocator \ref stxxl::new_alloc\ of STXXL allocates blocks on a virtual memory page boundary, which is a requirement for unbuffered file access. Along lines 8--10 the elements of blocks are filled with some values. Then, the blocks are submitted for writing (lines 11-12). The request objects are stored in an std::vector for the further status tracking. As in the AIO example, I/O is overlapped with computations in the function do_something(). After the completion of all write requests (line 15) we perform some useful processing with the written data (function do_something1()). Finally we free the external memory space occupied by the 32 blocks (line 18). \code typedef stxxl::typed_block<1024*1024,double> block_type; std::vector bids; //empty array of BIDs std::vector requests; stxxl::block_manager * bm = stxxl::block_manager::get_instance (); bm->new_blocks(32, stxxl::striping(), std::back_inserter(bids)); std::vector< block_type, new_alloc > blocks(32); for (int ii = 0; ii < 32; ii++) for (int jj=0; jj < block_type::size; jj++) blocks[ii][jj] = some_value(ii,jj); for (int i = 0; i < 32; i++) requests.push_back( blocks[i].write(bids[i]) ); do_something(); // do_something() is overlapped with writing // wait until all I/Os finish stxxl::wait_all(requests.begin(), requests.end()); do_something1(bids.begin(),bids.end()); // deallocate external memory bm->delete_blocks(bids.begin(), bids.end()); \endcode # The STL-User Layer The documentation of \subpage design_stl "The STL-User Layer" is on a separate subpage. # The Algorithm Pipelining Layer The \subpage design_pipeline "Streaming layer" provides efficient support for external memory algorithms with mostly \b sequential I/O pattern, i.e. scan, sort, merge, etc. A user algorithm, implemented using this module can save many I/Os. The win is due to an efficient interface, that couples the input and the output of the algorithms-components (scans, sorts, etc.). The output from an algorithm is directly fed into another algorithm as the input, without the need to store it on the disk. # Common Helpers and Utilities Beyond the layered library, STXXL contains many small helpers commonly used in C++ like random numbers or shared pointers. See \ref common for short descriptions. */ /** \page design_stl The STL-User Layer \author Roman Dementiev (2006) The STL layer of STXXL is composed of two large parts: containers and algorithms. - \subpage design_stl_containers "Containers" store elements, usually in external memory, but still follow the same interface as STL containers. See - \subpage design_stl_algo "Algorithms" operate, like STL algorithms, on iterators provided by the containers. */ /** \page design_stl_containers STXXL Containers \author Roman Dementiev (2006) \section General Issues Concerning STXXL Containers STXXL has the restriction that the data types stored in the containers cannot have C/C++ pointers or references to other elements of external memory containers. The reason is that these pointers and references get invalidated when the blocks containing the elements they point/refer to are written to disk. To get around this problem, the links can be kept in the form of external memory iterators (e.g. \ref stxxl::vector::iterator). The iterators remain valid while storing to and loading from the external memory. When dereferencing an external memory iterator, the pointed object is loaded from external memory by the library on demand (if the object is not in the cache of the data structure already). STXXL containers differ from STL containers in treating allocation and distinguishing between uninitialized and initialized memory. STXXL containers assume that the data types they store are plain old data types (POD). The constructors and destructors of the contained data types are not called when a container changes its size. The support of constructors and destructors would imply a significant I/O cost penalty, e.g. on the deallocation of a non-empty container, one has to load all contained objects and call their destructors. This restriction sounds more severe than it is, since external memory data structures cannot cope with custom dynamic memory management anyway, which is the common use of custom constructors/destructors. However, we plan to implement special versions of STXXL containers which will support not only PODs, and handle construction/destruction appropriately. # STXXL Containers STXXL library was designed to ease the access to external memory algorithms and data structures for a programmer. We decided to equip our implementations of out-of-memory data structure and algorithms with well known generic interfaces of internal memory data structures and algorithms from the Standard Template Library. Currently we have implementation of the following data structures (in STL terminology "containers"): - \subpage design_vector "stxxl::vector" - \subpage design_stack "stxxl::stack" - \subpage design_queue "stxxl::queue" - \subpage design_deque "stxxl::deque" - \subpage design_map "stxxl::map" - \subpage design_unordered_map "stxxl::unordered_map" Beyond these, STXXL also provides a set of containers that are not part of the STL: - \subpage design_pqueue "stxxl::priority_queue" - \subpage design_matrix "stxxl::matrix" - \subpage design_sorter "stxxl::sorter" - \subpage design_sequence "stxxl::sequence" */ /** \page design_vector Vector \author Roman Dementiev (2006) The most universal STXXL container is \ref stxxl::vector. Vector is an array whose size can vary dynamically. The implementation of \ref stxxl::vector is similar to the LEDA-SM array \cite CraMeh99. The content of a vector is striped block-wise over the disks, using an assignment strategy given as a template parameter. Some of the blocks are cached in a vector cache of fixed size (also a parameter). The replacement of cache blocks is controlled by a specified page-replacement strategy. STXXL has implementations of LRU and random replacement strategies. The user can provide his/her own strategy as well. The \ref stxxl::vector has STL-compatible Random Access Iterators. - One random access costs \f$ \mathcal{O}(1) \f$ I/Os in the worst case. Same for insertion and removal at the end. - Sequential scanning of the vector costs \f$ \mathcal{O}(1/DB) \f$ amortized I/Os per vector element. \section design_vector_architecture The Architecture of stxxl::vector The \ref stxxl::vector is organized as a collection of blocks residing on the external storage media (parallel disks). Access to the external blocks is organized through the fully associative \a cache which consist of some fixed amount of in-memory pages (The page is a collection of consecutive blocks. The number of blocks in the page is constant.). The schema of \ref stxxl::vector is depicted in the following figure. When accessing an element the implementation of \ref stxxl::vector access methods ([]operator, \c push_back, etc.) first checks whether the page to which the requested element belongs is in the vector's cache. If it is the case the reference to the element in the cache is returned. Otherwise the page is brought into the cache (If the page of the element has not been touched so far, this step is skipped. To keep an eye on such situations there is a special flag for each page.). If there was no free space in the cache, then some page is to be written out. Vector maintains a \a pager object, that tells which page to kick out. STXXL provides LRU and random paging strategies. The most efficient and default one is LRU. For each page vector maintains the \a dirty flag, which is set when \a non-constant reference to one of the page's elements was returned. The dirty flag is cleared each time when the page is read into the cache. The purpose of the flag is to track whether any element of the page is modified and therefore the page needs to be written to the disk(s) when it has to be evicted from the cache. \image html vector_architecture_small.png "The schema of stxxl::vector that consists of ten external memory pages and has a cache with the capacity of four pages. The first cache page is mapped to external page 1, the second page is mapped to external page 8, and the fourth cache page is mapped to page 5. The third page is not assigned to any external memory page." In the worst case scenario when vector elements are read/written in the random order each access takes 2 x blocks_per_page I/Os. The factor \a two shows up here because one has to write the replaced from cache page and read the required one). However the scanning of the array costs about \f$ n/B \f$ I/Os using constant vector iterators or const reference to the vector, where \a n is the number of elements to read or write (read-only access). Using non-const vector access methods leads to \f$ 2 \times n/B \f$ I/Os because every page becomes dirty when returning a non const reference. If one needs only to sequentially write elements to the vector in \f$ n/B \f$ I/Os the currently fastest method is \ref stxxl::generate. Sequential writing to an untouched before vector (e.g. when created using stxxl::vector(size_type n) constructor.) or alone adding elements at the end of the vector, using the push_back(const T\&) method, leads also to \f$ n/B \f$ I/Os. \code // Example of use stxxl::vector V; V.push_back(3); assert(V.size() == 1 && V.capacity() >= 1 && V[0] == 3); \endcode \section design_vector_generator stxxl::VECTOR_GENERATOR Besides the type of the elements stxxl::vector has many other template parameters (block size, number of blocks per page, pager class, etc.). To make the configuration of the vector type easier STXXL provides special type generator template meta programs for its containers. The meta-program for \ref stxxl::vector is called \ref stxxl::VECTOR_GENERATOR. \code // Example of use typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type V; V.push_back(3); assert(V.size() == 1 && V.capacity() >= 1 && V[0] == 3); \endcode The \ref stxxl::VECTOR_GENERATOR has the following template parameters: \copydetails stxxl::VECTOR_GENERATOR ### Notes: - All blocks of a page are read and written from/to disks together. Therefore to increase the I/O bandwidth, it is recommended to set the PageSize parameter to multiple of \a D. - Memory consumption of constructed vector is BlockSize x CachePages x PageSize bytes (see below). - The configured vector type is available as \ref VECTOR_GENERATOR<>::result. - Since there are defaults for the last five of the parameters, it is not necessary to specify them all. - Supported parallel disk assignment strategies: strategy | identifier ------------------ | ---------- striping | striping simple randomized | SR fully randomized | FR randomized cycling | RC - Supported paging strategies: strategy | identifier ------------------- | ------ random | random least recently used | lru ### Examples: - VECTOR_GENERATOR::result -- external vector of \b double's with four blocks per page, the cache with eight pages, 2 MiB blocks, Random Allocation and lru cache replacement strategy. - VECTOR_GENERATOR::result -- external vector of \b double's , with \b eight blocks per page, the cache with eight pages, 2 MiB blocks, Random Allocation and lru cache replacement strategy - VECTOR_GENERATOR::result -- external vector of \b double's, with \b eight blocks per page, the cache with \b two pages, \b 512 KiB blocks, Simple Randomized allocation and lru cache replacement strategy \section design_vector_memory Internal Memory Consumption of stxxl::vector The cache of \ref stxxl::vector largely dominates in its internal memory consumption. Other members consume very small fraction of \ref stxxl::vector's memory even when the vector size is large. Therefore, the internal memory consumption of \ref stxxl::vector can be estimated as \f$ BlockSize \times CachePages \times PageSize \f$ bytes. \section design_vector_notes More Notes - In opposite to STL, \ref stxxl::vector's iterators do not get invalidated when the vector is resized or reallocated. - Dereferencing a non-const iterator makes the page of the element to which the iterator points to \b dirty. This causes the page to be written back to the disks(s) when the page is to be kicked off from the cache (additional write I/Os). If you do not want this behavior, use const iterators instead. See following example: \code vector_type V; // ... fill the vector here vector_type::iterator iter = V.begin(); // ... advance the iterator a = *iter; // causes write I/Os, although *iter is not changed vector_type::const_iterator citer = V.begin(); // ... advance the iterator a = *citer; // read-only access, causes no write I/Os *citer = b; // does not compile, citer is const \endcode - Non const \c operator[] makes the page of the element \b dirty. This causes the page to be written back to the disks(s) when the page is to be kicked off from the cache (additional write I/Os). If you do not want this behavior, use const \c operator[]. For that you need to access the vector via a const reference to it. Example: \code vector_type V; // ... fill the vector here a = V[index]; // causes write I/Os, although V[index] is not changed const vector_type & CV = V; // const reference to V a = CV[index]; // read-only access, can cause no write I/Os CV[index] = b; // does not compile, CV is const \endcode This issue also concerns \c front() and \c back() methods. */ /** \page design_stack Stack \author Roman Dementiev (2006) The I/O efficient stack is perhaps the simplest external memory data structure. Stacks provide only restricted subset of sequence operations: insertion, removal, and inspection of the element at the top of the stack. Stacks are a "last in first out" (LIFO) data structures: the element at the top of a stack is the one that was most recently added. Stacks does not allow iteration through its elements. The basic variant of EM stack keeps the top \a k elements in the main memory buffer, where \f$ k \leq 2B \f$. If the buffers get empty on a removal call, one block is brought from the disk to the buffers. Therefore at least \a B removals are required to make one I/O reading a block. Insertions cause no I/Os until the internal buffers get full. In this case to make space the first \a B elements are written to the disk. Thus a block write happens only after at least \a B insertions. If we choose the unit of disk transfer to be a multiple of DB (we denote it as a \a page), set the stack buffer size to 2D pages, and evenly assign the blocks of a page to disks we obtain the following amortized running times of the basic operations of stxxl::stack operation | internal work | I/O (amortized) -------------------- | ---------------------- | ------------------------- insertion at the end | \f$ \mathcal{O}(1) \f$ | \f$ \mathcal{O}(1/DB) \f$ removal at the end | \f$ \mathcal{O}(1) \f$ | \f$ \mathcal{O}(1/DB) \f$ The STXXL library contains four different variants of stacks, each implementation is specialized for a certain access pattern: 1. The \ref stxxl::normal_stack is a general purpose implementation which is the best if the access pattern to the stack is an irregular mix of push'es and pop's, i.e. the stack grows and shrinks without a certain rule. 2. The \ref stxxl::grow_shrink stack is a stack that is optimized for an access pattern where the insertions are (almost) not intermixed with the removals, and/or vice versa, the removals are (almost) not intermixed with the insertions. In short, the stack first grows to its maximal size, then shrinks, then grow again and so on, a pattern which can be described by \f$ (push^{r_i}, push^{r_j})^k \f$ with \f$ 1 \leq j \leq k \f$ and large values for \f$ r_i \f$ and \f$ r_j \f$. 3. \ref stxxl::grow_shrink2 stack is a "grow-shrink" stack that allows the use of common prefetch and write buffer pools. The pools are shared between several "grow-shrink" stacks. 4. \ref stxxl::migrating stack is a stack that migrates from internal memory to external memory when its size exceeds a certain threshold (determined by parameter migrating_critical_size) To make use of stxxl::stack, one can use the generator template \ref stxxl::STACK_GENERATOR which expects the parameters from left to right as shown in the table below. \section design_stack_normal stxxl::normal_stack The \ref stxxl::normal_stack is a general purpose implementation of the external memory stack. The stack has two pages, the size of the page in blocks is a configuration constant and can be given as a template parameter. The implementation of the methods follows the description given in the previous section. ## Internal Memory Consumption of stxxl::normal_stack The cache of \ref stxxl::normal_stack largely dominates in its internal memory consumption. Other members consume very small fraction of \ref stxxl::normal_stack's memory even when the stack size is large. Therefore, the internal memory consumption of \ref stxxl::normal_stack can be estimated as \f$ 2 \times BlockSize \times PageSize \f$ bytes, where \f$ BlockSize \f$ is the block size and \f$ PageSize \f$ is the page size in blocks (see \ref design_stack). \section design_stack_grow_shrink stxxl::grow_shrink_stack The \ref stxxl::grow_shrink_stack specialization is optimized for an access pattern where the insertions are (almost) not intermixed with the removals, and/or vice versa, the removals are (almost) not intermixed with the insertions. In other words the stack first grows to its maximal size, then it shrinks, then it might again grow, then shrink, and so forth, i.e. the pattern is \f$ (push^{i_j}pop^{r_j})^k \f$, where \f$ k \in N \f$, \f$ 1 \leq j \leq k \f$, and \f$ i_j \f$, \f$ r_j \f$ are \a large. The implementation efficiently exploits the knowledge of the access pattern that allows \b prefetching the blocks beforehand while the stack shrinks and buffered writing while the stack grows. Therefore the \b overlapping of I/O and computation is possible. ## Internal Memory Consumption of stxxl::grow_shrink_stack The cache of \ref stxxl::grow_shrink_stack largely dominates in its internal memory consumption. Other members consume very small fraction of \ref stxxl::grow_shrink_stack's memory even when the stack size is large. Therefore, the internal memory consumption of stxxl::grow_shrink_stack can be estimated as \f$ 2 \times BlockSize \times PageSize \f$ bytes, where \f$ BlockSize \f$ is the block size and \f$ PageSize \f$ is the page size in blocks (see \ref design_stack). ## Members of stxxl::grow_shrink_stack The \ref stxxl::grow_shrink_stack has the same set of members as the \ref stxxl::normal_stack. The running times of \ref stxxl::grow_shrink_stack are the same as \ref stxxl::normal_stack except that when the stack switches from growing to shrinking (or from shrinking to growing) \f$ PageSize \f$ I/Os can be spent additionally in the worst case. (This is for the single disk setting, if the page is perfectly striped over parallel disk the number of I/Os is \f$ PageSize \cdot D \f$.) \section design_stack_grow_shrink2 stxxl::grow_shrink_stack2 The \ref stxxl::grow_shrink_stack2 is optimized for the same kind of access pattern as \ref stxxl::grow_shrink_stack. The difference is that each instance of \ref stxxl::grow_shrink_stack uses an own internal buffer to overlap I/Os and computation, but \ref stxxl::grow_shrink_stack2 is able to share the buffers from the pool used by several stacks. ## Internal Memory Consumption of stxxl::grow_shrink_stack2 Not counting the memory consumption of the shared blocks from the pools, the stack alone consumes about \f$ BlockSize \f$ bytes. It has a cache that consists of only a single block. ## Members of stxxl::grow_shrink_stack2 The \ref stxxl::grow_shrink_stack2 has almost the same set of members as the \ref stxxl::normal_stack, except that it does not have the default constructor. The \ref stxxl::grow_shrink_stack2 requires prefetching and write pool objects (see \ref stxxl::prefetch_pool, \ref stxxl::write_pool and \ref stxxl::read_write_pool) to be specified in the creation time. Consequently, the constructor requires a read_write_pool for prefetching and buffered writing. But it also has a second parameter, which tells how many blocks from the prefetching pool are used, this is called "prefetch_aggressiveness". \section design_stack_grow_migrating stxxl::migrating_stack The \ref stxxl::migrating_stack is a stack that migrates from internal memory to external when its size exceeds a certain threshold (template parameter). The implementation of internal and external memory stacks can be arbitrary and given as a template parameters. ## Internal Memory Consumption of stxxl::migrating_stack The \ref stxxl::migrating_stack memory consumption depends on the memory consumption of the stack implementations given as template parameters. The current state is internal (external), the \ref stxxl::migrating_stack consumes almost exactly the same space as internal (external) memory stack implementation. (The \ref stxxl::migrating_stack needs only few pointers to maintain the switching from internal to external memory implementations.) ## Members of stxxl::migrating_stack The \ref stxxl::migrating_stack extends the member set of \ref stxxl::normal_stack. Additionally, there are \ref stxxl::migrating_stack::internal() and \ref stxxl::migrating_stack::external(), which return true if the currently used implementation is internal or external. \section design_stack_generator stxxl::STACK_GENERATOR To provide an easy way to choose and configure the stack implementations, STXXL offers a template meta program called \ref stxxl::STACK_GENERATOR. The \ref stxxl::STACK_GENERATOR has the following template parameters: \copydetails stxxl::STACK_GENERATOR ## Examples: - STACK_GENERATOR::result external stack of \c double's - STACK_GENERATOR::result internal stack of \c double's - STACK_GENERATOR::result external grow-shrink stack of \c double's - STACK_GENERATOR::result migrating grow-shrink stack of \c double's, internal implementation is \c std::stack - STACK_GENERATOR::result migrating grow-shrink stack of \c double's with 1 block per page and block size 512 KiB (total memory occupied = 1 MiB) ## Example for stxxl::grow_shrink_stack2 TODO-df : but use read_write_pool instead of the two pools. \code typedef STACK_GENERATOR::result stack_type; typedef stack_type::block_type block_type; stxxl::prefetch_pool p_pool(10); // 10 read buffers stxxl::write_pool w_pool(6); // 6 write buffers stack_type S(p_pool,w_pool,0); // no read buffers used for(long long i = 0; i < max_value; ++i) S.push(i); S.set_prefetch_aggressiveness(5); /* give a hint that we are going to shrink the stack from now on, always prefetch 5 buffers beforehand */ for(long long i = 0; i < max_value; ++i) S.pop(); S.set_prefetch_aggressiveness(0); // stop prefetching \endcode */ /** \page design_queue Queue \author Roman Dementiev (2006) STXXL also has an implementation of external memory FIFO \ref stxxl::queue. Its design is similar to \ref stxxl::grow_shrink_stack2. The implementation holds the head and the tail blocks in the main memory. Prefetch and write block pools might be used to overlap I/O and computation during \ref stxxl::queue operations. */ /** \page design_deque Deque \author Roman Dementiev (2006) The STXXL implementation of external memory \ref stxxl::deque is an adaptor of an (external memory) vector. This implementation wraps the elements around the end of the vector \b circularly. It provides the pop/push operations from/to the both ends of the \ref stxxl::deque in \f$ \mathcal{O}(1/DB) \f$ amortized I/Os if parameterized with a properly configured \ref stxxl::vector. */ /** \page design_pqueue Priority Queue \author Roman Dementiev (2006) A priority queue is a data structure that provides a restricted subset of container functionality: it provides insertion of elements, and inspection and removal of the top element. It is guaranteed that the top element is the largest element in the priority queue, where the function object Cmp is used for comparisons. Priority queues do not allow iteration through its elements. External memory priority queues are the central data structures for many I/O efficient graph algorithms (\cite ZehPhd \cite ChiEtAl95 \cite MSS03). The main technique in these algorithms is time-forward processing (\cite ChiEtAl95 \cite Arg95), easily realizable by an I/O efficient priority queue. I/O efficient priority queues also find application in large-scale discrete event simulation and online sorting. The STXXL implementation of priority queues is based on \cite San00b. An operation of this priority queue, called sequence heap, takes \f$ \mathcal{O}(\frac{1}{B}\log_{M/B}(I/B)) \f$ amortized I/Os, where \f$ I \f$ is the total number of insertions into the priority queue. This queue needs less than a third of I/Os used by other similar cache (I/O) efficient priority queues (e.g. \cite Brengel00 \cite FJKT97). The amortized run time of the STXXL priority queue are operation | internal work | I/O (amortized) --------- | --------------------------- | ------------------------ insertion | \f$ \mathcal{O}(\log I) \f$ | \f$ \mathcal{O}(1/B) \f$ deletion | \f$ \mathcal{O}(\log I) \f$ | \f$ \mathcal{O}(1/B) \f$ where \f$ I \f$ is the number of performed operations. A sequence heap maintains \a R merge groups \f$ G_1,\ldots, G_R \f$ where \f$ G_i \f$ holds up to \a k sorted sequences of size up to \f$ m k^{i-1} \f$, \f$ m << M \f$, see following figure. When group \f$ G_i \f$ overflows, all its sequences are merged, and the resulting sequence is put into group \f$ G_{i+1} \f$. Each group is equipped with a group buffer of size \a m to allow batched deletion from the sequences. The smallest elements of these buffers are deleted in small batches and stored in the deletion buffer. The elements are first inserted into the insertion priority queue. On deletion, one checks the minimum elements stored in the insertion priority queue and the deletion buffer. The difference of our implementation to \cite San00b is that a number of larger merge groups are explicitly kept in external memory. The sorted sequences in those groups only hold their \b first blocks in the main memory. The implementation supports parallel disks and overlaps I/O and computation. As in \cite San00b, the internal merging is based on loser trees \cite Knu98. However, our implementation does not use \b sentinel elements. \image html san00b_pqueue_small.png "The structure of STXXL priority queue" \section design_pqueue_generator stxxl::PRIORITY_QUEUE_GENERATOR Since the \ref stxxl::priority_queue has many setup parameters (internal memory buffer sizes, arity of mergers, number of internal and external memory merger groups, etc.) which are difficult to guess, STXXL provides a helper meta template program that searches for the optimum settings for user demands. This is necessary to limit the amount of main memory occupied by the different buffers of the data structure. The program is called \ref stxxl::PRIORITY_QUEUE_GENERATOR. The \ref stxxl::PRIORITY_QUEUE_GENERATOR has the following template parameters: \copydetails stxxl::PRIORITY_QUEUE_GENERATOR ## Notes - If \c CompareType(x,y) is true, then x is smaller than y. The element returned by \c Q.top() is the largest element in the priority queue. That is, it has the property that, for every other element \b x in the priority queue, \c CompareType(Q.top(),x) is false. \c CompareType must also provide \c min_value() method, that returns value of type ValueType that is smaller than any element of the queue \b x, i.e. \c CompareType(CompareType.min_value(),x) is always \b true.
    Example: comparison object for priority queue where \c top() returns the \b smallest contained integer: \code struct CmpIntGreater { bool operator () (const int & a, const int & b) const { return a > b; } int min_value() const { return std::numeric_limits::max(); } }; \endcode Example: comparison object for priority queue where \c top() returns the \b largest contained integer: \code struct CmpIntLess { bool operator () (const int & a, const int & b) const { return a < b; } int min_value() const { return std::numeric_limits::min(); } }; \endcode Note that \c CompareType must define strict weak ordering. (see what it is) - stxxl::PRIORITY_QUEUE_GENERATOR is template meta program that searches for \b 7 configuration parameters of \b stxxl::priority_queue that both minimize internal memory consumption of the priority queue to match \c IntMemory and maximize performance of priority queue operations. Actual memory consumption might be larger (use \ref stxxl::priority_queue::mem_cons() method to track it), since the search assumes rather optimistic schedule of push'es and pop'es for the estimation of the maximum memory consumption. To keep actual memory requirements low increase the value of \c MaxItems parameter. - For functioning a priority queue object requires two pools of blocks (See constructor of \ref stxxl::priority_queue). To construct STXXL block pools you might need \b block_type that will be used by priority queue. Note that block's size and hence it's type is generated by the \ref stxxl::PRIORITY_QUEUE_GENERATOR in compile type from \c IntMemory, \c MaxItems and \c sizeof(ValueType) and not given directly by user as a template parameter. Block type can be extracted as \ref stxxl::PRIORITY_QUEUE_GENERATOR<>::result::block_type (see \ref tutorial_pqueue). - The configured priority queue type is available as \ref stxxl::PRIORITY_QUEUE_GENERATOR<>::result. \section design_pqueue_memory Internal Memory Consumption of stxxl::priority_queue Internal memory consumption of stxxl::priority_queue is bounded by the IntMemory template parameter in most situations. */ /** \page design_map Map (B+-tree) \author Roman Dementiev (2006) \ref stxxl::map is an STL interface for search trees with unique keys. Our implementation of \ref stxxl::map is a variant of a B+-tree data structure \cite BM72 supporting the operations \c insert, \c erase, \c find, \c lower_bound and \c upper_bound in optimal \f$ \mathcal{O}(\log_{B}(n)) \f$ I/Os. Operations of \ref stxxl::map use \a iterators to refer to the elements stored in the container, e.g. \c find and \c insert return an iterator pointing to the data. Iterators are used for range queries: an iterator pointing to the smallest element in the range is returned by \c lower_bound, the element which is next to the maximum element in the range is returned by \c upper_bound. Scanning through the elements of the query can be done by using \c operator++ or \c operator-- of the obtained iterators in \f$ \mathcal{O}(R/B) \f$ I/Os, where \a R is the number of elements in the result. Our current implementation does not exploit disk parallelism. The flexibility of the iterator-based access has some circumstances for an external memory implementation: iterators must return correct data after reorganizations in the data structure even when the pointed data is moved to a different external memory block. The way how iterators are used for accessing a \ref stxxl::map is similar to the use of database cursors \cite BDB99. STXXL is the first C++ template library that provides an I/O-efficient search tree implementation with iterator-based access. In the following we briefly describe the architecture of the STXXL B+-tree implementation. A simplified UML class diagram of the implementation is depicted in the figure below. Our design allows to use different implementations for leaves and (internal) nodes. For example, leaves could be represented internally as sparse arrays \cite BDIW02 (currently, only the classic sorted array implementation is available). Leaves and nodes can have different external memory block sizes. Each leaf has links to the predecessor and successor leaves to speed up scanning. Our B+-tree is able to prefetch the neighbor leaves when scanning, obtaining a higher bandwidth by overlapping I/O and computation. The root node is always kept in the main memory and implemented as an std::map. To save I/Os, the most frequently used nodes and leaves are cached in corresponding node and leaf \a caches that are implemented in a single template class. An iterator keeps the block identifier (BID) of the external block where the pointed data element is contained, the offset of the data element in the block and a pointer to the B+-tree. In case of reorganizations of the data in external memory blocks (rebalancing, splitting, fusing blocks), all iterators pointing to the moved data have to be updated. For this purpose, the addresses of all instantiated iterators are kept in the iterator map object. The iterator map facilitates fast accounting of iterators, mapping BID and block offsets of iterators to its main memory addresses using an \a internal memory search tree. Therefore, the number of "alive" B+-tree iterators must be kept reasonably small. The parent pointers in leaves and nodes can be useful for finger search (The program can help the search tree finding an element by giving some "position close by" which was determined by an earlier search.) and insertions using a finger, however, that would require to store the whole B-tree path in the iterator data structure. This might make the iterator accounting very slow, therefore we do not maintain the parent links. The implementation can save I/Os when const_iterators are used: no flushing of supposedly changed data is needed (e.g. when scanning or doing other read-only operations). Our implementation of B+-tree supports bulk bottom-up construction from the presorted data given by an iterator range in \f$ \mathcal{O}(n/B) \f$ I/Os. \image html btree_uml_small.png "The simplified UML class diagram of the B+-tree implementation." */ /** \page design_unordered_map Unordered Map There is currently no documentation here, see the tutorial \ref tutorial_unordered_map for some notes. The unordered_map/hash map library was created as a student project at the University of Karlsruhe. If you can read German, contact the maintainers for a copy of the student thesis. */ /** \page design_matrix Matrix Currently no documentation here. The matrix library was created as a student project and documented in the following thesis: http://algo2.iti.kit.edu/english/1919.php */ /** \page design_sequence Sequence The \ref stxxl::sequence container is an external memory deque without facilities for random access. One can push and pop on both ends of an \ref stxxl::sequence using the functions push_back(), pop_back, push_front() and pop_front. Only one block at each end is guaranteed to be in memory, but the sequence will automatically prefetch and overlap writes when many equal calls are done. There are no operator[] or other random access methods for the sequence. The only methods to access all elements are via streams: the sequence can be streamed from front-to-back and from back-to-front (in reverse). The functions \ref sequence::get_stream() and \ref sequence::get_reverse_stream() are convenience access to these streams. */ /** \page design_sorter Sorter The \ref stxxl::sorter is a "sorting container": a set of items can be inserted in arbitrary order, and after all are inserted, the set can be read as a sorted stream. See the \ref tutorial_sorter tutorial for more information. */ /** \page design_stl_algo STXXL Algorithms \author Roman Dementiev (2006) Iterators of \ref stxxl::vector are STL compatible. \ref stxxl::vector::iterator is a model of Random Access Iterator concept from STL. Therefore it is possible to use the \ref stxxl::vector iterator ranges with STL algorithms. However, such use is not I/O efficient if an algorithm accesses the sequence in a random order. For such kind of algorithms STXXL provides I/O efficient implementations described on this page. If an algorithm does only a scan (or a constant number of scans) of a sequence (or sequences) the implementation that calls STL algorithm is nevertheless I/O efficient. However, one can save constant factors in I/O volume and internal work if the the access pattern is known (read-only or write-only scan for example). This knowledge is used in STXXL specialized implementations of STL algorithms. Example: STL Algorithms Running on STXXL containers (do not do this, read below!) \code typedef stxxl::VECTOR_GENERATOR::result vector_type; // Replace every number in an array with its negative. const int N = 1000000000; vector_type A(N); std::iota(A.begin(), A.end(), 1); std::transform(A, A+N, A, negate()); // Calculate the sum of two vectors, // storing the result in a third vector. const int N = 1000000000; vector_type V1(N); vector_type V2(N); vector_type V3(N); std::iota(V1.begin(), V1.end(), 1); std::fill(V2.begin(), V2.end(), 75); assert(V2.size() >= V1.size() && V3.size() >= V1.size()); std::transform(V1.begin(), V1.end(), V2.begin(), V3.begin(), plus()); \endcode The algorithms of the STL can be divided into two groups by their memory access pattern: scanning algorithms and random access algorithms. # Scanning Algorithms Scanning algorithms work with Input, Output, Forward, and Bidirectional iterators only. Since random access operations are not allowed with these kinds of iterators, the algorithms inherently exhibit a strong spatial locality of reference. STXXL containers and their iterators are STL-compatible, therefore one can directly apply STL scanning algorithms to them, and they will run I/O-efficiently (see the use of \c std::generate and \c std::unique algorithms in the listing above). Scanning algorithms are the majority of the STL algorithms (62 out of 71). STXXL also offers specialized implementations of some scanning algorithms, which perform better in terms of constant factors in the I/O volume and internal CPU work. These implementations benefit from accessing lower level interfaces of the BM layer instead of using iterator interfaces, resulting in a smaller CPU overhead. Being aware of the sequential access pattern of the applied algorithm, the STXXL implementations can do prefetching and use queued writing, thereby leading to the overlapping of I/O with computation. STXXL provides the following scanning algorithms: - \subpage design_algo_generate - \subpage design_algo_foreach - \subpage design_algo_foreachm - \subpage design_algo_find # Random Access Algorithms Random access algorithms require random access iterators, hence may perform (many) random I/Os. For such algorithms, STXXL provides specialized I/O efficient implementations that work with STL-user layer external memory containers. Currently, the library provides two implementations of sorting: - an std::sort-like sorting routine: \subpage design_algo_sort "stxxl::sort", and - a sorter that exploits integer keys -- \subpage design_algo_ksort "stxxl::ksort". Both sorters are implementations of parallel disk algorithms described in \subpage design_algo_sorting \cite DemSan03. */ /** \page design_algo_sorting Parallel Disk Sorting \author Roman Dementiev (2006) The development of STXXL has been started with sorting, because it is \c the fundamental tool for I/O-efficient processing of large data sets. Therefore, an efficient implementation of sorting largely defines the performance of an external memory software library as a whole. To achieve the best performance our implementation \cite DemSan03 uses parallel disks, has an optimal I/O volume \f$ \mathcal{O}(\frac{N}{DB}\log_{M/B}\frac{N}{B}) \f$ (that matches the lower bound), and guarantees almost perfect overlap between I/O and computation. No previous implementation has all these properties, which are needed for a good practical sorting. LEDA-SM \cite CraMeh99 and TPIE \cite APV02 concentrate on single disk implementations. For the overlapping of I/O and computation they rely on prefetching and caching provided by the operating system, which is suboptimal since the system knows little about the application's access pattern. Barve and Vitter implemented a parallel disk algorithm \cite BarGroVit97 that can be viewed as the immediate ancestor of our algorithm. Innovations with respect to our sorting are: A different allocation strategy that enables better theoretical I/O bounds \cite HutSanVit01b \cite KalVar01; a prefetching algorithm that optimizes the number of I/O steps and never evicts data previously fetched; overlapping of I/O and computation; a completely asynchronous implementation that reacts flexibly to fluctuations in disk speeds; and an implementation that sorts many GBytes and does not have to limit internal memory size artificially to obtain a nontrivial number of runs. Additionally, our implementation is not a prototype, it has a generic interface and is a part of the software library STXXL. Algorithms in \cite Raj98 \cite ChaCor02 \cite ChaCorWis01 have the theoretical advantage of being deterministic. However, they need three passes over data even for not too large inputs. Prefetch buffers for disk load balancing and overlapping of I/O and computation have been intensively studied for external memory merge sort (\cite PaiVar92 \cite CaoFelKarLi96 \cite AlbGarLeo98 \cite HutSanVit01b \cite KalVar01 \cite KimKar00). But we have not seen results that guarantee overlapping of I/O and computation during the parallel disk merging of arbitrary runs. There are many good practical implementations of sorting (e.g. \cite NBCGL94 \cite Aga96 \cite NKG00 \cite Wyl99) that address parallel disks, overlapping of I/O and computation, and have a low internal overhead. However, we are not aware of fast implementations that give theoretical performance guarantees on achieving asymptotically optimal I/O. Most practical implementations use a form of striping that requires \f$ \Omega(\frac{N}{DB}\log_{\Theta(\frac{M}{DB})}\frac{N}{B}) \f$ I/Os rather than the optimal \f$ \Theta(\frac{N}{DB}\log_{\Theta(M/B)}\frac{N}{B}) \f$. This difference is usually considered insignificant for practical purposes. However, already on our experimental system we have to go somewhat below the block sizes that give the best performance if the input size is 128~GBytes. Another reduction of the block size by a factor of eight (we have eight disks) could increase the run time significantly. We are also not aware of high performance implementations that guarantee overlap of I/O and computation during merging for inputs such as the one described in \ref design_algo_sorting_merging. On the other hand, many of the practical merits of our implementation are at least comparable with the best current implementations: We are close to the peak performance of our system. \section design_algo_sorting_overlapping Multi-way Merge Sort with Overlapped I/Os Perhaps the most widely used external memory sorting algorithm is k-way merge sort: During run formation, chunks of \f$ \Theta(M) \f$ elements are read, sorted internally, and written back to the disk as sorted \a runs. The runs are then merged into larger runs until only a single run is left. \f$ k = \mathcal{O}(M/B) \f$ runs can be sorted in a single pass by keeping up to \a B of the smallest elements of each run in internal memory. Using randomization, prediction of the order in which blocks are accessed, a prefetch buffer of \f$ \mathcal{O}(D) \f$ blocks, and an optimal prefetching strategy, it is possible to implement k-way merging using \a D disks in a load balanced way \cite HutSanVit01b. However, the rate at which new blocks are requested is more difficult to predict so that this algorithm does not guarantee overlapping of I/O and computation. In this section, we derive a parallel disk algorithm that compensates these fluctuations in the block request rate by a FIFO buffer of \f$ k+\Theta(D) \f$ blocks. \subsection design_algo_sorting_runform Run Formation There are many ways to overlap I/O and run formation. We start with a very simple method that treats internal sorting as a black box and therefore can use the fastest available internal sorters. Two threads cooperate to build \a k runs of size \f$ M/2 \f$: \verbatim post a read request for runs 1 and 2 thread A: | thread B: for r:=1 to k do | for r:=1 to k-2 do wait until | wait until run r is read | run r is written sort run r | post a read for run r+2 post a write for run r | \endverbatim \image html overlapping_runformation_small.png "Overlapping I/O and computation during run formation." The figure above illustrates how I/O and computation is overlapped by this algorithm. Formalizing this figure, we can prove that using this approach an input of size \a N can be transformed into sorted runs of size \f$ M/2 - \mathcal{O}(DB) \f$ in time \f$ \max(2T_{\mathrm{sort}}(M/2)N/M,\frac{2LN}{DB}) + \mathcal{O}(\frac{LM}{DB}) \f$, where \f$ T_{\mathrm{sort}}(x) \f$ denotes the time for sorting \a x elements internally and where \a L is the time needed for a parallel I/O step. In \cite DemSan03 one can find an algorithm which generates longer runs of average length \f$ 2M \f$ and overlaps I/O and computation. \subsection design_algo_sorting_multiway Multi-way Merging We want to merge \a k sorted sequences comprising \a N' elements stored in \f$ N'/B \f$ blocks (In practical situations, where a single merging phase suffices, we will have \f$ N'=N \f$). In each iteration, the merging thread chooses the smallest remaining element from the \a k sequences and hands it over to the I/O thread. Prediction of read operations is based on the observation that the merging thread does not need to access a block until its smallest element becomes the smallest unread element. We therefore record the \b smallest keys of each block during run formation. By merging the resulting \a k sequences of smallest elements, we can produce a sequence \f$ \sigma \f$ of block identifiers that indicates the exact order in which blocks are logically read by the merging thread. The overhead for producing and storing the prediction data structure is negligible because its size is a factor at least \a B smaller than the input. The prediction sequence \f$ \sigma \f$ is used as follows. The merging thread maintains the invariant that it always buffers the \a k first blocks in \f$ \sigma \f$ that contain unselected elements, i.e., initially, the first \a k blocks from \f$ \sigma \f$ are read into these merge buffers. When the last element of a merge buffer block is selected, the now empty buffer frame is returned to the I/O thread and the next block in \f$ \sigma \f$ is read. The keys of the smallest elements in each buffer block are kept in a tournament tree data structure \cite Knu98 so that the currently smallest element can be selected in time \f$ \mathcal{O}(\log k) \f$. Hence, the total internal work for merging is \f$ \mathcal{O}(N'\log k) \f$. We have now defined multi-way merging from the point of view of the sorting algorithm. Our approach to merging slightly deviates from previous approaches that keep track of the run numbers of the merge blocks and pre-assign each merge block to the corresponding input sequence. In these approaches also the \b last key in the \b previous block decides about the position of a block in \f$ \sigma \f$. The correctness of our approach is shown in \cite DemSan03. With respect to performance, both approaches should be similar. Our approach is somewhat simpler, however --- note that the merging thread does not need to know anything about the \a k input runs and how they are allocated. Its only input is the prediction sequence \f$ \sigma \f$. In a sense, we are merging individual blocks and the order in \f$ \sigma \f$ makes sure that the overall effect is that the input runs are merged. A conceptual advantage is that data \b within a block decides about when a block is read. \subsection design_algo_sorting_merging Overlapping I/O and Merging \image html overlapping_merging_small.png "Data flow of overlapped parallel disk multi-way merging." Although we can predict the order in which blocks are read, we cannot easily predict how much internal work is done between two reads. For example, consider \a k identical runs storing the sequence \f$ \fboxsep0.5mm\framebox{$1^{B-1}2$}\framebox{$3^{B-1}4$}\framebox{$5^{B-1}6$} \cdots \f$. After initializing the merge buffers, the merging thread will consume \f$ k(B-1) \f$ values '1' before it posts another read. Then it will post one read after selecting each of the next \a k values (2). Then there will be a pause of another \f$ k(B-1) \f$ steps and another \a k reads are following each other quickly, etc. We explain how to overlap I/O and computation despite this irregularity using the I/O model of Aggarwal and Vitter \cite AggVit88 that allows access to \a D \b arbitrary blocks within one I/O step. To model overlapping of I/O and computation, we assume that an I/O step takes time \a L and can be done in parallel with internal computations. We maintain an overlap buffer that stores up to \f$ k+3D \f$ blocks in a FIFO manner (see figure above). Whenever the overlap buffer is non-empty, a read can be served from it without blocking. Writing is implemented using a write buffer FIFO with \f$ 2DB \f$ elements capacity. An I/O thread inputs or outputs \a D blocks in time \a L using the following strategy: Whenever no I/O is active and at least \f$ DB \f$ elements are present in the write buffer, an output step is started. When no I/O is active, less than \a D output blocks are available, and at least \a D overlap buffers are unused, then the next \a D blocks from \f$ \sigma \f$ are fetched into the overlap buffer. This strategy guarantees that merging \a k sorted sequences with a total of \a N' elements can be implemented to run in time \f$ \max\left(\frac{2LN'}{DB}, \ell N'\right)+\mathcal{O}(L\lceil\frac{k}{D}\rceil) \f$ where \f$ \ell \f$ is the time needed by the merging thread to produce one element of output and \a L is the time needed to input or output \a D arbitrary blocks \cite DemSan03. \subsection design_algo_sorting_scheduling Disk Scheduling The I/Os for the run formation and for the output of merging are perfectly balanced over all disks if all sequences are \b striped over the disks, i.e., sequences are stored in blocks of \a B elements each and the blocks numbered \f$ i,\ldots,i+D-1 \f$ in a sequence are stored on different disks for all \a i. In particular, the original input and the final output of sorting can use any kind of striping. The merging algorithm presented above is optimal for the unrealistic model of Aggarwal and Vitter \cite AggVit88 which allows to access any \a D blocks in an I/O step. This facilitates good performance for fetching very irregularly placed input blocks. However, this model can be simulated using \a D independent disks using randomized striping allocation \cite VitHut01 and a prefetch buffer of size \f$ m = \Theta(D) \f$ blocks: In almost every input step, \f$ (1-\mathcal{O}(D/m))D \f$ blocks from prefetch sequence \f$ \sigma \f$ can be fetched \cite DemSan03. \section design_algo_sorting_impl Implementation Details Run Formation. We build runs of a size close to \f$ M/2 \f$ but there are some differences to the simple algorithm from \ref design_algo_sorting_runform. Overlapping of I/O and computation is achieved using the call-back mechanism supported by the I/O layer. Thus, the sorter remains portable over different operating systems with different interfaces to threading. We have two implementations with respect to the internal work: \ref stxxl::sort is a comparison based sorting using std::sort from STL to sort the runs internally; \ref stxxl::ksort exploits integer keys and has smaller internal memory bandwidth requirements for large elements with small key fields. After reading elements using DMA (i.e. the STXXL direct access), we extract pairs \f$ (\mathrm{key},\mathrm{pointerToElement}) \f$, sort these pairs, and only then move elements in sorted order to write buffers from where they are output using DMA. Furthermore, we exploit random keys. We use two passes of MSD (most significant digit) radix sort of the key-pointer pairs. The first pass uses the \a m most significant bits where \a m is a tuning parameter depending on the size of the processor caches and of the TLB (translation look-aside buffer). This pass consists of a counting phase that determines bucket sizes and a distribution phase that moves pairs. The counting phase is fused into a single loop with pair extraction. The second pass of radix sort uses a number of bits that brings us closest to an expected bucket size of two. This two-pass algorithm is much more cache efficient than a one-pass radix sort. (On our system we get a factor of 3.8 speedup over the one pass radix sort and a factor of 1.6 over STL's sort which in turn is faster than a hand tuned quicksort (for sorting \f$ 2^{21} \f$ pairs storing a random four byte key and a pointer). The remaining buckets are sorted using a comparison based algorithm: Optimal straight line code for \f$ n \leq 4 \f$, insertion sort for \f$ n \in \{ 5..16 \} \f$, and quicksort for \f$ n > 16 \f$. Multi-way Merging. We have adapted the tuned multi-way merger from \cite San00b, i.e. a tournament tree stores pointers to the current elements of each merge buffer. Overlapping I/O and Computation. We integrate the prefetch buffer and the overlap buffer to a read buffer. We distribute the buffer space between the two purposes of minimizing disk idle time and overlapping I/O and computation indirectly by computing an optimal prefetch sequence for a smaller buffer space. Asynchronous I/O. I/O is performed without any synchronization between the disks. The prefetcher computes a sequence \f$ \sigma' \f$ of blocks indicating the order in which blocks should be fetched. As soon as a buffer block becomes available for prefetching, it is used to generate an asynchronous read request for the next block in \f$ \sigma' \f$. The I/O layer of STXXL queues this request at the disk storing the block to be fetched. The thread for this disk serves the queued request in FIFO manner. All I/O is implemented without superfluous copying. STXXL opens files with the option \c O_DIRECT so that blocks are directly moved by DMA (direct memory access) to user memory. A fetched block then travels to the prefetch/overlap buffer and from there to a merge buffer simply by passing a pointer. Similarly, when an element is merged, it is directly moved from the merge buffer to the write buffer and a block of the write buffer is passed to the output queue of a disk simply by passing a pointer to the the I/O layer of STXXL that then uses \c write to output the data using DMA. */ /** \page design_algo_sort stxxl::sort -- Sorting Comparison-Based \author Roman Dementiev (2006) \ref stxxl::sort is an external memory equivalent to STL std::sort. The design and implementation of the algorithm is described in detail in \cite DemSan03. # Prototype \code template < typename ExtIterator, typename StrictWeakOrdering > void sort ( ExtIterator first, ExtIterator last, StrictWeakOrdering cmp, unsigned_type M ) \endcode # Description \copydetails stxxl::sort # Requirements on Types - \c ExtIterator is a model of External Random Access Iterator (In STXXL currently only \ref stxxl::vector provides iterators that are models of External Random Access Iterator.). - \c ExtIterator is mutable. - \c StrictWeakOrdering is a model of \ref StrictWeakOrdering - \c ExtIterator's value type is convertible to \c StrictWeakOrdering's argument type. \section StrictWeakOrdering StrictWeakOrdering Comparison Concept Model of \b StrictWeakOrdering Comparison concept must: - provide \b operator(a,b) that returns comparison result of two user types, must define strict weak ordering - provide \b max_value method that returns a value that is strictly greater than all other objects of user type, - provide \b min_value method that returns a value that is strictly less than all other objects of user type, - \b Note: when using unsigned integral types as key in user types, the value 0 cannot be used as a key value of the data to be sorted because it would conflict with the sentinel value returned by \b min_value - \b Note, that according to the \ref stxxl::sort requirements \c min_value() and \c max_value() can not be present in the input sequence. ## Examples A comparator class for integers: \b my_less_int. \code struct my_less_int { bool operator() (int a, int b) const { return a < b; } int min_value() const { return std::numeric_limits::min(); }; int max_value() const { return std::numeric_limits::max(); }; }; \endcode A comparator class \b my_less, that could be instantiated as e.g. \c my_less, \c my_less, etc. \code template struct my_less { typedef ValueType value_type; bool operator() (const value_type & a, const value_type & b) const { return a < b; } value_type min_value() const { return std::numeric_limits::min(); }; value_type max_value() const { return std::numeric_limits::max(); }; }; \endcode # Preconditions [first, last) is a valid range. # Complexity - Internal work: \f$ \mathcal{O}( N \log N ) \f$, where \f$ N = (last - first) \cdot \texttt{sizeof(ExtIterator::value\_type)} \f$. - I/O complexity: \f$ (2N/DB)(1 + \lceil {\log}_{M/B}(2N/M) \rceil) \f$ I/Os stxxl::sort chooses the block size (parameter \a B) equal to the block size of the container, the last and first iterators pointing to (e.g. stxxl::vector's block size). The second term in the I/O complexity accounts for the merge phases of the external memory sorting algorithm \cite DemSan03. Avoiding multiple merge phases speeds up the sorting. In practice one should choose the block size \a B$of the container to be sorted such that there is only one merge phase needed: \f$ \lceil {\log}_{M/B}(2N/M) \rceil) = 1 \f$. This is possible for \f$ M > DB \f$ and \f$ N < M^2/2DB \f$. But still this restriction gives a freedom to choose a variety of blocks sizes. The study \cite DemSan03 has shown that optimal \a B for sorting lies in the range \f$ [M^2/(4N),3M^2/(8N)] \f$. With such choice of the parameters the \ref stxxl::sort always performs \f$ 4N/DB \f$ I/Os. # Internal Memory Consumption The \ref stxxl::sort consumes slightly more than \a M bytes of internal memory. # External Memory Consumption The \ref stxxl::sort is not in-place. It requires about \a N bytes of external memory to store the sorted runs during the sorting process \cite DemSan03. After the sorting this memory is freed. # Example \code struct MyCmp: public std::less // ascending order { static int min_value() const { return std::numeric_limits::min(); } static int max_value() const { return std::numeric_limits::max(); } }; typedef stxxl::VECTOR_GENERATOR::result vec_type; vec_type V; // ... fill here the vector with some values // Sort in ascending order use 512 MiB of main memory stxxl::sort(V.begin(), V.end(), MyCmp(), 512*1024*1024); // sorted \endcode # Sorted Order Checking STXXL gives an ability to automatically check the order in the output of STXXL sorters and intermediate results of sorting (the order and a meta information in the sorted runs). The check is switched on if the source codes and the library are compiled with the option -DSTXXL_CHECK_ORDER_IN_SORTS and the option -DNDEBUG is not used. For details see the compiler.make file in the STXXL tar ball. Note, that the checking routines require more internal work as well as additional \f$ N/DB \f$ I/Os to read the sorted runs. Therefore for the final non-debug version of a user application on should switch this option off. This checker checks the \ref stxxl::sort, \ref stxxl::ksort, and the \ref design_stream_pipesort "pipelined sorter". */ /** \page design_algo_ksort stxxl::ksort -- Sorting Integer Keys \author Roman Dementiev (2006) \ref stxxl::ksort is a specialization of external memory sorting optimized for records having integer keys. # Prototype \code template < typename ExtIterator > void ksort ( ExtIterator first, ExtIterator last, unsigned_type M ) template < typename ExtIterator, typename KeyExtractor > void ksort ( ExtIterator first, ExtIterator last, KeyExtractor keyobj, unsigned_type M ) \endcode # Description \copydetails stxxl::ksort # Requirements on Types - \c ExtIterator is a model of External Random Access Iterator. (In STXXL currently only \ref stxxl::vector provides iterators that are models of External Random Access Iterator.) - \c ExtIterator is mutable. - \c KeyExtractor must implement \c operator() that extracts the key of an element and provide min and max values for the elements in the input, see \ref design_algo_ksort_key_extractor. - \c ExtIterator's value type is convertible to \c KeyExtractor's argument type. - \c ExtIterator's value type has a typedef \c key_type. - For the first version of \ref stxxl::ksort \c ExtIterator's value type must have a \c key() function that returns the key value of the element, and the \c min_value() and \c max\_value() member functions that return minimum and maximum element values respectively.
    Example: \code struct MyType { typedef unsigned long long key_type; key_type m_key; char m_data[32]; MyType() {} MyType(key_type k) : m_key(k) {} key_type key() { return m_key; } MyType min_value() const { return MyType( std::numeric_limits::min() ); } MyType max_value() const { return MyType( std::numeric_limits::max() ); } }; \endcode \section design_algo_ksort_key_extractor Key Extractor Concept A model of the Key Extractor concept must: - define type \b key_type for the type of the keys. - provide \b operator() that returns key value of an object of user type. - provide \b max_value method that returns a value that is strictly greater than all other objects of user type in respect to the key obtained by this key extractor, - provide \b min_value method that returns a value that is strictly less than all other objects of user type in respect to the key obtained by this key extractor, - operator >, operator <, operator == and operator != on type \b key_type must be defined. - \b Note: when using unsigned integral types as key, the value 0 cannot be used as a key value because it would conflict with the sentinel value returned by \c min_value. - \b Note, that according to the stxxl::sort requirements \c min_value and \c max_value can not be present in the input sequence. # Examples A key extractor object for ordering elements having 64 bit integer keys: \code struct MyType { typedef unsigned long long key_type; key_type m_key; char m_data[32]; MyType() {} MyType(key_type k) : m_key(k) {} }; struct GetKey { typedef MyType::key_type key_type; key_type operator() (const MyType & obj) { return obj.m_key; } MyType min_value() const { return MyType( std::numeric_limits::min() ); } MyType max_value() const { return MyType( std::numeric_limits::max() ); } }; \endcode A key extractor class \b GetWeight, that extracts weight from an \b Edge: \code struct Edge { unsigned src, dest, weight; Edge(unsigned s, unsigned d, unsigned w) : src(s), dest(d), weight(w) {} }; struct GetWeight { typedef unsigned key_type; key_type operator() (const Edge & e) const { return e.weight; } Edge min_value() const { return Edge(0, 0, std::numeric_limits::min()); } Edge max_value() const { return Edge(0, 0, std::numeric_limits::max()); } }; \endcode # Preconditions The same as for \ref design_algo_sort "stxxl::sort". # Complexity The same as for \ref design_algo_sort "stxxl::sort". # Internal Memory Consumption The same as for \ref design_algo_sort "stxxl::sort". # External Memory Consumption The same as for \ref design_algo_sort "stxxl::sort". # Example \code struct MyType { typedef unsigned long long key_type; key_type m_key; char m_data[32]; MyType() {} MyType(key_type k) : m_key(k) {} key_type key() { return obj.m_key; } static MyType min_value() const { return MyType( std::numeric_limits::min() ); } static MyType max_value() const { return MyType( std::numeric_limits::max()); } }; typedef stxxl::VECTOR_GENERATOR::result vec_type; vec_type V; // ... fill here the vector with some values // Sort in ascending order use 512 MiB of main memory stxxl::ksort(V.begin(), V.end(), 512*1024*1024); // sorted \endcode */ /** \page design_algo_generate stxxl::generate \author Roman Dementiev (2006) The semantics of the algorithm are equivalent to the STL std::generate. # Prototype \code template < typename ExtIterator, typename Generator > void generate ( ExtIterator first, ExtIterator last, Generator generator, int_type nbuffers ) \endcode # Description \copydetails stxxl::generate # Requirements on types - \c ExtIterator is a model of External Random Access Iterator. - \c ExtIterator is mutable. - \c Generator is a model of a STL Generator. - \c Generator's result type is convertible to \c ExtIterator's value type. # Preconditions [first, last) is a valid range. # Complexity - Internal work is linear. - External work: close to \f$ N/DB \f$ I/Os (write-only). # Example \code // Fill a vector with random numbers, using the // standard C library function rand. typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type V(some_size); // use 20 buffer blocks stxxl::generate(V.begin(), V.end(), rand, 20); \endcode */ /** \page design_algo_foreach stxxl::for_each \author Roman Dementiev (2006) The semantics of the algorithm is equivalent to the STL std::for_each. # Prototype \code template < typename ExtIterator, typename UnaryFunction > UnaryFunction for_each ( ExtIterator first, ExtIterator last, UnaryFunction functor, int_type nbuffers ) \endcode # Description \copydetails stxxl::for_each # Requirements on types - \c ExtIterator is a model of External Random Access Iterator. - \c UnaryFunction is a model of STL Unary Function. - \c UnaryFunction does not apply any non-constant operations through its argument. - \c ExtIterator's value type is convertible to \c UnaryFunction's argument type. # Preconditions [first, last) is a valid range. # Complexity - Internal work is linear. - External work: close to \f$ N/DB \f$ I/Os (read-only). # Example \code template struct print : public unary_function { print(ostream& out) : os(out), count(0) {} void operator() (T x) { os << x << ' '; ++count; } ostream& os; int count; }; typedef stxxl::VECTOR_GENERATOR::result vector_type; int main() { vector_type A(N); // fill A with some values // ... print P = stxxl::for_each(A.begin(), A.end(), print(cout)); cout << endl << P.count << " objects printed." << endl; } \endcode */ /** \page design_algo_foreachm stxxl::for_each_m (mutating) \author Roman Dementiev (2006) \ref stxxl::for_each_m is a \b mutating version of \ref stxxl::for_each, i.e. the restriction that Unary Function \c functor can not apply only constant operations through its argument does not exist. # Prototype \code template < typename ExtIterator, typename UnaryFunction > UnaryFunction for_each_m ( ExtIterator first, ExtIterator last, UnaryFunction functor, int nbuffers ) \endcode # Description \copydetails stxxl::for_each_m # Requirements on types - \c ExtIterator is a model of External Random Access Iterator. - \c UnaryFunction is a model of STL Unary Function. - \c ExtIterator's value type is convertible to \c UnaryFunction's argument type. # Preconditions [first, last) is a valid range. # Complexity - Internal work is linear. - External work: close to \f$ 2N/DB \f$ I/Os (read and write). # Example \code struct AddX { int x; AddX(int x_): x(x_) {} void operator() (int & val) { val += x; } }; typedef stxxl::VECTOR_GENERATOR::result vector_type; int main() { vector_type A(N); // fill A with some values ... // Add 5 to each value in the vector stxxl::for_each_m(A.begin(), A.end(), AddX(5)); } \endcode */ /** \page design_algo_find stxxl::find \author Roman Dementiev (2006) The semantics of the algorithm is equivalent to the STL std::find. # Prototype \code template < typename ExtIterator, typename EqualityComparable > ExtIterator find ( ExtIterator first, ExtIterator last, const EqualityComparable& value, int_type nbuffers ) \endcode # Description \copydetails stxxl::find # Requirements on types - \c EqualityComparable is a model of STL EqualityComparable concept. - \c ExtIterator is a model of External Random Access Iterator. - \c Equality is defined between objects of type \c EqualityComparable and objects of \c ExtIterator's value type. # Preconditions [first, last) is a valid range. # Complexity - Internal work is linear. - External work: close to \f$ N/DB \f$ I/Os (read-only). # Example \code typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type V; // fill the vector ... // find 7 in V vector_type::iterator result = find(V.begin(), V.end(), 7); if(result != V.end()) std::cout << "Found at position " << (result - V.begin()) << std::endl; else std::cout << "Not found" << std::endl; \endcode */ namespace stream { /** \page design_pipeline Algorithm Pipelining \author Roman Dementiev (2006) The pipelined processing technique is very well known in the database world \cite SKS01. This page describes the abstract design of the stream package, see also \ref tutorial_stream. Usually, the interface of an external memory algorithm assumes that it reads the input from (an) external memory container(s) and writes output into (an) external memory container(s). The idea of pipelining is to equip the external memory algorithms with a new interface that allows them to feed the output as a data stream directly to the algorithm that consumes the output, rather than writing it to the external memory first. Logically, the input of an external memory algorithm does not have to reside in the external memory, rather, it could be a data stream produced by another external memory algorithm. Many external memory algorithms can be viewed as a data flow through a directed acyclic graph \f$ G \f$ with node set \f$ V = F \cup S \cup R \f$ and edge set \f$ E \f$. The file nodes \f$ F \f$ represent physical data sources and data sinks, which are stored on disks (e.g. in the external memory containers of the STL-user layer). A file node writes or/and reads one stream of elements. The streaming nodes \f$ S \f$ read zero, one or several streams and output zero, one or several new streams. Streaming nodes are equivalent to scan operations in non-pipelined external memory algorithms. The difference is that non-pipelined conventional scanning needs a linear number of I/Os, whereas streaming nodes usually do not perform any I/O, unless a node needs to access external memory data structures (stacks, priority queues, etc.). The sorting nodes \f$ R \f$ read a stream and output it in a sorted order. Edges \f$ E \f$ in the graph \f$ G \f$ denote the directions of data flow between nodes. The question "When is a pipelined execution of the computations in a data flow graph \f$ G \f$ possible in an I/O-efficient way?" is analyzed in \cite DKMS05. \section design_pipeline_streaming Streaming Layer The streaming layer provides a framework for the \b pipelined processing of large sequences. Many external memory algorithms implemented with the STXXL streaming layer save a factor of at least two in I/Os. To the best of our knowledge we are the first who apply the pipelining method systematically in the domain of external memory algorithms. We introduce it in the context of an external memory software library. In STXXL, all data flow node implementations have an \c stream interface which is similar to the STL Input iterators (Not be confused with the stream interface of the C++ \c iostream library.). As an input iterator, an \c stream object may be dereferenced to refer to some object and may be incremented to proceed to the next object in the stream. The reference obtained by dereferencing is read-only and must be convertible to the \c value_type of the \c stream. The concept of the \c stream also defines a boolean member function \c empty() which returns \c true iff the end of the stream is reached. Now we tabulate the valid expressions and the expression semantics of the \c stream concept in the style of the STL documentation. ### Notation Symbol | Semantics | --------------------- | --------------------------------------------- | X, X1,...,Xn | A type that is a model of the stream | T | The value type of X | s, s1,...,sn | Object of type X, X1,...,Xn | t | Object of type T | ### Valid expressions Name | Expression | Type requirements | Return type | ------------------- | ----------------------- | ------------------------------------------------------------- | ------------------------- | Constructor | X s(s1,...,sn) | s1,....,sn are convertible to X1\&,...,Xn\& | | Dereference | *s | | Convertible to T | Member access | s->m | T is a type for which t.m is defined | | Preincrement | ++s | | X\& | End of stream check | (*s).empty() | | bool | ### Expression semantics Name | Expression | Precondition | Semantics | Postcondition | ------------- | ----------------------- | ----------------------------------------------------------- | --------------------------------| ------------------------------------------- | Constructor | X s(s1,...,sn) | s1,...,sn are the \a n input streams of s | | | Dereference | *s | s is incrementable | | | Member access | s->m | s is incrementable | Equivalent to (*s).m | | Preincrement | ++s | s is incrementable | | s is incrementable or past-the-end | The binding of a \c stream object to its input streams (incoming edges in a data flow graph \f$ G \f$) happens at compile time, i.e. statically. The other approach would be to allow binding at running time using the C++ virtual function mechanism. However this would result in a severe performance penalty because most C++ compilers are not able to inline virtual functions. To avoid this disadvantage, we follow the static binding approach using C++ templates. For example, assuming that streams s1,...,sn are already constructed, construction of stream \c s with constructor X::X(X1\& s1,..., Xn\& sn) will bind \c s to its inputs s1,...,sn. After creating all node objects, the computation starts in a "lazy" fashion, first trying to evaluate the result of the topologically latest node. The node reads its intermediate input nodes, element by element, using the dereference and increment operator of the \c stream interface. The input nodes proceed in the same way, invoking the inputs needed to produce an output element. This process terminates when the result of the topologically latest node is computed. This style of pipelined execution scheduling is I/O efficient, it allows to keep the intermediate results in-memory without needing to store them in external memory. The Streaming layer of the STXXL library offers generic classes which implement the functionality of sorting, file, and streaming nodes: - File nodes: Function streamify() serves as an adaptor that converts a range of ForwardIterators into a compatible \c stream. Since iterators of \ref stxxl::vector are RandomAccessIterators, streamify() can be used to read external memory. The set of (overloaded) materialize functions implement data sink nodes, they flush the content of a STXXL stream object to an output iterator. The library also offers specializations of streamify() and \ref stream::materialize for \ref stxxl::vector, which are more efficient than the generic implementations due to the support of overlapping between I/O and computation. - \anchor design_stream_pipesort Sort nodes: The Stream layer sort class is a generic pipelined sorter which has the interface of an \c stream. The input of the sorter may be an object complying to the \c stream interface. As the STL-user layer sorter, the pipelined sorter is an implementation of parallel disk merge sort \cite DemSan03 that overlaps I/O and computation. The implementation of \ref stream::sort relies on two classes that encapsulate the two phases of the algorithm: sorted run formation (class runs_creator) and run merging (runs_merger). The separate use of these classes breaks the pipelined data flow: the runs_creator must read the entire input to compute the sorted runs. This facilitates an efficient implementation of loops and recursions: the input for the next iteration or recursion can be the sorted runs stored on disks (\cite JensThesis \cite DKMS05). The templated class runs_creator has several specializations which have input interfaces different from the \c stream interface: a specialization where elements to be sorted are push_back'ed into the runs_creator object and a specialization that accepts a set of presorted sequences. All specializations are compatible with the runs_merger. - Streaming nodes: In general, most of the implementation effort for algorithms with the streaming layer goes to the streaming nodes. The STXXL library exposes generic classes that help to accelerate coding the streaming node classes. For example \ref stream::transform is similar to the std::transform algorithm: it reads \a n input streams s1,...,sn and returns the result of a user-given n-ary function object functor(*s1,...,*sn) as the next element of the output stream until one of the input streams gets empty. As mentioned above, STXXL allows streaming nodes to have more than one output. In this case, only one output of a streaming node can have the \c stream interface (it is an iterator). The other outputs can be passed to other nodes using a "push-item" interface. Such an interface have file nodes (e.g. the method \c push_back of \ref stxxl::vector) and sorting nodes (push_back-specializations). Streaming nodes do not have such methods by definition, however, it is always possible to reimplement all streaming nodes between sorting and/or file nodes as a single streaming node that will \c push_back the output elements to the corresponding sorting/file nodes. */ } // namespace stream } // namespace stxxl stxxl-1.4.1/doc/tutorial_sorter.dox000644 001411 000144 00000010571 12405153572 017227 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_sorter.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_sorter STXXL Sorter This section introduces into the STXXL sorter container (to learn more about the structure of stxxl::sorter, see section \ref stxxl::sorter). The STXXL sorter container combines the two functions of runs_creator and runs_merger from the stream packages into a two-phase container. As a result, the STXXL sorter implements a external memory k-way merge sort to sort large amounts of data. ### Create a STXXL sorter Before using a STXXL sorter, we initially have to define and then to instantiate a sorter object. Two template parameters are required to define a stxxl::sorter. ValueType defines the type of the contained objects (must be a POD with no references to internal memory) and CompareType is the type of comparison object used for sorting the runs (see example below). BlockSize and AllocStr are optional (see \ref stxxl::sorter for additional information). The more straightforward of the two sorter constructors expects the comparator object and memory_to_use (bytes) in ram for sorted runs as parameters. \code // template parameter typedef stxxl::sorter sorter_type; // create sorter object (CompareType(), MemoryToUse) sorter_type int_sorter(my_comparator(), 64*1024*1024); \endcode The comparator class may look as follows. The operator() is needed to compare two given elements a and b. CompareType must also provide a min_value() method, that returns the value of type ValueType that is smaller than any element of the queue x, i.e. CompareType(CompareType.min_value(),x) is always true as well as a max_value() method that works equivalent: \code // achieve an ascending order of sorting struct my_comparator { bool operator()(const int &a, const int &b) const { return a < b; } int min_value() const { return std::numeric_limits::min(); } int max_value() const { return std::numeric_limits::max(); } }; \endcode Note that CompareType must define strict weak ordering. \n \n The sorter container know two different kind of states - the input state and a output state. Insertion of elements are only allowed when the sorter is in the input state. After sorting is called, the container enters the output state and inserting elements is disallowed. ### Insert elements Inserting elements is possible into the sorter container by calling the push() function: \code int_sorter.push(5); int_sorter.push(10); int_sorter.push(3); \endcode ### Sorting all elements Sorting all elements a sorter container is holding, call sort(): \code int_sorter.sort(); \endcode ### Access sorted elements After calling sort, the items ca be read in sorted order using the operator*(), using operator++() to advance to the next item and empty() to check for the end: \code while (!int_sorter.empty()) { std::cout << *int_sorter << " "; ++int_sorter; } \endcode ### Determine size / Check whether the map is empty To determine the size (i.e. the number of elements) of a sorter container, call size(): \code std::cout << "number of elements in int_sorter: " << int_sorter.size() << std::endl; \endcode To check if the sorter is empty, call empty() which returns true in case: \code std::cout << "is int_sorter empty? " << int_sorter.empty() << std::endl; \endcode ### A minimal working example of STXXL's sorter (See \ref examples/containers/sorter1.cpp for the sourcecode of the following example). \snippet examples/containers/sorter1.cpp example See \ref examples/containers/sorter2.cpp for the sourcecode of a more comprehensive example. \example examples/containers/sorter1.cpp This example code is explained in the \ref tutorial_sorter section. \example examples/containers/sorter2.cpp This example code is explained in the \ref tutorial_sorter section. */ } // namespace stxxl stxxl-1.4.1/doc/tutorial.dox000644 001411 000144 00000007565 12411366426 015643 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial Tutorials and Examples \authors Timo Bingmann, Daniel Feist (2013) This tutorial on the Standard Templete Library for Very Large Datasets (STXXL) is meant as an introduction to all those who use the STXXL for the first time - just basic knowledge in C++ is required. Note that the focus of this tutorial is on the most importatant features and is by no means exhaustive. If necessary, we refer to more advanced functionality which would go however beyond the scope of this introduction. Why do i need the STXXL? Imagine a situation where you want to work with a std::vector which does not fit into the internal memory completely. That is precisely the situation where the STXXL container stxxl::vector solves the issue. The practical part about it: Stxxl containers work in a similar way as their internal counterparts from the Standard Template Library (STL) so that parts of this tutorial may sound familiar to you. Naturally, the vector container is just an example - the same applies to other data structures. Before running the STXXL for the first time you may check \ref install_unix. # Available tutorials on STXXL Containers This section introduces in all currently available STXXL containers. The practical part about it: STXXL containers which have an internal counterparts in the Standard Template Library (STL) work similar so that parts of this tutorial may sound familiar to you: - \subpage tutorial_vector "stxxl::vector tutorial" - \subpage tutorial_vector_billing - \subpage tutorial_vector_buf - \subpage tutorial_stack "stxxl::stack tutorial" - \subpage tutorial_queue "stxxl::queue tutorial" - \subpage tutorial_deque "stxxl::deque tutorial" - \subpage tutorial_map "stxxl::map tutorial" - \subpage tutorial_unordered_map "stxxl::unordered_map tutorial" Beyond these, STXXL also provides a set of containers that are not part of the STL: - \subpage tutorial_pqueue "stxxl::priority_queue tutorial" - \subpage tutorial_matrix "stxxl::matrix tutorial" - \subpage tutorial_sorter "stxxl::sorter tutorial" - \subpage tutorial_sequence "stxxl::sequence tutorial" Furthermore, the stream pipeline component of STXXL is described in the following tutorials: - \subpage tutorial_stream - \subpage tutorial_stream_edgesort For more information on the structure, the internal design and time / space complexity of the provided operations, see \ref design_stl. # More Examples and Real Applications The \c examples subdirectory of the STXXL tarballs contains more example code than those of the tutorials above. These are less documented but still contain useful code snippets and usage information. - The \c examples/applications directory is a collection of real external memory algorithms computing non-trivial output. We welcome contributions of interesting applications to this collection, currently included are: - the DC3/skew3 suffix sorting algorithm \ref examples/applications/skew3.cpp - There is a collection of simple tools which copy and sort files containing integers or structs: \ref examples/containers/copy_file.cpp "copy_file.cpp", \ref examples/algo/sort_file.cpp "sort_file.cpp" and \ref examples/algo/copy_and_sort_file.cpp "copy_and_sort_file.cpp". \example examples/applications/skew3.cpp \example examples/algo/sort_file.cpp \example examples/algo/copy_and_sort_file.cpp */ } // namespace stxxl stxxl-1.4.1/doc/coding_style.dox000644 001411 000144 00000005557 12405153572 016461 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/coding_style.dox * * Coding style guidelines of the STXXL. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page coding_style Coding Style Guidelines \author Timo Bingmann (2013) STXXL coding style follows the current style of STL and Boost, as STXXL strives to provide STL compatible interfaces. Following these guidelines greatly shortens the maintainers' response time and increases their willingness to incorporate your code into STXXL. We cannot provide a full coding style document here, the source code itself must serve as a large example. As the STXXL has grown historically, not all parts of the STXXL library following this coding style. But we do put down the following list of rules: - Naming of classes, structs, functions and variables must follow STL naming conventions: no capital letters, use underscores between words. - The exception are template parameters: use CamelCase for the parameters themselves, and the underscore version for the typedef. - The smaller the scope of a variable is, the shorter its name should be. - Member attributes of larger classes should be prefixed with \c m_ ! Attributes of smaller structs can omit \c m_. - Tabs should not use used, indentation width is 4 spaces. - The following code shows example of the rules above: \code //! A class that does something important. template class some_class { protected: //! a class attribute, prefixed with m_ int m_used; public: //! types are almost always suffixes with _type, //! with _iterator being an exception. typedef ValueType value_type; //! \name Group of Functions //! \{ //! Return current state of page in cache. //! \param page please only document parameters when needed. void get_state(int page) const { int ret = 0; for (size_t i = 0; i < list.size(); ++i) { if (list[i].page == page) ret = list[i].state; } return ret; } //! \} }; \endcode - Use of "using namespace" is absolutely prohibited. - All public interfaces must be documented using doxygen (see tags in example). - All containers and extensions must provide a simple tutorial and example. Design documentation is greatly recommended. - All extensions and subsystems must provide tests which sufficiently cover the functions. - All preprocessor macros should begin with \c STXXL_ . */ } // namespace stxxl stxxl-1.4.1/doc/images/layer_diagram_small.png000644 001411 000144 00000137644 12411125462 021222 0ustar00tbusers000000 000000 ‰PNG  IHDR^ü?ˆÌKgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs ¯ ¯å †©bKGDÿÿÿ ½§“¾IDATxÚí½œ]Gvçß,µ˜™™™™™%‹™Áb[–-ƒlÉ Û²e’-Ûã̘-Ùžd CÉ@ÖckÔžÉ6ÿÝÙl6Ù$;a˜û¯ï©:÷Õ{ý^÷{R³îý|ê£Ö}÷Þ¢S¿uꜬ¬èŠ®èŠ®èŠ®èŠ®èŠ®èŠ®èŠ®èŠ®›þÊ1¥‘)=Meʤ¨D%*Q)§2Æ”>¦45%÷fÜBS¦šò¨)?ÈÎÎù³üÚõ~S«NÿŽJT¢•ò(µëÿ¯œœÜ?7˜ó3Sž3e¾) oÐlÊż‚:Û´m¿ ç¨ÕÁ°9GƒÑKNc–=lÊCQ‰JT¢R¦e¬Á–ÑKNÃçÝô·)hÑihPPØàÿ,zÏ”‰Nû®‘W¶p˜ììÏš¶í }{0kÇ›Áü} |•¨D%*Ræï»ÌÙõ•`Ô¢ûƒV]F9¹yHÁM)¨‰À;“vè3-˜¶éå`Áþ+2oT*¶,ØÙá77C¿/Gô•„up%˜¹ýõ ÛÐ%A^~á_ŒZ_Ó$ß^ÙÙ9?i×k¢tôæYìU³Lß|!˜°ê Sž &­}6˜{ë»å_¯{æ¾×èµAÇ~3ƒSo æïý ˜»ç`À”ÝæÞ,ùmή·Ê¼îy¦ž9»ß)‡úâÚ´í5³ð–ûÏ †ÎºMžhäæBæì~;è:d±J¾ck èÖ2åtÃ]ƒ)ÎE [ÉRßì_ÚtäæÕ rók…õ[ãW>QîóOÝøbP¿iÇÀЃiÃXrï³w}9hÙy¸Ü«×¤}0Ë€sYJ¦Ô;móyûÖÝÆ&ï6÷?%ž)^ j7úßy·¾ÑÉM¾3·½´è8:øŠ)õjð0œä¿˜¼K:MtåªVãWžj6 ¡MD68‹Í æWìßWbfŠÏÆÞ)þ¬oƒfðŽàE8moÐy༠÷Ø "™úŒB¾åÕ盨bu]Ž7£xL“Êd#ÕתÓXêíÐw†ûîe·à^ zŒX!õ›sGœÄ[ì[û“™f¼qÚw9¡}Éž-¹½Q©¼µ1|Þ± ¿v½¿Ã,Z€÷€‘rþ}j$íV~1‹¼çÈU¶yuØÕ0jÒ¦p|IÀgÌÒÙž¾åB0bþÝA¯Ñk(R¢~3ÅÈ…÷ʳ#Þgž½(§€ùÀiûĬ¡À” x‘0Ç,9 š¾?>ÿXÌ *à[ÒŽ~·݇-“oÒ¤v~׺zY/ætÀ”=ÁÄÕOÉ·¡7êìiÚ_«žÔ‹' m¥>~›½ë+æGäÿãW<&æIûý#bé1r¥yf_0yÝsq@Š©†w)ì]Œ_ñxÐwüæ ×˜u²““ /K]lèØö.‘öbr™`4ŽHÒ®ü2{ç—6þ œ2%¯:ƒnmSÞl×krDXU@š±õU²½-µëtºØ‚p~mMeŒÌUÛžÌoÙAaýæb È+(´R²¹×¸UO ž‡X›™oñl½Æí‚æ‹C¥iÜv„éÉ3ðF­»Ž–÷4ëÌÚþ†|wÆÖW‚Nýg ¤¾ýVN®”Zu  ñ½þ¹‡ÉFb4+i_†-ƒ!³Ë3ƒg¥{¿ðÞY·I›j×m"õw0GúÎX]öˆ,B©×ôƒÕÂ{4Ïõ›´=ÈÉÉ“‚ù¤v½¦aü=lîQé ð¦vÌ eÜù»që^"yÏ4ÂJ/=F,gîþДÕx›‚þ¤¬HÚ­|U pe±NHVž¥^TmU³x{L¶QËîbèÐoº€÷ÛõžlÀäm'%ô Á¬Y»â'Ù¦ûxF¬VùîÔ/%ÞV]GÉ=ì¿/÷Yx¦þæý&lI;ìø§¥?#æßt¾\Lô ɸ~Óò­F-º xO\}&èÐwº÷6ït¶Ô¼w‹HÆS7¼š!:¼¦nur çÙ>ã6õ›Øo×mÔF6&ö~þ9öÙ†ò ¨Û°•ÜkÛs¢´}øÜ;E‹@úµãÈë¢ ¬‘!3Aófæ©uuÞö9¹ù?ío¤‚x+·ªÌ•…_`¤HTbTyÝØT¦`DŠó7¿v½`Ä‚{‚…?‰Ö‚OkQ»N^$G‰gQ»ÙPµ`×E:-àÝñ¦€ïp)xÊúç­û›¡!@ À¤"§¬AÔ÷!3§ì Û'’ñ-§¤-“× %Q¼'ÄVë|:Ùðõ—±ò ‰Í8žÅ,“W ÷â…?Ž^vÆ¥Ofœ·ê!÷Ð`ŒGaýfrßQ˜ s†Øµ#oŠÊ^3Ç#‡áÿO3O}«3ðv0Àû³þ“vFÀ[ɶ]@¯¾Q“Uêµè¸‘xŸ  1ó$vJ|{}à­×¸­>·±Ôy ol¦ã °áþ¥À‹dlM—å~Ûãå~-¥þi›J^3 .Ò¡iG¼ îþ6@5xÆAaHÚ"f •ÈókÕ F ÃøXì°1à‚œõj(¼,ÿgLNÝö}ì-„Ï2´+¼Ù²9Ãâ¥_-:ZÐÀŒ5 ;wÏ»̘ÔScÌЇˆV+xa↞ÞþÕx[îñmÔ©x+W…l@5iMl¯Ù ¬;¶I€éË^@sÒš§ÄyYÔh•(‘Øæx/À)›tæYÜÄZua¥ã†­ƒ[.¥ ¼“Ö<#õr¯eç¡ÝU½+ÄEÌ€™~ ðeÓcçí]û^6â’ï‚ð;ÉéYíº}'l ¥ãÑ‹Oˆ¶À³˜BSƒ˜DrdÑ*ð¶ì4,drr`耕ÖGÌ?&¦—ÖÝÆÊ¸„L³±G¥òÖ ›µFÁŸ·]uÞ:¦¼-.<‘*Ui¾»HŸmœô ø60j|ÃÝ\éÔªÛ84¨G€/ÀÌ®>›?H¸¡ µe7ñX`x±kâUÀA ¤M•Z›w"ftM H;ÉèÌí¯‰‰Õ]¤b¦ôïs_M'x­Ä‹ÄŽiD½0‡°á OM¼0ý6¾<3Ã0”΃æ[¦eÆ6-<ðqÚÀ‹y„ñ£˜.`pá[<GÀ[ùZ7óöÇ.jbµ¾î2 ü?qb«r¼0)(¸àe€”‡màœn$Ѿã· Ppƒšwk xl“¨ÄuÜæ÷úŒßädÄ6×°‹6h!öM]Tk¤$¾T(°yZàíz5 ­*ø¢xc4iÝKÌ%c–>$’5ÿW³Gs£Ú7m×/ì'õò €0W@ ñÔ ?2Ìpàí?;tqcC SC–x[t ™>ñƈ¶ë Ì~·É3”xàêlÔäcÍß0:¤x¾¡L,ôÔˆ¼*µ ‘¸=‚gLɯîÀ;*7¯Ö_á‚o%™f’Í*>’šªë³M2PDê¨p'S5©ת‚ÂúA½&íd'Àå}xq‹êgTs¤aTrêë?i‡H{Ô Øsz À±QVòë2hÔÍo|o¾ÛàÃJŽÙ¯¾‰Šé€ïtø ¼u àó¼/¨·IëÞÐzi³]¯I¨ŸM/€—6á‰ÑÀÔßkô:MžGÂÆÌ@Ÿðü ¾‹Gì|gúÀm±¥Œ[þh¼Ø€é‹”˘2¶ÔÍ·0óÐÚmpD¯•»Nð'7Lð -/® (07œG#®^ñe–³é›/ ÈÌÝýv‡÷E‚D*äÀ3*ñH…HxSÖŸµj³Äv¸\Lâå_žC-Ç“€ï yõÌpõX÷)»I†”kë~µØÉ1<¨»/@IûüoΔºž“6ÒnÌ*ô›rb\@Ôßwë>öŠŒFË 7ñL[h/õRï‰?ºGÃØ¸E{`l½ú‡XŸÞ{–ïaÒÑþèæ¨TÞæ3tf7ƒ³ÏÐr“š¯axNnÞ¯º ^`ƒ Dà[ávÞ¤G}S¶›kãCõª¸™] %åøØáiùðJþlñãÅ%Þ+Öö+ÅŽÇÞ½Ù-¹¯±ßìO<¶›"z™Wwiß,µŸ }IÚŸ¨T8èÂñ,1ZÊæÖ´X¼kòòkÿ ïH;‘Ù¡ ûüàe÷¿ °¡€)~µ©@I-Ïò¯•#0‰Jõ0/ ¥`§/(lð[ƒQ·ÕƼthGNnþÿ@šÂ'Òº ] ã£RUÊeQ¿­iá¥0îA²¢a&y–ý`2Q‰J•+k éI«Ÿ»;Ùp 6«)QÉ’]¹.òÏ×j×kú/ízN”³ôlJà+Š]0*U£`ÔR–ÏF%*•U&­}F܇Í9"]݆­ÿÝ`ÑLYYS³O$^ÍLÙ@ìKItY«îßÖiÐò·õ·û§¨D%*Q)R·a«ßÔ®ÿ³srÿÒ`ÏSvW÷ƒ7âñÐÕ”)¦Übʺ¨D%*Q)§²Â”édÄ©Éf…芮芮芮芮芮芮芮芮芮芮芮芮芮芮芮芮’/Žëæ¸õÊñJ¦÷²+ø^vÜË*Ç{Um kâxåTññŠh®jaYÑ\ÚÎÁL¹Ý”i®QµLYmÊQSæx÷–™rÄ”…Y6µ1÷—¸çnqÏèÉ3žÃ/®¶«gš»·>+–s¬«w‡)õݽQYö<ô6SZ¸{Ýs{½{¤Ý8hÊ­¦tt÷ð÷=ìúÓÅÝëìÞãÙÞîIêö»g¹{ÍMÙîÚ8ÂÝkìÚq»k+¾}M¹#ËúsÕ5e{w¦›bu®rc3ß '_»ç–fÅây.tÏ-wãÅûÓ½ñÒ1œêîQ#o¼hß.Sº{Ã]ß×Vî^?÷Ü>SÚ¸{½ÝXíuã”åÆò°¯îî^Sö˜r(+–gª¥{žìî5óÆp¤7†[]ÝÝ=|³7¸1œîÝ[éÞëô 76 ÝxQ¸ç–{c8Ï}o•ûV¶›žÛdJ¡{n¢kË&×¶,7ß:†:®C]ßvyãÕËÑ&cØÞÝëáh‰Òͯƒ 4×6Ë:ãóþw¯…û>÷†%Ð}ãî5tí¥“Ü½BG<7Ãõ·Ð×7F 2ËÜsK æ¹1>âÖ¹Žá,wo£ól7^G\›t ǹ¶l1¥©7^G\›¹{ƒÍìvýçêéÑ¡®ÛîîÿÝx*.ísï÷s÷Z¹ñ:’@s;ÜŽr÷¸¶ÑçñÞ½õÎe¹±Xçž›åÆ«¶‡s‹ÜºÍvëõ÷o-7†³=œË86/ð [¬]%TÖÇu¤«w¯§[PÝ=”ïážëå5²³{®oV,÷|'w¯_Vìè][wow¯[ÝRá&·Ž”ëȹ~ óPC÷Ì0Hê¹o ÷€¼ŽkÇȬXöÐÚ®#²b§W èt ,Ë zw¯³Ç™{»±éæaw÷\Oo »{c¨ ͯ<Gºúkyã5Òb-± wý©ë¨Ž¡:†7uã2Ôí†Þ6öˆvˆ»ßÌc6z¯¥]{Úxc8ÀÕ­`•çú5ÒõSǰ·»§4—íÆd”Gs9nÍ rßlã×÷œ¿nû¹çðsFt}Ïv}ôqNǰG’1ìâá\mo|ÒºÆ8‰,º¢+º¢+º®ïšéi-i]µ<]Ñ]eå9i¸qTªU©›Áïtæ®´¯ìcsU¸rœŠ0ÇÙï6åÞ¨D¥œË1g·žåÔÖYÍœ=ÿ>S¾dÊ×LùzTªUùÈ”çÜþÆÐRL ³œY$í«¯·qT.l<²³³?­ß°ño;véýï½úø¯>Fý.*Q)Ïu0ôV¯~£ßúû™Ûj˜!ýf»õt¥~Ã&ÿØ«ßðߟ¶4˜»l[0oÙö¨T›²#˜±`C0dÔ´ m‡nÿž——ÿn“·EŠyÏw¶ß´¯ùn§°*\ÎßmԤſ̿eGpßé·‚Ç_þfðÔ+ß ž~õ£•r-ÐôvÏc_ æ,Ù4lÜüŸ´Ú/Mmu^~ÁŸ>)8râbð”ùîËï~¼rå—Q©fåâ‡EÁÙ/ý8xôÜׂåo šµhûof~ßó6‰oèÂ]dyÝŽÙÙ9ßíÙwXpû烗ßû<¸tù‹àâå"3×¢•Š)†Þ »—ßû,8tüÅ {oIåýMÏà¤khNnÍ[<ýÚYú5ß¼ðÁ/¢RM ówÉÐ9y)hÝ®óïÌÿºÑÖ<Þt뉠°N½¿É*žð³Ô‚jçŠQ»°î_oÞ{2’¢Råʺ÷µ ÿwVìHR3™¡áÿ½ãðc¢¢FãV‹Á¦³_úã ßàqhAÏfÅšÀ£aT& §וua>ÓwИàÙ7~o5“ì|]«Á}, μò½IÖÐé£%ÐñÒ&ÍZýçÃϵLi8Ñr!Å·i'*ñÅËE¢z_r&@{?Ù½ µ‹|¿"Ë‚» …ïfÅ€pdexz ¤ÞQ‰ÀÛ4;;ûk³o m)ªN•rþý«¡zIQà·W>ú•)¿,¡üʵýš5Ìó¼ù7#@úÐ÷zÕTo–3½øÎ§ÂýŸ}ã‡Áó_þI g2Wƒ™ 7²Ø~¯Ÿ÷{»õT¦Âà oý4xîÍÅÆú¥wîÖÖíÞóØ—ƒ=wœ n»ÿåà…¯ü´\˜!ýbóqÏÑ3ÁÞ;Ÿ“ ÷žyýûÁ»ÏšúŸ |öJÆ4˘Ù÷Ï'Ÿ¹\µÍOvy2ÈÎÉÁË¡KÂÆjF1&UòæZû¼¼üŸ¬Úrgů!˜—Þùypç#¯k¶3\lw°dÍþ`ÃîãÁ¡û^ ž¸ømyæîG¿`ٲ…ßÙ ?ûåûŽ=+ÿßo‰E’î"ĘPÞ=xï¹àÅ·ÿ[Æ}â½ô YçÞúY¹Œ‹üŽ_ &Î\ô8:l¦Î]#`PS†xˆÃx³¿ŸÂ²3ƒGN1sþã”Ri&`c[¶î`Ыÿˆ ÷€‘Aïþ¶PÇ´ykƒ[ ð)}½dž3iAPPP;èÐ¥·Ìy˜;]·ŸjÖ ê5h$@ÉØÜm@¿ió6¦þZÁÒµû3fâÇŸxǼßZÚ¿xõÞPªª´pû‚ܼ¼ÿ•‹¡N 2¾º×á«X–W§ü‚ZŸx¼×DšX´êÖ aãfH2q¥°N=q¡=“g¯,ö{²Âb8uî÷ƒvzÈÿ;vé5däÔ2^ú ã5q^H_f¡›’G£ó—ï λÚÜ¥[ƒŽ]ûÃÆÌž¼ôÏ<SßcÿO­Ò‡Ï\.þœÐØ¡SR¿–‚·=.ÚÙO} L˜úYÇžå×ï¾ë?ƒ”¬ï¯ßyo¼é¼_¬í^»ýþ–)ðžH ¼Ÿ\«ì«Â—ÉA%«[¿¡QûN=ƒ©sV³oFŽŸ´lÓQ8/íY³í˜HF¾CƒZµëÈ;µ \߸ÏïlÀàïÇ·ø½S×¾FòÈ x›4k-ïöè34Þóï.ÒäKfq鳨’VüEx®Øt»¼O÷Ýõ¬¼û²{Ï0+Ò1Ru"èAôÔE¼¯õñüù÷>‰§VíB©_k¤+\§ôûj‚@-†¹©T“LÒ¹`êâÛ< Ð$‚ÏОç¿ò4\ +c㪲€wô¤ù2ÎH˜ ·}JÀV…… ›w ƒ~кNdø*ü£F@`Ι'ž¹ï‰·eÌýñ¶´v58c@›ßï8y)xøì'f~~gvÛ~èQ ¼ùxOË¡ÏÇŸ|W¾ýÄ…oɳÐS?þÑЂÉF xØ0o«!ÙïRÇñ'Þ/öþ™W¾+Ï?úâׂ;~-xè¹£gž;f4TÆ€6ÑGÖ$ï••X‚Ä»8ÓƒhD)t3/´|ãíANNŽÐÚÁÎÇÄÅA l’èÿ±c&˜{mÚwÂkѺƒLªØÞÌïû£/~½L—ñ8tß¹`úüuÁ<r÷>þV°qÏý² )[<,í@qséi7€dQ 1EÞ[¼f_¸x LÄ„éË‚A#&gÜ"¦]„´"³t›¼{àžçƒw?/j,ïܲᶠANn. ô2^lŸ¨œ¨½´ êð€¡ä=Ô凞û¨XŸï?óž€ÉÓÃ&c&/ ¶îHÀZAà¾Óo ÐŒ?;:zz0gÉV³ð^P‰ÀÛ¦¢·~ƒÆ„¯}òk´QæÊ}æ`ÛGäIfÜdhç´,ö `Ä 5šEèÞgˆHÊõê7ɘyQév¯ÛyOСs/Ñdû†šIpñ´›jÅ—±¹×ÌšõS't„ý·¯J¨z ~Ì …æÛÍZ´•ñaž…Ú͉÷—o8,€úäÅo‹÷€¼?Bßo#ï7mÑFêsœ£•ÃÇ_2šÞ@ÓöÒ¿ÑF[˜±`}ШIs©õJ¸©–¼µ2Ý\›ïÄä› x×l»K„2|ìÌ`ïÏ'žþ@V6Ø.Saàܪf·jÓI6”p/ h•-ðB(n50ctìÒ[„ª›,Ì H„w2Òû³¯ÿ@uàðI¢²æää‚l!ÿb—ÃT€tÄFá]ØjZ©¾S·¾ò7oÕ>èÒcvÎâf–¹kDòmiƤn½†Â”XdyN5îÖkp¸Èé/cݪmçÒ)Zj9t cÞ[CæÆi:ÚÍ*“ì˜/h=ïîd>*Vl¼Mh#77ÏhFÊ7“ï£o 6‘û€=ëó†Îs}óŒ‡&ô}hÙJÌoÉ3¼FÐPàíܽŸkg#ÑJ0ؼƒùó>Zmºeý!¼ „ið»š%Øøµë¡vpäd¹/×ûgº¹ÖèæÚ\³õcôÄùA¤³üü8Fu{Þ³oU&ðBhcôêÇbÊšÒ_že§û#Ñ¢†â¡’0ž¯™g!L ºP»Vl:lÞû !Ò"¡rŸq€`}àe±k@]ãP_·|D$ˆ\£)`ï}ý«žÒ6íy@@O@ ç[ã¦.’ùe7^mijo³ßGrQ›òþcÏ…RÙà“eáíðÈü @Ò»t“¯_²³­y PÁÄóx5”¼……õÄd$ãlæ ÆËÈ»‹Ä"à`Y÷À=/„éH-Zµwjg®®$W‹ Ð8v*¼3nˆÛÌb,¶ìH€ÍŽC…ÁD°?w6Ì€û|Õ“ßœ°®ÝqOª€iâbà[ëwÝ'íTuZÛ C¤­Ôó¨(O‡Ê^Õ”9«Ä<ƘÝöÀùà‰ ßŸ/ x•¿rÅú¬cçW‰‡#fÊi#¶`bTà]hö4´;ów=À‹ sÏÂ,ÄÜðš1Mx › B¾oM"9âyôмhP¸¶)ƒ™½dsE/i•Æe|3‘kð²cŠ]’¿!R *›J¬¤ä3^£²8^uR§Ju× ¼ØA>øŠ|;4Ið@faª9?b$T€o{£–*CÀ ˆß÷ÉRþ5R.2,_âÅ·9Ñ()ðš:f-Úš9Vm¹C¤_6µOÃÆÌ¯„ÝFåä™p‘yîKú÷­GŸ‰ IÀAZ“¶šÂÂã÷“lØÕTà­'6Þ áX_Jp©* xU×g S «mè„yû¼³§³‰Éÿ‰Ê=ýêÉæ6[è÷º€wø$±É+p ÃÏxnðvе ý(ðŠ©aÃ!ù?€ƒ ÝxdÀ8*xëee˜P‚`Ûn®Íµ¢`íö»e3u„aì—¨`Ídغmgq[Ñ6e ¼lFK Á/Y³Ol[ô1™[KY/( Ð?u‰€*n>ìO»Ú©cÍâ 5£¾±c #!Ëæš¼+Ó^Ów•:x{`Ï"T‰W€×,„z†4kÙVl‡h˜}˜õa|y¦ßà±ÁÉg>Í„7v¯aöÔTÑM¼DìKx§4àdÑà`bl¤êü²iF8L✥[å9\'ÍZ!æÕ†‘ãf ÓÞ{×3ÁkÿºL€7· —{²¹Ö¡›<ËÞÇó÷…Y4j&ŒPÜ‘p‡2@tâé+,Xeï˜É dœ™£Ò€—;ž…>;>tÐÄÐá9ì·H»êÛׇ€kVÖ¶X„ ´?´7æ™÷™7lüx¿$ïqŸ:xMÐú¸‡ëb x_o#®Új—¸Ûx]ðìBÃTxÙÇàÀ¯jú¥]+6Þ÷ç>vohñÂccô$;~ÐŒ¯½–ð’D³g&ÀÇ©µæ7›W»Ù€jöM\_p—Br€¸·D¯¤D¤X¤7ü\QÇÔ‰›ßÙˆbÃh¸ù÷ž¸bîÍ\´QŽ':~ ¨_ú®œ’ãÛHt,`ƃM$Þ§€'„Æ¢„À‡Žž!Ϫ.68Üfh#¾ž˜MÄßÑ9Ô㟌G ‰‚nv…‘2ù6'ÓðŽ N$×ø1(2àI6âØèÂõÌ‚ <#ØÃíg²‘š°EÎZ´YÚ‰÷¦¾Ç"âdÝ#MÁ Øg“¶†Ç`ßý¹ø®2&Øyéžq`~Ÿšj㕃,F Åó Ÿ8°%dx8ó¾< ãd.h³¯î ò ¾Ò;Ìø=âüÔ}„N>‘dÑþ°'cF(€iZ€¶Zs«'äðDa®¸æ¦ôÀF÷к´$l/Q0_èºÒ÷‘÷!>Â÷@Aõ}ú"ïAG}¾íqù¯›wÞ¯  û ª¢U•¾”¼ÑɵtψSP³ô´§¨…’ŽT€ÿ¦þ=yIõNøî»Å¿mO®}&¿ùgØSµé“ç^ëü<þ´’y€à°‚ÆXÐþúïRw²Eþr’ö\pGY0Œãù÷¬‡B¬Ÿ'iççâ/§èÌ$žÑ—gÌ{þI»ðôZ!® à 4…Ù®¥ùìa°§x4`{ÕsÒ’ì(¹ ƒÂ'ñiOlSÜ=ïYÞÕ®äï»w)“÷]  @ö¬…›„)#‰Ožµ2ÔÀ¦Ì^åL|×ÊxW™29£ 5™ŠÈ5Å,*Q©ÊÀ{cmþ"X¼úVQµqxkfpvËìñHRW;lÀ˜Ï0_²H|zËj? à­[J ü*2Þ¨DÀ[æ4\ü8”‚É/žšëM 3Þ:˜Í~é3fß¿œ7ãkº)ë#àJTÊx« ÀÅP¯éAùý@ê^î¼²î{ ÑɆ™Ò#àklJ«Ê^6f4yT¢R• ¶Ñ´€wÔT(JÐZ“i ì$À»#«x¶ª½¹–››÷»è7»ßQ‰JU*ìð#Í|-xqŸ[fTÝhÌj6-ài““““¼+3Ý\#¾h·Ê^#I|†+þ‚Q‰JÕ+½4J[‰À‹¿(~ÜÑxÕlZà•Á¬DàÅrP?à›mÊ–Ê65ÀI4xqT¢R• îqø?—fjà„)£4|Tj&-p|Ýhé7¼¹6˔͕¿¹v茕ò*‰itnäE×Yﵯ÷Ê7ŽR§¿¹ö“ìgTªrRl®õsqoÒ¾š¦Hgy5ÔÀtìœ<"Öê8“K¶s_K?)¡©)Цu¹þæøkr:©´ƒ(‘;YT*!ÙåŽzríZÂIž¢2¢tÁ>tU©lŸÅ÷>;Æß ›4‹^‡ùÙÞúY\:Í{O¸8®‘Ð4÷šœJ3 –ì§Ì^p~¿…«öϹcÕ<àHŽ7ñ¡äÛÌÇ>ñ­Ôg^~/ù©8›§íjÜQPN¾1þd_X¶þ DÌJö.§ílúóÏÂúõ]ì{?¶ùäÜ8jN;Ûþ«Iç8Þ¨¤¼L™‘ ðµüª2ð²ØWˆÎÅ9rÂÆÎš_/‘Û3âœë.MrãwÎÍÛT?×*Wb½ô ·¨@søþ—$âÇ$‰ÖDrN=ïŽÛ åÄ_%ÀÑÖÆO],™#x†8d¡àÎñ#‘0D/L}pçlÛ0;¼&d:îtVQò“\Wb¿Ñ¢’¤þØ#oÈ÷Ï»€C¡”}¹ôï$j¢bþ#œ¨Y3n•É$øxÀŒÆ“LH"Ö1Ia…†BN? Î#u§h×%ïÔZYKÊ%o_S:ÖSƒŸÞ\8}ÂäaÃcÑÎÄñä9TS9³P¶ !ž' Utì”…Û©mÚܵAÇ.}$®'›N{Ž>%¢‘Üx©‰4Ñ€+ @n*$L¤%À2nÊ"‘ê˜hL!å˜k! a0‚‚ó!'Q 8ÓHðH—÷?ùžÙCQ–$¹Ì]ºM“vÑNyÍh+¿Ãžyí$¼j4Ò.1€ùÚbYÔ0ÚÂdTÂLE õÀÈ C"ÀXt´~¬äà½çda{˜ð›=Œh¢‘8a¢ÌcNû54 ß󈩃v°èèï’ÔK¶±ûÂÌÌØZû¯•xl?ùµHФzý“?àåo¾§¦æ‰Sf0Xæ†Í}žèç "Ú„™YSÒÏ…ñ£Ýa~A¨€™#ÉCS„pÄÜ·Z›Õ¦xça~Ì­¤Í‘|y§„Ö5ÐVL4HÒ¬3ÆPR¦;:{É…‰ä;0¥°Ž¬È·©ÍÚaý%/sÌ0Ïú]ú a ­öò aþhôõDŸ I±#d á É:àwÛ®÷CEéÈj / Ÿ6ôVðn5eNØ\ƒëm4ê)\:) ¹Í±-û‰¸ “ ¨1ëw—رHĸ‚|Ä›e² œ‰3n .˜¿±š˜WÞ¼ï¤Äè…80ut Î.±eMÝä9›wËvÙðaõß`‘:?À§ï 1’× ?@$?$@â€8Q­%Õv:‹Ö¤F `äÌ‚¾ï‰·eLHpimº÷K1Á¬`bGžü‘´/6bŽ­ãc‘Z:˜àKüY$=þtÎb#Þ1¦ÚƒV¡OÐßH‘haªÌ< ͳˆÞ<éï¢Õ·ŠlÁb½˜1h?àœ,îmE/’®JŠŒ ´Œv £9p-Çäó$" »7ÏcŠB€`ü¡i˜äKx sÄBN@ãÄ(‰,U`Y¹éˆ05úÁFæ„éK…>HVÊ< ±ÉÊxДúÿÙ(˜™ü0£Hé´•µ9kñ&a˜Ì7™ª1‘yšõMj Æöï:ò„ÄŠ& û:ô Ú$‚”¥7ú‡À ŸWL’æ;ôí‹`òh¦e•‘ºà]ç’J¤}53¥]•”x ˆbßcÁ'ÛDSO•E­aïxï¡z²¡²|Ãm¡ÝõÁg?€„Ž?ñ®€ …4ÁâómÀL$R*ÿÎtÀK[vBP @…”*™#X"eÒÌR C*I~i¾O¦TÀû£¸v]·*ãôôA5€8\&€Ðß(”÷Ü3,°sΫÁwÛBÊPéGÍ ~Ú"_ʈy5\³oNÐyä[€¥o³ôëûd¾kc$ǾaÙSJÜgªIü[Å«—ô€Ù‰ä›ŒOUðj°À{ARþ´Ø¦a€€ ­ž|Æ‚åBg"±æ­o 8“ Œ{$Hh©û:öoÀ‹5ƒ¶ÕwÐX‘Y| B“ nÌ Ð4Lp}@¦Ą̃ÆÏÿÉ*ý²Ûôä{0G Ëxïã_Zaâçݾʽ&u¢õa†BHð6c¸Áh šIs‚ ëœ V¢SíA“B@Ë‚yêÆ+‚krÏO•™«j ÀK¼›&5ÂÆË"@E݇ðÔŒç\¤F¤E$=Úÿ•‰Éޝ¶i"€0,ðÚuˆG]—Ž‘–ú'*P¼F:B*Tj*"a$k‰ -¤.ˆI‚žhêA5h î‘ ‚RP¨jÑž*"SYìx_ÿ†Ðœýj•ðãà5R8R ~©ó´€n9 ¼—…¡ X^. ßCÈÀT‚†€v…tÌ(ÿ’qÄnl>^€Ð,ßÄÅNiõ i[3ÿêw›$àǦ'Ò§&oe|0'!ykz'Ù05cNœi¥Zú€iN2G¸ä¦/¾ý©˜°Ë#yjj»¥ÍØô1“¨Qh/˜òh«5»}G„ðéÙ¦„* .L¤€7ã«JÇãeðP£\Ô0@©ud:\÷•ï "Q"}hL¶\TùM·žs@ x¿.j“¯‘x‘àÒpLâå™Ï|(’†55ƨ¤ØOY€©ºü¼H¼Ø™á¸Ô¥6(Ì ¨Æ´°' ÀÀcƒCBÆ4ã@bMÑÆ{kø§»4…~¯Úï8?d·1C}l´ 9aµþ¾ñ!%óE? _ïßW:î´\†JüsE¥º,Å2_‹ë{lç(|iúP_e~;ýòÈõ=KJêkE/(^ ³?Xb3XZTq€ óO"ðB»šÂŠï a£ýábˆ †qBAHÁ;åáç?) ×¶!¤ì>ò„¬Aß[Ɔ}“Ò2ë—¶ãµ¢™%Ð@è×=F"Ü`ýnrà=' `xŸv`ŽDŽ©z¦=ŒŸ5Ó,1m;“Ô©Œ·‹)­3¾V™u«X?^käGÅ€ó"%’Ê›”ºšœw~¦p7~‡k£‚0pZ6’€ `~ê…ßVo½+̆ZƦ¶+|]i0bc:ùìeáôwŸz3Œÿ‰Í ð¤Ò5€Í&„QC€Ø±òÔ±! b¡Îñ{:›k¡9ÀßÈØsÎ,€ýÉip×\p¦UÏì¦$ã@ÙˆC}Å6Æâò'ОbY‘—ïRØìÜè§š 4uÁШ[7W¬éã'aª#ë7kÍ€szþ½äÒ?ßd¬XüØq1"Ç—š3dSßÍwôl=ÀÃF'¦ÚJ¿Q{ÕV­É"aœ©Ü«*x¡ãÅ›â¤pu§BH°vÙ™¢–#õÂÌY c¶ÅúÀôE_ÕUñßî6×^î1ßH©Œ«/›¶˜ÂHéÎ:`“–º­l¿`5õÓVê¿Û<3tÔ4×—åöÏ»Ü7rj³àÝ^íÂB¦sr‰`A³˜ ”D{œffeq½èÙ,å”Ô»ŸÅŸp€¸êþŽ÷'U0`£Kw–cÿ~^,˜Þóí’´ÏJ®®]LúŒzjÄÚq5m5)&çÈV‹½ø"e£Þ±áxÔ¯Ý`Û/ÿ"m°ˆ] ‚d3ŽÍ‹¢Ž— cã\})Ônž©ƒE…™eÿÝÏɦ¥lLºlÇ,².Ó>˜Ò»ÒHÖì ³¸NŸÿ–lž¢JãÆ3¥´¯øÉ¹«²¹ˆÒFìñô“oÛ ½ŸÈæ÷ÙÐcq³ñ„&Áf “¶ð;mCýdŒ*4${š¯r%^µ‹2¾q´ð¡u«„93 Sg3€@*Íßihƒ{Ì Àc£Àeƒ ß"CÅ sÅþÊóüÙC< Ì7´XW…þt´~æm1fã½&®o´pGbU† ¡õMŸ·N\i‡ÕlNRé”y¡½Ðs{èø‹BClÂêúg­Špd˜ 6$qÚŠÙé@ÿ÷ô(x7»¸7i_M2‘£X •oÅÜÑ?÷†•8Aîáq”Çé>TC¤Ü´ôdÏ!9°:$€ÀÅÕ "Gj}÷ –EÉn¹Jº0@ÁÂcQHºïSoHð>*+þ½ø³ˆ±£–²˜°õݲþ°ØâY(h)€8Úm‹ß8û\ìölÁ`'ÍZ. ¹xõ>Y”HkxfÀ7ì¾O¾IŸè3ÿ € é 4šÚJ*m£"O®Ñv{l:ùqjé‘ù‘øܘ‡D!„vÀÐÔ~«16`œZ`$‰I=&ÇØÆÙi¿d¿ þã×ñ“0›~+Q;¢@1Vœ fÅþŒÄÝ&_ Iµjrüͳ´=q“Wކ›e3ÒhXHâ˜G)`§hw𑻀·­sTˆRÿÔdàÀr%vÅG¿ÀD‚@!.@xÇáGXpëÁÍ ŸPlÔŒ5RjR£þ‹)¢Õ£·HÌެ"1¬5jÀùªó Ź}“‘VP¹Ç®7*- ø"µpt€Ô‘ªxð@ Á܃êËÂ@rF cQúv96»Ü0$.¤+T^´ úC;`,P¾Íâ`3 àÏâGªF‡ñ<ãÄÎ=cRÒ[…ÇjHñ¾š‰˜cÔ÷˜-ûZi®Åבê÷4Òû­„gRß¿VJ¿K¿§ßbß&Š”‹4ŽÍMFA¿œm¼_³óo¼Õx°!£¦‡¿,öJ¤IÔT^@ dU [À” TmÜ•%@öi÷/ÿG²D=D½Dºò7ãG]˜ Zìz>6@ÔFÚÀ»Ô…ÉÀž8{O\ðx†Í!$Q¤c|@Ù´DÒ¤Ù¨ä Ò(@˳vÌ:ìK܇'ml Á߀0maÑÑ?$lÌ0h¼ðn·q,xÍsh²!e@•ºS© U+HÎUa.×{47YL…Ä{2×. —ž«ª†S¿#c?†6Ê#¶J ÀÛ9ÓjLéou ïX$…ÍRƒ¼@hØ×Øfa¢FãÕ`ËÅޅ䉚˜!ù¡Š7Cø¯Í†-‰PvÓŠ®iHY¸á1Á3̇[pvç¨z~“l`ÑÀ–öÑ^«’~.»Û³8ÄŸ¼hOH™ç‘¤CUM9âÊ’yæØ1îC€ýû»ò7 ŽTOáÐ m@Ò!Ñ'É6kÔb;8xØ+9ÉÆ À› ÌjJt2ææ"ßSý×g,4*æ?gÆñÅ_ïªPk˜v²cÊe°' àÝä\skZXȨ$n®.±³ë׊¹g©*‹‹Pî^_Hñ<ŠJ®ÑÃpÏÙmÔ@@eÃL¥`ê°±µ^ü0¾žÄïúje\à¢$Ï©§ v×ø(d×Jü»¤6Ä¿s-£ÈW¹¹ºéù&ç"—šÔöÑïgQ¼›_RÐ8oÔðÍᆰœüÜÿ,Ò# ßxÂfæ4 ’‚B»Þ¿–ò·âtêõ¿Ø¼Æ¿ÁuÎzÐ%úÐ+)s^:|8€'ž9ô[ã£hÌi„#üÑé÷°13EúgíR7&\*1…ÁpÔ#Î|‹¸e ¼m2µC²Ou^~`ñcDAýyIÔŠ3áR/ÜžIæ¸1Žýìz?åNn1QH¯dç]‹kÿyޏ´¨3ùµ€T¾Cåwþÿ(ªD‚ôÚ“E,jÄNÌt) IðgSƒ¨rlB0–HÙøñbЮQÙ¢Di0FýÄvÈ7‘Î%ÍŽYD™ÃõOýè‘Pž9|üEQéL˜ ”ñ6Lù¸Û0Zö9l‘ÉyÕ˜÷ý“V*QŒ ÿ[˜™úÝÒÀõ¦tD¬]<1ðäÕ¿)&ÀñaÌHø3fÐ'ß8å”*"[¹ÛxM½Ð¦;í»[b$|Oú àÑ7h Iq ßØ4%ž†¡¼˜;úÉœº0(_#yE–œéXâó:%'üü9èh°§R'óÊø¬~ž<Ñ>ø–I}0$q‚Ö0ÖÌ?-ó½Â ^ÅÆÏA#]zô—>ŠÄkR526y§‰—¾bãEZï[" S? çQ±¯Œ^48¾ËÚša$iìÞ´‡Í5Ì Le ¼ÝRÐFÊk²)«*xï/xù @èÞ{ˆ%* ±² OðˆM¤ö1<Šh¤ÑqnË(Ç•’ëmI#ö#ÒÇLƒ0ax \’, ªNkuÔ,©ÿiH?¤ ¬ŽTÀdÈ0B³Rdæž(m…‘°ë “ôv`€}Bêƒa@”6VéÏãN iXÑYžyM˜Êy݆Ûû"°‘±A"5®Þv§Ì ‡)`^m±™¶È”Ô-.r:õ"ék–_Ž/Kà#?Úš„ø<,L9T]Şȧ 0`˜ #»ò>c fhúƒ4…šÍ;>Y¾ñ°¼ƒM )M‰·mY¯dP~þ«Á\ÃH¯Ù+m‡yÁ$5é'ó £‘óöxæ_c)06°ý“À0Z?Il“ì ‚Úàˆ5 –xx¼0-¾E{4…dâ™C<T¡5õB`}!Äð¾œt4Îo0L%4‚æ  "ôÀH°«¹¹‚qª¬EbŒ¨Ù á7„/Æ€¶ÁD5› ψŸö‰ 2Ì?ó‡ Æ¿hh³ÌÚ#$åuy5œ(›Íµ¹.²Ne]óók}Ê`”dGdRájplu¥B5g!1øý‘xIcB´"€†…X>øÌ• ÿñbBågÒ¤Yˆ‡Å ¡¼Ä( xÃø¾.å8™{!†)sV†…é‰DÿðÂàô´•ß‘P¨“CH-éžF²ãñ#›Þ…Óa¦m€#v0 µÀijµÕ´uQíѺѦ;Û«¶Ü) iˆ…‚Ä©L2E\Žß |À0žG¢æ$ª,Ò,õÐ&ÔAÆœï0wÌ%s€ª6ÁÏÄÖ‡Ÿ0`b#‡Ù1FrÁ“…¶ò S?æ¤e>cÁ ѰH˜!´–‚©ƒoKñûì;h ›gh#º ðþ(E\€÷I¤é³×ij` ÆL‹‚¹èâÖZ°¢¸øñ›³×¤ŸÐ@*×J VÏXa—©/‰M]ã#Øo•,hTºPr6ó„yÂO#äK÷š¡Åņ[ñÀHŸ‡÷ã»ö=ñßÄ~ ÔìHŸ¡c„/{@èÚu/InnÞoÌœ¼‘@è=LZ‰ÀÛ*''ç{pùTGmÀK¢fù®.<š s¶X$M¤¼ióÖÉÝîâ›öè3Ôº…N ¢ê£ò²¨^Lx.Vx_Q^€-^ïë$•q ¼³ãw¼Ø ?ˆ^Ú˦ hao²Ù‚¥ÀÃO÷ƒÔ†ŠÇ1I¤GúÃ@ECµ£§aÇ¡ÇÄD‚4ÊÂFÍ5Ì i”…¢ @D2¼x-MÃ.å}»°v$UìâüM¿‘`P/IByÒ&ñÀ\cÆXK6—ì“Å:‹Dvgè±büi#óŒýÛ&î¤ß´&ÀbŸ&˦½'¤NlÖ:.Íð13…Ñ 9ê;ÌiIArhß0´ú-S¦ ãÛ:víSâwªRaîl(̚حÁ ÐE3]Q¯#t'4h˜ðŸ$dîYï\s+õÊöJiW¡)¯ã[gÕàäA10$É¢0–ñ Xøw?úe±_±Ð4¤$$:ÞAXPÇYx¨,ÜgÓI i˜Í ìƒÜ'­;ÀÏ c÷¦;ã,4@ éUIÕú"Ç_ÝÿåHíOÊó,z<24ù"¦=æ·L‰ÉÛ :Ãó@%¬ó­Ÿ†Œ V: ûêRüæ¤ lÏ|Gߥo<ƒš¸2žü«Núl>þÒ7¬Ôᾉ}’o`òáÛ~òFÚ¬á:uñó;Ï!=1×þ¡ ­_ƒkÇÿm¥-Ä)™Œÿó]ÚÜ£î27Øê‘tÕ È£âªT—jᢖ“ÊÐé%SrRÅýÚ÷E‚DBhU­Ö£šÉNó¡Nr¼´´ö•vT8Þå,^õfœñŠ@ÆsÂF7K>~©$Aã¦-ÿÕÐé†h¸w~A­?WW°¨TM¾ÑtQ€7.~f¾6%×›ÿ^¦´Ï$§˜²ºŒ÷NShÜÿ1¥_ÏÎßD ´êöåâBÚAÊaçkïûÖMÃáË­¤8R¢¹!ÕúI3ÓU+Ù¸4|2´üû¥ E€9Õº]—ÿÀvýÊ•/"z©a  g³1//ÿ×f®‡Ý(Hrrmgï¼ û¦{t¹  Öo°¥ao½t9’ô=˜oÝLjUTÊf‘ è¾þ}ÙùÏË/øKCŸÓ a$žïàiƒ]1þ$cTªg±‡nØ„d£~Ã&h>·&195+ÁþŸôêiÊð*¼ˆí·æçüN‰aÏÕÈúì;Ó¦’õløeT¢Rzq‘Þê‚yaðˆÉ€î7t¹-A­,émÊ·äûŸ„Ì$á$6wMY•êSð¬a€ zŽF×®] þ¤;ûx­« ›k×¼\y¦L3å›¶ü7ütíñiää.=Q‰Jù•#BoœRlԤſº ”‰€®/ù>lTÒ¿@BêØµÏâøG¥ú”>GÍ[¶ûÂ:õþŸóh™ïLJÉ®m™n®&ìÎ¥ã±PÚuÇu¯ïÇFÆ+®Ã?4åGQ‰J9—:z»hÊZSZÜ€ðQà´IŽ‘ÞnÊiSžˆJµ*™²Ñ”Qi„|쟕a¶öq¦,Iã9ˆð¼)o§Q>»AàõA¾®)Í]ýQ‰Jy–æÎÃ';+º¢«œ¯ùin®u1åï ¦[nx£+º¢+ºªÃÕ8C˸{Oã9Äè¿Éxÿwšîd%]ˆø“œËÛ~SŽF%*Q‰J9•n£l†³Õ§«ý¬v®¹e~¡Š3åK”ç³âÕ¥{áªÑËí þ°AÃ&׺mç S×>A·Þƒ£•¨D¥ÌKwS:uë´n×%hÔ¤ù?fggÿÌàÏ™,R!¯ÌÚå¬i_ù%ìÔ%ó:ÈÏ ä^è²Ù·%''çí;õ”~„aãx'GX9•¨D%*åQ8Žû¡99ñÚµç@Lü…óÔjPn 2¥kyl®å¹®LÍ™¼ƒäxºõÿž°m @ÓìDÎýQ‰JT*êÍdü-‰ ذqó1Øô\–=(Q&Wº'×0¼hÊÈ4ìHЋMyß”ÞHÞ‡ê7lòOÆïOK=G•¨D%*å Â6® ú›µlûÎ-0Yl›†™ ¦L™¦W›kšeÌ¥:×Á”S¦üm†›kÓ jþAÉ tI¸$ÝïGs•›«Øhf„‡­W¿Ñ?f%ÏʾÂ9d´‘•“¡WÃ?›ò¦³kè…=w¦)ß1å?Ýsÿ+Mw2l'ï’Â9ݘ´7»”4ûQÃ4kAyÔMì¾ú•˜Ýຈ9šÏ*'ÝÙ,Å_Dó“$@¡%§Í]C,Þ?Ê*uq‡³”ùæyÙÞ1åßu~m…ižN{°{ï!ÿexGv]+L&TgÃeÿsrr<>È<¡±ÿØÙ Ví:ò;ùÍøRÖm"… ßoÜ´U˜Y#=‚ýBv‰6nöö[`-ŠÅ9í9(ü½va]a&gç¿vÓkRd ©ß°IЮc÷0W`´‹§9"A«¡Ñ xƒ¥<¹¶+ƒçs£ðï{`«åݱâLN«Õ7åâæF¼£ðæäæJ\aÜ[†™äÔ’ûý‡Ž·!4zHîµÂ:õ,ðî¼7Œæì2#Q—¾üã”y´.¹\Y˜-ø.î{arLSH­Ä÷›6o—ü’o£†ùy´Šï)€·yH+yyù’rGÃî!¥k¿(ôÅ^›§ëgbŠ"jÙ5d/ ˜_rmѬ/¹~kRÈø1¶ß´‘¨¾ï¾ùY±oúIS‰\¦}Ô~û©á“Ž÷»Ås qÏæ4ûT@_³€¼èÚ©ñ_yŸv¥²·ó™™©+Vϵ¸¤¨|S6«õ›æY+ÑÆúÆ»$keì4j*ÙF4\´ãÇ}ÅÆÛ'\6NpƒÍˆ‡ºS™š'v$9BL€àLC£µ2Àò=‚•G69ðæçH˜Ì7~ïÏ$½Mó–íä~Ç.½¤ø‘n“¯…'ÿös¤Lò‚?;L“¤)PÔ(òÏõè;,hÓ¾«ù~±iij¤dÀ{è¾sÍkÀ°‰BÄ--èñÀ‹4›k€·ï ±6€Ñ êëÖk¼¼û¬ZÜ {švµíÐMÚF25“ÆI“BÂ(È©E<\ò©‘»w:÷’´î´U)˜0kôê7äˆðïœ×ÇîÒ͸ÙÑH:Ÿ®ßuoÚ6Û xsóò$q$ÀAâN®Ü<«´ †bÀûÑ/eQ¤µÔàC•ä™:uÄ‹ Ï€1G[·ëJ£Mšµ 7i! ¸ñL¼-Úˆ©ÀhѺƒ•¾‡Œ“kÉTöDàígžm×±GPXXORoßùðkì›:[ÄbŽ^Ã@0I´hÕ!hÖ¢­€vfÆ€çø?õÚÀå?©Íô ©ß q““#÷Úvè.¹ô`T<ÿ‰€}io¾˜ëøî÷?ùž|°sÁÐå7ÛÎVÒï\£‰p¤ ´¨0ù ¥0v<›C˜II…®ñ~XÞ-¨UÛô­}Èp(í:õ0ŒepP·~C™ •B5ïmZn´Ÿ:uëÙ¦o0B ý¤¿$AU‰|’“bùN«¶åwÕ,²2CC05½O{¡žEÂyGÀëg~9Y–áŒO®å¦qNPÌ1å{`hà|Û~í0Æg¢” KÃc¢“™ðÏ6ì¾?ÞÀ«vÝ·Ð)€Ù‚ÕË!xIé~èÞsá=$JìvØZ]Î(üErE¢ì?d|,€Œ€&]‘2=h€D¬Ç ­œ4ô¤­ÉðÞ~⎃=#~ŸL€—ݸÙi ø“¯Nrtsà=Ò˜ð½~ °¦¼Ç#àM¼,nTÃ!#§Šç’ ’&2§²ñ.^½7!TPˆ†gÉG'÷Íwv~\Y7¾¨ÃªªE!¨'Úxùud#L­’6ÁsÀ=<âÚ;ûa#Ih›cÀ[$‚T X“íé`Riõ9xÛ©# *yA- ¨kwÜ-Ï’P&•ýÈñsD 笾J¶iìa<ÊøðÊ`üîí‡Fð¼Ô³tÝ™+î;SÌx‚ÐV5±Üùðë2¾ ¼Œà £äyÝDeŽ^3÷Ð67m)÷êÁD`ûž-ùà®Ú#¥g¿á!˜ã!‚ F—qF«¡žÇ^úFÜô0s4mÞ¹ÇðÒ·½w>‚@…Ý€Íw 9nêb~x›<÷æKݘDjZÜñ¸‡ºˆäÎ=Tjl¤»ö ÁI€$;±š{¶ìHh”1d¼ÆBwæÂ á¸ÀHh+¦Fß7ma˜ËKt:‡˜μò]ôµÛÉœðMlð¯}ükaŠÍÍ3<;~Ú©gÉš}òŒÚÄÃzÙz0)í6’½/ñr-†¾Ã([¶qÚŠi‡n<ÂÀôÙãFÒö\2^°°v&À;Ì|Hxÿ,˦mïYŠ{ØXS>1å/#à-£Í5£ö½öɯe[‹T]ÇŠK¼¿/ Üù¶,Z;j_ä($ªj·±ÒwÐÙð’ðòþ¸)‹Ãz÷Þõ¬Ø8aDõZŸ xQÁr»øbh^µõN‘v•)Q1`ˆtÙÀÙÀ^6§ÜóB†3n =nÙpÈ_LâE…tÆŸX˜&!æ°GCÁ¦ªÀÛªM§à©W¾'ÀË\ÚºrdóÎïûð.•z6ï{PÆžºØ Œ«ÇHñ·ÝÞHûß¶ž x±1#é*ðbïMÞéó×…ŒsôoÚÀ»Èá^Ú—F+íb³laÁ~ ³Í!Ž€÷F6×Ìbœµxs°a×ñ`Æüõ¡Y€ ì”É€•ò®G^ŸeG ÛvàaÙHR_Z¼$Xµ.Tr¼ØÈcã[*‡5|/›\Ø Ù G­À‘T±]&ßdÀ ¨?÷ÆlûÉÒˆ^€ôÄy! °q„ô>fò‚Pò.xÏÅ/c;i–í  êÐL˜¾4Ü0S‰ óŽH‚F¢Ä×óq6^3_³VŠÆsÀFÊXa?ØðByÁ³ñ&^óMÆ>ðR¶÷6íìö0RlÁ'ŸùPÌdÂ:guð蹯ÅI¼Ø©9u˜x‹‚%k÷‡û„éËÄÎ  G憴€wg¦'×rÒ ßXp‘yº'øºEÀ›ðb?MdÕR¯†d(ØA SðESUš1ÌâždÆÞ{À(Yøb4ßâyU;Y˜þ ?]¸J!¡ñ,íÁìB2w2ÃTšôôXªfq#M"ÁY@¬#ÀE?ÖUºÅd€‰×¯ºõȽ±SƼ:tsÀ7)^ìjJ!iªŒ×Ýω­à‹ÍÓ͵ѓ懌S©cˆ 9U˜ ‰ôË7™3èXݲ0ÐVm'žxz5àqÆ/fÆ’û{ïzF$N楩sŒŒ›§‡ =J ëÖ—z0odf„)† ðúZÊc/Æ€W7Fi¾ÌŒÒ²‚¼6vÖůÓ9Ò?ôÜÇÁ¢Õ{åù»ûR¸9†º €!¥!ÕŽ5’6BÀ4Ñ1]¤O$&$¡qS p¸€z¾Ï&§ Y¼Ãÿ‘Ù\BªJ”äȰQs1qð>Ò\r)êªlñ }Á‹A#`@"EÒEzäXë ÃTx–ßÄ?ÖåšmwɽmO…‡ØñgãŠûxuØqù¹QçŸÉ]¹éˆà ˆÞ&ÏÁ”Ôo£>Úê­w‰È°áá.¥]æßÙ$DjÆSðƒl|sÑŽÃI=d7¶¾ÏEòÞüå»dž1ÿ TÜ7Q$z$[=8¡‡ø?Çž21œ`E…-Œè»R—ý¯1åq€¢Ë¡öÿ²â³ßí|xÏ%üö?L¹#͸x£R¥ÏæcgåÄ v]9`{GÇtàÇiˆÊÍt€¢ðf|r-;©v³ éŸRûkï4Û™ò-g7NgÓN€wÓ­Xw'âF%*U¨à·«¶æsaž¢½^ŽÆèf)lx²g’ääÚÔLT Ì0rz¶{ç5ÝßÅñ )m3ø^'Ó‰ÏØ NêQ‰JU+[Ãòpøw4.7_ÁÖÏQôœœÜDàÍË4¹oº'×½nw±x}àýK—83?àÍÎÎþ w\v8Q•¨D%*U±€QrŠ2;;x Ò4Ù†×gÎ$GÛëÎkA÷?½¿ÿÚå\kŸ.ðæç×úlùÆÃâ¢Ä‰›¨D%*Q©Š…Ã.¸yææ³ñÎt§€Ó¾ ÒÌÁ9ä¦\õ@ï†\|†'1vÞYiH¿bãÅ/ò’‹!•¨D%*U± éöËds-Ý«CBÊ\ÅŽ™ÒÂ;¥†×Ã?xϨkäÕ•¨D¥†»“Íq‘Ó¾ú›2>?^¤Ù¯™29‰1Éy­K‹E'‹JT¢r³oNV†(Ò͹ÖÅ™N–pjM¯>îôÚ¯#àJT¢.~!í.M㹆Îí¬nšÔw¡ÒÚTEàM–ì±"ê,~¯¨ÜÚ˜éû¥µåB¶¥¦¸޾ë1î îßòÃt¿³Æo(xg¸ü•i_µ’4Ovµp§Ó¾â€:»ŒlǼ È‚P` h.}W¢\Iüwg|Ÿ<óÚ%mÁN4÷WæIùŠ‚‡Ÿÿ$8|üÅŽ´ÆÚG*‚Í$‹«K "|ñêâu¶…X·w=üúMy´•…J4·ë¡5NÍá3LxJÂ?¼§´ù¼^e×~ïOËA‘’ž#Èt{3æb+%,d¹l®ajø¿Îv{¼:/'ˆ Å9{þNMKÏÀ[)à ¹x9^âiÄI”Füßl Õ%JD«àÃßD”ÐhÏ^òê ÒÂy_%ͨ”Å¥èÑ6J–ó •D6¡¥J]W“.&r¦‘á€ÐŠòm¯-d‹˜½xKpääE×–XǤcâÚiß/’`b»’\3ÖŽk)¥;<ãû|-Ìý¿ãÛàµÏI‹é½w­Ä~…cìæ¶˜Tš‚Þ­Ú#‘݇dóW|,l;ß8zâü€S„Æ$ºÁÔýo$§½ØÿK~.ö§óˆÀ&fÑ1“11…”Jã§/f cx1a\“ý]òøÇ¾zÄqÂo•¤A•¼s]è„´¯Þi¾ÐÙË,|ou^ˆŒT0CFM•Ô3»n?-ÿ—8²nb ™xÿ“ïŠÄI²ÃmÜa@?>t‚wP ©¿‘@’oóÞ–}JÎ3’<&/1nÉ@Šž%å qdù†/GS©—¿IÂH]½ÖÅwöÜñ”s¡=$:ô—ˆa,¦÷œ•دÉï‘‚u;ï1ߺ]¤åÝÙxÍ%“´×´…°‚Œƒ¶…46¤7§-»<œvyÏx™cž?pÏóÒOþFªæ{G¢"Þ/ã±x;`뇥Ÿ Œ_µåNa L¿ó‘×%rá'ÆŠPˆhDe³ï}îÞ;*ï1¿WI #»Áß©ÿ¾'Þ–é´‘¸ÇÄð% #¡'©Ç¦~ÿ¾´ßÎ{ShP„¤¤ÿ{Ì7™SÆŽ1Ô:Ѿ4 ˜d×0ý!”åì%[$Q&ù×HOèGèˆç ]-qc#a:Í÷m =Ô¿ü¸ç8uG¿BS§¤MÐ s’ x ¥¹ÚôùØ£oJ{™[â!C÷™A8J‹Çø”q&t¥2 ¢Þé0—Œ!™3ø?m¿ÛÌ }#ÑêšíÇ„Y‡wË‚â»Ýz–”<€u;SÚ „:yŸ¶ÒObó®´sö’Í’aƒ9Ú¼÷AéG{yn¢ï¸ûy‰]˘jla€¶(Ä!fáÓ/þí7x\°pÅna>€¿Ó6ÚA\c2óbÎ!ð8é˜4 —Œ©ÑR* ´‘x·Ð Y"È.g@q̤ù"ý]¼lÕe€âÝyÛi)¤b0IŽ `cƒûÏ^¼Y,"fˆdð›ŽÆ^Òªï?öœÍqfêà‹Š‰Ô‰Ô ¡ŠÉàÝÏB³’×ú÷ ¸BÀ,X5 ¬½€-’/Ylšö¢”ÀK&c¤6Ƈ¡¨ ,:’ CœÉM}H1,`f“Vˆ@j^á_¤JƆsî’` HÄÔá"a"Œ¶P²ëÒ~"!ù½þÉŸŠTØ:€0öbÆë×N@˜ҤT¾C1æ© ³®ö°\yP„dq™ß0Ïp&º ÿ¤º÷ôÛÂpùÀLÄ2ÚK»Ñ2’ej Ð™5Ð<^q™{‘êœñg-Ú¼¦½$¹D(˜½x“ôéЇ^9lseÌ =$ý fž Æy‚,¾Çš¢ïŒ?ÌÀò0´€‘6a¿º143Eˆo‘ÌÁç¡ Æ™ç@打10t¼Ä$~Ô¬²T3ç´ pÇœ‡€ƒ @Ày•Ž?ãÊC¯Œ©dN1uÀ˜æf £…Ö¡=êe.ªsŠ:Î!àÅ—÷_K)ÿæ² ÷©z6Þ"É3ÙÈâp<7ÍHÇN½¶KÓz³–5«áúH'H/,¾¡ƒä©©Yœy_¤‚â6Þ¥vÑj.I%*ï ¼¼e†ÏH:›{^beܬ$èÙÎL}ä\ƒP‘²{õ!ßñ!ÑÖGZåÓÞFÿ 0€‚ã_€<Ö–"iê0’ŒˆûׯeøØY܇æöjÜ3¼¦þ ,f³¸ù&‹š… Ðò, ޱÂÄ8’B'ì³i'`€nÙ÷äUS›,À H°Èa"jÑMC4$J+>-ãoçú—¢šC$žDµ†!Û²ZÚÂøßoæ©ZÍC’ØU2}Å?ÿ’iSÿ"ñÚ„¢Ö¤T½ÜöÀËÒ>æ °,¡A˜A¿ÁcEʆöhcÅØ#b  0KÜsF aÜ>"yœ|SÌ¦Ê˜ó¿± ‡ð#³’)ãŠôÛ©k_ÃäW„c…ô ãAƒ„YÐ?ý6ÍÐ’ò3FÌí€i ‹]ÜÍ  Æ4ÎHÀ˜6|Z­dà-·“k>ð¦[ªä x-á]•¼Y9Ò ÏA̶]_¸•Ïe£{à q ¶±è°õAÐH@»Í¿,&8µDô7ÄÄ¢I)ñ‚³Y%¾Œ,@S%^ ê$‹S7$[¤&ìy¨™Ø1õ7ì|,¾Ñ¶CwQu§Šäópãì¹7¦|‰“xO¿eÛbÚÌ8°0ðH@âÅÔ€Äd¤À†dÈ÷Ì ‹Vß*ã¤QÈS¯~O$QTr€›oÙM¼kqÀ €¨¼KÝHZ ¼¨å€st·‘duÌXŒd=†A¼~û´‘öxÿ˜¾gú€¨¤Ìûe;NhìP'ëI÷`>ô vw€aâ’ËlMÛ0#)ðòúÈ;|WÍOŒ7ãJËÃÛ#à]šæA´¸„”C2Þß:P-©öç3SzUMà½Ã¯%: ùmÛw“ ÝÀа±(,2é[&’! i”‰Xµõ¨³ÿþ‘¨Ñü Ñ£J²°WRm¼ýš6bÜlÙ¸ã;,йFRp—;/ Žô*ÆfígPl†`Ò€ !P.R,V$,êDEeÑÛÍ­ðvé9@À É©)’>‹w‰µñZÉs™,jÚ£5  ÓàžÝ¤³oQQ”T 3Brä0ÅÄL _—”Àk€‰õ™×¾/ª2 œÅ'ˆFB"Å~šxMŸÃð=Ã$xÅŒ=”îÔCÛ‘ÜQñɯÆÜ ]©É•ÔéHï€-ƒ]w²ï…¡mÚ{€ӫq»ðó cê݄РcÀ›º$xèìÇrSŒ$ˆ=œñeà »'ª;óJ¼ë,ð2´s m„fVóš1ó©À«w0eLRHÖôÓÏ1¾Ì t@›`Œ$¾DkSà¥.hæ»ûŽ'…®`4ÌÑ„à=cÀàcÎa¬´—±A¢e=Ñ^¤`2bS¦Ï_të5X$í…¥/v݇é™þ±Þ '~CXXk¾ýÒ»ŸU&ð–[XHxŸpîeK)\ž*çÇ‹ SÎ1£–X"­™ÉÇvºÓmCåð’8!^L€%¶OÙ©v®RH!H¼ÇolŠ ‘s`£~¼¨SØI¢È³,¤A¾]“ +}KÀÉ€ŒYDÛ` B¤.Ôj#]’Ã#/|UÀ ‰+Þ÷‡"µ²ø‘´ùžøõ2à§`Æû Vì–C ¶…¿±ë±ÙÀð,Ä cCÚ؈C͵æ‚kaý¨´ÿgñÓ•$afê¡€”ÍØF#ãJ w¤áãO¼+ ]U~€Ú‰ÞcS%Æ’ñÈ& ±xõ>OÚL{Æ…÷1y\Ï€?”vcÿ‡yÀ˜CÍÂ/L»'LÉ\#êï03˜*¿QÚB‚¥_|‹÷±ûª–€·ã :Ê h5Í+t Šþ˜[š:! Ç\1nd«öýxIEP@¿ahIÌ t‹æ$óæÀšq@;6‰tUÐ YæÀÇ|Íð7f!uoÜÐÜÔ¥Œÿ?øìG¬xw­Ñöè´É÷˜ãŠØh+x3¾®gs­úz5$9MÄäB\HNšÊ… eYÕô‹p³!™dé¿¥>uTÒsqm̨®kqþ ñ¾WeA#ß/*Ö– q6´LÛ’¼/Rø`¦ö§Nf—N³¢ð»™¾§nX¨Ù€/ÎâbƒG\Ä.•ÚgÆ âÝd‰:ãǶ´1+*6‰ýJkœ¯ƒnR®LöÝä󔊓ýV§\HâSøÿÄ6Ø;´²J¶ñŽOìZìhoãt²?^üך9pnï¾—]•71-v>$ESS<ÉÒ•Wû#¬—‹nÚã»%lØu§Ï_/"Ò&YvÕ¼eÅóÊß\Ûé¬ev5p&ƒq.½ûïœÄ›íòæüÄ%¸üK‹÷ 2²ÊÉy9IZr1ÖgœÞúZÙ{Ëÿ,ý gã+½¾t³—g»¯?‚l®:ÀdQ¡fcöÐTéå«ãZ¹Ž[yôÁ×\¢X áµÄadFÞ ýJÈ@|›Knù'.¾îm^…ë‡.Áe¢Gÿ›òŠ)Mo¦èdl Ê'SwÙdž{]»¯ÎîÉÆìš—3Ñ£º±!É¢TR’36hJiíÁÆ‹}®,@=88u=ïrr û$¶pì‡Ø”“Ū(‹‚=ŸñÔÓw–Ù[›3í¸„^°=sHãz'ßåûe— Ù¶IíÓØoåÔßM0§à­—fB‰¸3ÆÛK0C\ñÀô ïtÿîw›Ê _ÞÝ¥˜jðbèg×üÕº;]pçÎÙb—ZNl¹{êZ“ÌÞªnOj×:ýò7eçßnf%Øãíq*Á]ò¿íî[I6懛ø÷ðÕÓH~{ô÷pnç$›6ºûÌâäû¶ïE¡ŸdìèøôªÃ~²þúß×¶hIsƒCìr‡u&Œå¥ËEIíØz†]~68ɸa÷ýáñâdãr1¿¢Çϯ[ûĆ"Þ††ìfûKÇo8¤—°oÈF§?/©êRZR-…öáÈ÷Å{ĵ=Y<‘Ò"£ùñ?p¥Ä% šµ¾·Ï èó±9,Jkkðf|ÎlCŠßÈüK¢ÿϔŠ ü­ ýwx¿÷q)6äš¼œ‰7 †ÅÌN1Ní¸Z± |àE ¹ûÑ/ËbÇ= IÂ'Á6.>ØÀmIv² à ð^øƒ0â^þbâÿ¸ü¨Û‹Dv²/[oü*q»â›¨ÕHaœÎc žcE¸“ᇋ‰•9bœï#µ³ñˆË'Ê4Þ€ÄW0Œ‡~áRÆ8mñÆ`ó °Æ«ä¼gÞA²c÷zú q ð¯€$öV^\Óøícìñ¾PÏîû»ôô)Ã]z ¯ŽCÓ_&`ø¢H§ÄÃï ë1`cLÐO<sEžÃkDž;üxœÇu2ŽÕflñ¶H¼kä„!‡4aÁߪÀkžW‰y½ûžz’ð6˜§ fü 7|¢•>xEºv§ø—¾+§ñ| LÇXãw ÍÀè7}e˜wè‹Ó|¸«ñ;߀f™? û%*Õó¼ÎáËàòUIÀ;" ×ÙbRm“¿ ò6ÔHûÓÍûm¸)¿ñö7.æIß~Ï»ÿç¦ô¸Y€+μã~ƒ‹0ÄÉâÅÍàeQâZˆ®œ“ç$ È"ßI\µ#žÃ G—΂à`@"ðp†M×4êÇõÿT6 O°Õ“EìÖãËÌ÷Xðá ÅïÞg¨¸ñ€ÄYymà ØbòàÚÈ s$j ‹EM_é3’Ñ‚å;eGYiÜ^×SmøÍrH€ñFÚ†vhÇrŸvš}Ñf/Áœ` ú±x?u‚ÙcÖK }Ó‚=‰y«Ø¹©›ƒ0êaÎU¾Ë8¡ÁÈé^Í_±S$u$w0Á•$€”)D¦#°ó;hø$[G’X5xW:¼LûêXB<…ÎÄ€þ7ÏdÀžö¤Ú6e¹÷’ñî·-å(]’xíÁƒ3¡Íb;"P6‰ThÏç_5^U` 1 ˜±àLÎzçåÕ~‡—E€4ËS²“:ŸÄðýèW¡} Ô9ú  ʘ Þ`´…YœAÕE¢T}Tr¤žuKŽš¼'Êfsmš)ëRü6Î3ü£snîBü­¼_O°ûá¿»ßþ¶”pi ð~j£yÕà5ji÷>Cl‘±'¢6³p%CŠbÓ éiÛ.‹˜…† •ÅÀBä’ù 1~¶€‹PѣáÄk$.¤ŽS?À ~²ù¤Š ©–}l‘€ ÏÙp„ï‹D cPW&6°Õˆ€}xñÜÀŒA¹ä‚žÐWl¥|“1`ѲmöGb¯a€*€Ás0,¾oÍ1 Ÿ3 ‡>Úèm_ÈÂæ·nQ'vÍN]ûÈ÷i/ÀÉwQíy&¨ÒwŽLwí5HL |[2ÀËa—ÖˆqH€I¥iEÄlÁ\ i`ž˜Ëf®QùyÀbüæ—ÁD°Cêd“&׳߰ð~hM 0Qõ …3fo}@ž…2^l®Áx©¦.€mÿL ¦ ( 1 •a’BÝGÚÆl1}Þ:™‹Ä“|x¥³‚ø Ä!ÁÜÀ¸ãáÁ'RìQ‘x)&3´S‰AA,ê;Ÿ¦æ™ IºEÀ€áCkô§&/›Œ¹¹yìi ôplh){YÅ.³U ^ EÀøæ›N <ûî} A€—8Ûnà2w*¡þV¹¹¹ˆŠœÌŽWÝ Ò‰–0YÐ,> *Í™òóìGŽX·ÊøhôL»ÆàvÝf ˜w°I Sà'±¼ú‘ZX ,êGzt5§ óâÛŸ†æ yÎ,êaÑèŽ<‹;@)àd¤DÚîw± ÔSƒ¢;娑‘|ñ@ʵ¾»V¥e='àÛÒ@ŒÂÆ<Ó|`äûá,‘˜iî_¨ûa*ó]‘¬ ÈÊX ~HzÃÀ4c2kñ&ahl8ÒF´ Ì Ô˜ñNÌìó=Ù@šåÆEÆï²ÝŒbþx‡º)6àxnþòҖд`Ú¼ÑhqÌ ®c³b Ƥ÷Ÿ‹J/‘èL™6Ù”Ñh†êÂÖLû5¶/x46l˜ÀGàvèÎü1–üÍøYïŠwй”A—lb»æYÆ’ÍM9’kþmžr¡K™ksãšÔC£­|;.`ŒV tfîÑGh)ð«ï)¹/dL³³sþ{®mw®¹er‘ÎâL)¡“pbûï)ï÷Ýé·’NÆ]f³¨&LŒïšc³|ú4²PÂß.p‹û›r߆–Œ1¤ó »Â‰Îñ00¤Tn ûöÕ¸÷V“øz®Æ·ÇdÜsÒ§øçø?›Dz6?ÙsÅÛR$ á\‰1})Æpî­ŸKåay»x?4Òïë³Úývܼ”0.ÉžS[m²zã¿q­ØÜiÂ6¾õÓ°/‰c¦÷T -žóÍö_765ÛJiô’ŒnÝöø-3ü¼û>ÍÒŽâsxµÚ¯mkâ=gee– å¯rqoÒ¾H¿Þµ„ßûšò£ ‹WÉ„d]8H•†O”r€IùT¯þ#~÷tŠl»ÕÙË!½ã®×Êì¸(Rnkzp£ìÚ™Éñàë=NZò÷oèxíõ´§LÆïZÙÖWVu•k©¹G‰azxáÌz5a3­±;½V&›k¾?ïëΗ÷²¿q&ˆ»’ ^ëâ5`–øUš\`VÝz ÿNòžÕsCåJÜW3 앨D%]i·(8ñÔAó–í8(¶éFÍ ³MÙ’fŠ <¦š2ÆÙsSØ…9Ãsg®(í‚[|“]¯gs­*ÄjÈ3åP½ÿ‰ìº¬»"Ö ¸_v¥šYö‰':”ÆX•8Ï×’þ.LRõO.C£¯ ³ò˜ëëùfM§9é×ûW%1A³mþÝ™ê&Á/Ü^›gxÓMY_€wzA­Ú¿¹eýa¼»â'’ ßä5CÒ&£/9³ª‘\•~d¾Ø¼ïAÉ.q!à”åì—~γf!ö '˜{ñëa¶M:J&Ò¡Š’ªˆo$AOI_ï|*9ì´^)¦RóHl’4Aœ Rd`®Iñ„š\sm¿yd³”ïšlõضé4F"NÚAN¶šËðm¿ˆ!S¿Aãß–^Ω^ ØNÞ:jšä«Œ DŠ!mOÇ®}‚özH’@ÎhW§8¼$´4ã4hØÄ¥*Š@6Å\“Â'œç÷Ä¿“&‡#ëäãÿdÛ%ÕÒÐÑÓ‚–m:²ƒšÊ7H6©iRJNædK&áe;Wo»Žæßν$) EI¥£™§Kœë÷>—ÔJ:×䚻ѹf͑։oÖ­×P˜ 9ÖHDú*’m’žIצÍbü*¸¼CÆèš3±¤ñyéŸdÊÉÉÉùóxð¯N¦´Ìô:dÅg®Œkž ^eBGÊ&j–—aƒa¥¡kÅÔE’ÞùEÛͽâ2Ë&2K.M»}öš÷ìN]+ò¾W”4ëÕÔ1!ð@8n€×.qaúm¹è2"k*öTýL67Ü sÁEbu^KJÀ:>š·´q*^WQò6\ù"å7Ÿßtë‰pž,ß÷M$X’D6kÑ6xøùO保 ›Êó9¹¹A“æ­ƒ†š…ßhÓ¡›€tªzù>A´›µl¾SPP;Èϯþ¿¾Q\†& ¥#gŽðç€ÜqÃFOÐGjVuß§½‹­Ä¤×k)hìšHtÃÆÌŒ¾úñŸ÷?ù^ФY+iàûêG¿ 3"“a™$­´ƒñÔ€VÉh%®? )’.ùôbý'α]3 ôŒ'9ëš6oóoYÅoβqoªÍEZ “Ý{ùMˆWT)Ü BkÕ¶“Xazd5œ¿„ƒó‰g‘H…Nºr¸çy!Pòk=óú$û+I ‰®æ6©ÎI"ˆJGÀwÿð,^¨v¨¡HUØ“Pg/Äå³²àJVaõî>rFÒ‰“ã*x‘xyöÖ£OK²B2û pÿ™÷¥-d¦E&]Ð6³˜©W1íA k0ß@²Òüa PÇŸ|Çf´5¿aš! :Ù|É&Âwµ˜þò ý$#íÙ/ÿ8nÞÉVLÛ0 òó r¸¡ÞsÿÁg?ŠË³F2J’q¢öòMA’L3U&æuš±ÊÎΖ Ê BüûÈ _5Rm'_¤’N6iÖ:[²ò’ü‘̹dàÍË/ßpGBýN¾¼O´hÕ^žíܽ¿5™§‘¢aî÷ê?B¾-Ýe~#k0’2æèŒqãwæ‰ÌÄСŽ3}>âhzâ=ž!;k‹6œ64 ýXû(®ô‡”ìüN†gl’ ©š¶ 5{Ä£Wê…> 1²S =`º º2ÿ¾æ{»<˺0ïÑèJé ʘ)HŽI›îqôŽ¡HšŸÊ÷ˆ‡Ëó,IOùžmdÈÈ©ŒÇ…¬ø${5 ¬I%/õ0eΪJU=7ì¾?ÈËË'‘¤/oÚÜ.4Ò¬µHÁ`éÙwx_“TrrrEýð]zF‚—w_vÉ !R·vaÝ`àðI’ðDÊAr¢ÿÝ{rM²³s‚6í» Q+b‚¹eý¡ u».áB§´hÝÁ©š×Bà­W¿‘Ïò-¾Ù³ßp4¸6Ä:|ìLiK·^ƒäÙúnq­Úr‡¨²;?˜¨ VmP9pzÉÖ‹Ë ßA /0ªn½KZq€‰çù6ŒHû@†ÚCÇu Có¥H~9Ý0ÞÁd÷ðÙOœl×mc®D˜¹b³(5UL:wü>yöJnxÆG–覎ñia¬'L_*6÷Š4MRÙ³M>Ͳ™sôjŸéæZeG'keT·ï-[w°RÌ jCFM“ …‘&˜,ÿ‡8Ùp»x¹(´ñp¿yËvÁ’µû%-6¶1  Dy¦UÛÎ"u°x‘Ê búʽM{OÈ¢àÙZ† û +À¤À QªÍzìiÌaúüµ²€mZö¢x©$œ¹{Ÿ!²È¸?ùÎ0'õpúYÀH`½ € n"‰©íŽñ˜±M²ˆÑ"€)¦—te>€ÊÈñs„ ©*”†jøØK_—¨Oj—”Ç€e!«4…ôC_‡žÖªÏûNm¬F2nêbYä÷~K€‚þb#Å&¿tÝÃTfIŠ€9SàelhýÐh(lz9 G€„öøß#}»~ šTJÞ}‡‰¿(’å–}†àÓÑ0m$MèF™ãÈß0Oè{òÜeÛå·†›IC˜à†„ûŒË3gØ•qÂafý MÃÆÌ 3;ì„qÚAB»0tBÈ|aÌïØ)‹ Í}ÙhM̚ÆvΜ2V› }3Ö„sð"=aCÁ]Ô[’ÔíoÓmDeÔMÚŽDÄoí:v—'%•ö‘hŽ=ú¦0^À `à;ÇN½!&îꀬ€cÒͤ= ‰ÐRà“x « àœWeÙfþ!$Æ1“w>ôZÆÀ p ¡£ a0ÌÇ áVûAE‡9$J¼€‰~ ZJG⥽Ò$Ú Lfî²mÂ|yÿ¡³‡À‹™óçº4à…ùÁÜaò˜ˆ¸GZr쥀çè‰óCF®™^â÷+ò>¶Õ&\§Ï[ÛŒu^ qÀëÖïò‡e=56`ËÞ'À:ìÿß:µ³œù›“$^kÃPÞó‚,æ,Æ–"ž$FЧ/˜, ­Š^ìÔ†f Ž>8áÅÌê´¹æ€÷x…¯ø"BWî± Ö  .ç;© ŽAÜ-ªÆD£ŽÂÙÕ6 x"É©ƒýŽÃË¢‚¸Ççÿ¸)ÉI¼+_„À ‘qŸIE½\}àÅÞ¦޽6”¼0 6רé·(Ù¸éWx±Å©)E™ÌB¥!$z^l¸¼uêŠyÃÞîf ù?u²ARË™5@RUc¤/6_Ôs1xazh/ögžÅ´À! éJ%/€W7ùPEßÁ#¦¸9«2˜Fò®äÀ‹  xØòd´T¢]´úÖ8úyîÍ?6À<ÉšjÊFd:6^˜àˆÝ• VÆù¼ÛàYx‘4cUÖ¬4àEp8O>{ÙB)X}”aN ¼jO-¼fÜ‘0•é[à-Šs'K^ íif:Ìf¢&ê„v™ãùËwYS—YcÄ_;¶pøwŒ¡ÌH»Ðál0GÁ Ôô‡ˆØ·+ÐD)Àû@RàÝ’©WCeo®Uð2apÍÚ…uBƒ=ö-À’‚t¦‹“M'¼ Ø‘`€ÙaG€´6Ùiánû#iví10´·¢f±Ùa¥‹ki/*6js—îýC{ UM €¦µ÷jˆï ”À‹4LŸÔy_$[°Hö;o;-÷yF@Ô|SCxïM^æÕP `kFòây\—tCð¶ûÏËûgÜb%w¹÷²ŒÉÒµûC{¸ÞÏÄ‚va{ç[ìê+xÐvvÑ/&ÁdÀk7ʰÒ'š¢°uÖ­om°Í pî;öœ˜q0!ÍA72–†‰ÉX¦áÕ`7×~h´’_y9cÏúÀ;U63Þ;ð>ÞñÓð~å§!•¼lÈ¢ö‹é`ì,éóKî°Çñ¯Ð°3É54íƒ(Ð’ÇecÛ5ÀËæã mb~b?æ }1ÎŒ)?ó =³±ˆ¤ÌšBCI¦aTðvOâÛ[â5Å”U7#ðBÀ8~ëƦ=éò‘n‘2±áªšׯ~8×<Þ€j%œYM3nŒ“D!(ݶ›[;Dª’Åeˆ…Í5 ķâ¯Ú‘Q­Q±—¦m f,„‘NB(p»báÌ»ÅJ•}ðšg¸7†o·¹ÖQÁ"Þ"ù^s˜"XDH÷bŽ1àÇßÀ6ØÓm®a¯<ûfLâU©YL40ÀFÍ H/þÆÍh# Yà.»8Ò’nPbófë=Ù\3sGi?ã€m”þ«-…û^i¯e»dì±ß°ª¹¨+æL&ÚVì÷Œ)f ½‡Fƒ›SIž3øñ*ˆažzº×3€W÷ ÑbÀ»Ä ÀË!nsÍöv^ûqSÅ€×™˜g^BÍg“ ßa̪…Lñ€—©?ßó¾aOĺ‹Z =6ÿ´0[Ö›lôÂÈa¤h4Ì?®HÏÐ4Jç´³ŠH¼_óLÙq³¯ÝH±;«HØ`! •@`¸,›?üŽjˆ;ÔÒµd³ðPNŽjω&¡ÓˆFwøYD¼¯D¿H¹ØÏ $À;uîj!FÔw6ø‹‰`XðHZHÅzr‰Ý^m+Œƒ:°;ã‘À}ÀUÐL©{®Úøü…-“üShµŒ ÝÄkIߺ“ýP¾É·Ùa#Š:a\,hî/ßp›h Œ HrŸq£ýx Jû‡˜‡Qæ‰Ì3Ô‹TÊ‚¤Í¨þ00¤1€€6òíÄ”:ÊX½üîg)Aw9Ú …ùfƒŽyE‹IôÝFc ñGKa˜û¾f ‘¼”©¦ÚÄÅK³s1Clâ©€ÓC»N=åYç¼¼¦ž%köËo˜T°ÑÃÈ9Tr_\$V6¬ºöŽïCBcæ9ÚÆ"óÃáHÑn ð8a.€'ï00õ2¶Ûã˜7k«ßqnŒ¡ƒÃq6w5¡ñð]„h‚ñGØÁ¬…Ä‹© à¥^yZ4ÿâaÁ†u*—ÁJÞ– îe¥^ˆÈCnF‰5 [ì·J;€¿n¨Yì(C̨À»n?-œ i-Qª˜úâx?ê Ÿ1„‡jËw Òeˆ¶vò™+"m…Á:Ì¿´çèC¯‰tuàîçeóIæ Cì´•>ÙçkäHOÜçY%~LöÅG¢ö'cL´Š=w<>þr8FÚLxȷ͘X©ìšÔÍÂå>mаKŸ±m0™Ý,ŒŸ{ÆŽM8ñ1õ'ÍB¥Í˜WlýŸ HcoÆ û*¦#TÕ’@6Ò7ÚGyÒ|f£ÁÝIü”Ú£}¦¾£rä´ØúÙýO<(’ªHLéã–Š1ȳfL”6õÀ_”.e®q›óè‰ûŒŸ0‘:OÇÆ  Ïñ¦îŸ¿ù~H?ú<߆qð; ïR/'k£Ðñ¹¯ÙµC{}'žµcò¡0.hPc¾Yg* u#Ùî;ö¬ÌñÁ{Ï ýÊøUpx€7Ú\Ë<"SêQ±ß‹BBôCÇi¸JŸëB´Ø³ðEDî¡G“Ø‹»´6éQPÝÈH \’ì½d÷Kë·ÿL²ºJ¯³(Å÷Kÿ¦2¦‹ Ï”ÔøùHg1ÆÆžzìµ0Ò %¸(ÅÆß›ûŒ£y¥×ÆRé ñ·8ZÍ|þK¯ïzÞ)NßÉž éárjúÍ|Ž+xw8ëAÚW·ÁvSoy9’v59vlÔR6*+øOT2(QHͨ\?ðöp¾¼i_ä3[Y™À[`€ÿØ×>ùµøžVç‚] Õµ‰såì¬c.à"Õ¼oQ‰ÊÍ^^ÿêŸÊšÎÍ˯þ›kyyùŸ±«‰Nõ/§%h gþ`þ_3ú•¨ÜÜ…õ<Å.´ÙDàÍ8ËpOSFT&ðfggÆî0Q‰JT¢RuK'ñÉÉÉMÞµ¦L­^›kùŸáŠë»•Q‰JT¢R 3„OM"ñf²vŠBº¹&!ø4€sT¢•¨TÁÂ^ nŽI6×ú›Ò9àoÊÒÈ«!*Q‰JT*îäÚ|SvEÀ•¨D%*× ¼ 3µô3el¼Q‰JT¢rÝÀK¶i7åɵ¸S4ž]¦,G”vïb܉«êÄHÊn¬ü“fÉO,EÊëðNzkäZ±¿Ë¤î ˜Óòh{e\#a[aõ^È„8ÿ–°}¯ÿ@Ž‚–A—€(Nz®]Ï£S×¹·~‚ O8/O vA_r¬«ú¢e¼lr£ýyŸ8œãg,ü3ÿZ¦C̃,Ë0 ãÛÿMƽx¼‘_‹Aà$;,q&Ê¢þ3¯|ÏÆ+)GZÚ24 mчç\ª¨*¼d£èš ðafXTW£Gº˜¢„ù#X n„ÔT,9?±=+^Ò÷BßÙz–¸¸Gx>½GÖBøÑÚ2yÖJÉN8'?Î]c$“”5u¶>/U&—hâÞ+¡ÏüN0œy˶‡É9K“âß²)¿aLäh#ÒÊMGä”O˜¢Üõø¶DÂÒ@6©ãÄbbø;ËÉŽö&KŸ綸Tv­Øï%ÑGº1 ’Õ]Òo±ñŽÕ¬-þÄ=kþ%è±A4X“?'Zó@`²¬ð÷®#O[ö?GKÅû_g²øÆ"y‰hG(Êd}÷¿LêN5f~?Dbh‹>Ð?"ZâÚI\c™ÍcÅ„…ÜY— !Ä"™4‰tDÔ$Ž!+‘Iô%D²zÈ%1Ô @Ò#Hõ9)U¤.âÀ’¯J'Tׄ+„ðø.Ùqï4õK‚J²[®Lè:Âú½ U$#%Á P"†™ÅÃwˆ•¨…çøä,.$F͹ö¢] fàG‚"j”ôùìÇò¼åŒ1BRB3ؼïA I´*¾§¿ŸsÑÑJfJ?Å@D2ê'Ì#È’L½<#~¦¼SäxmpùÇ¢¤twQÎø6ãO”*ú“eìü0‰:¼GŸ˜OÚ`#ÙïòŽ p~- fÄ8jðz¾O,a¢si–íßýgÞ·Òa)A…ƈ†ÖãÇç°ýù‰í©ƒþÔM(Ü'Ü"ý!Zma4Ûã‹tIvIdÞãô§”V$uŒ¨dVªµá6HÔ»}S2k?u}ö]ëÔ1BÒä7?¼!ó͸û!/ù÷YF,ý~G"œ1´}ž»FbÌ’Ðt+Ä\]¹åa^ö!&ªÄµM¡¾R‹‚o!‰ 铸4<€cOæe2Kð½)fÝÃ÷¿$AËù`Cv[€Ó_p„0ä›3͸’Yƒ>£AàCy§ùæÊÍw„Ì 3 Y-8€Ã˜,Z¹'?m©ô{úÀ;ùŠ•¼¨ bœÉ!7aú2y‡‰/ à°paœŒ!ãè_Haë‡ð?Ù?Æ–¹èÐŽ–Ï÷ÐŒÈÔ ³ í#ÇÏ•º¡±Õ[ï ³îð­ïï½ëyoTŸ6CÚ$,çÄ™·ƒˆ½KâV膱‡£—4E¯'×ݶ ½Ð~h…gi í¾eý!aR„u$4ÉSé?í³߯Æ/Ȉq³¤ß³m64¿JÚ$ñ™ Øÿ—>‘ ”oðœµGÖ`bò*m³V‘tS_¤ö½-Ò'‚°ì/tÎÜÔÞ_;üŸûûÍÚd1ŽhtôïÑFÍ0R†À»-Ò!ý ÐGJž>ÿ-#QÓðäÃΔ>©—9aÈS$ç› :N¨Çf=‰/šÁáé 鉟¹ÑLÔV3À”b;'Eò<æ4$Yi‹¿‘lÕÔÀ{$Á$É€¿vÈ}ðo?t*X¸jô Uçýr°ñï¦W&À7Ú”ÕxïN ¼¢r› cñ1hû®ÛN q!°hÖï¼O{,/TQ‰¦¸2Òuc/fAP_üH‘“†— (\ò6xÐa"!ˆé–ö‘èd€äò·‹dAóMž‡Éx´iÊœU¢’½rÅn,Pßqè1Q™¥¿®ÏDICõ¨è3%Rß„^¤¬çݘ =Ž7 ¦âǤHÔPê[¦s¡C9aN¼´“L ˜q"«DçnýD’b¡¼—®¾Hrm1f6í‘í7 •७€°Øfö”ƒM“þ¬Ýqw8–H¸Œ% Nݼ/éÌx¡ŠòèíéXÇŠTLж֧^ùC‰4‡9M"1º¦º>4U“ö‡±QàEkÑþD0M—Î0ã°ZàýoX Äx)Ì€¨Ò¸n:aB Ï˜JDé˜XÌ2ôçÀ=/Ha’a,skFéVS˜36Ð"LGӨÑliŸÒƒÚxOejô‡v3 F¨*kÂÌ‚À ƒ$ÃÉ%G¿:4íy‚é—fÒ°LýG2O>ð²†f2‡n¾)Ð>i¦`T`ñ° a¬$ e'‹û×f¾ƒTÎB„F˜?æ`€ñ, CŸuãû=ïÜä%›4s…Hr´ÿ¢0 ïî€0m䱫[“ÀA‘Î`Äüd~Ø´¢h ¼Ìéø?ßUÚ¥o$ØDæ›Ö|²#`í ù¢u!Ù¬ÐÏœýÒļ@"LÚ ÈÒÖ ó†IšÂÔ 0§Ðó³`‘žCà•±+åYx¡-Róð^± OyޱDÊ„¦h°'Q4 @=1à}G$dƉ9á=ÙX5:iÛ¾›˜è#c}c'-s Í2&ôùæ9ÖóH½Ìûd×þëI’i%ÞóÉùÁ®AŽ:×Ü´¯Á•άéÄ ÚëÍ `³á‚¤ÃÿÓ#†+a{´jÌ"é‚ÁdÛÝæ I…ÉPâDB –\Vĸ-j„ÏómŸ<ƒI øfáÌ2uJ,s¢û‹œt@vq}fåi!v ª% ¥ šŒ”ÁÄ(*Ò©©©˜ßØ„"2ïNj¡¿Òg#u0H,ܧÏ, ÆŠz^Wn>" éÁþ¾JÔP~GåÄFê{PБd—mÛΦ í£],$ê•ïa^ìdíëòÝ%QhÌ•ŠqfѰé9kÑFW˜€Õ2ŠdþôÔ}>ž>ÿM[@ðb\h“•R¿€•M/sŸÍ¾ SdŒ1¯ M¢æ`Œ;úlô°©‡iF7œ`bSM_žHØ`Ô ­ñ-¤:•Êaüñ»x•˜ñ\a´Æ‹6ÊÿMÝ0úÉ|±6øiÕnüm:°Q©Eª6Âö^õBäeƒÍÐqhÐöõ­bº* „1Òö;¼dÛmÆšy¤Ê‘©C# KâÊûL 0'3¾ 2|ãԹ߆Ç\Ð&Öæ 6Ž‘ÌUÀT"ŒÒ¼÷˜OÝã=Ö´ÆXd@×B—>ÃÜñ¾Åf"Ï¡á m°Æ™G´¬Ä‰icCh0;'ç/üvW˜2©:m®á†ñ±¤F¿p;ugPM¡º©ý‘†¤àïÊâgËÁ õa…;ª›ï²„ŒY)¢MdâÊd6„-KSÊ$;° ndú ‹²îñ@Í=€‰AÕSìâªc$(ÞC*Sw+í3¿ñR­_?R*ðy— IëYÏ!=þ÷_ˆô‡›,Ÿc@!eëw^t›?, I®h–ö©û_Ìqߺ%î2ó] ’,&!lêH·H|“ydœùuª s%¿™~séÂìa$<ú}ˆk‘¸ÙLÎÔ…´¤î†êà ØÒ7õ†`q'Û à=¾Íxð=¿nÚQ¿U½­;™Ž—|Gÿÿö§q®‡<dó ºáHÜJ³|›qT×8[××ÜÔ¯.t :χëù|½ñ3ÆŽ1T×¼g*ðÜ‹ïÄ»Ê\{î˜ô‡ç´=J#’¸ô퟉ÔYK×j¸IiîŽ'ÀýûaÂSêáÛò^x8Ç2=Únæh_§¡1>¸y¼^_^ÞÃ|™•ý ƒ[M=Ûy=›k•¼¹¦œé;hìïÔžs£G^õ¨jñÃ×’åÕc¾¾{LªAç7$Iqž2AfQ©ŽÚS()©$¿:xgÀýu±úï¥ß碤‡*“U–6&Ê44Qañyˆ¥7/v˜¡„±ðß³&¨«"…Â/]ù¢Ø8'K¶™t,½ñH9V)›\+},J™ÿ‹)’”–v ´3ØBÙp-‰Ž“ݵ¤yå2Yé/á¹i¤¤ï_ ß{/iÛ¯¥Ì¡—8×{‚•ýƒYo'œøeJïL€o¸)³*9^Ãâšþ–Íë57Td©Ì,§*œ½N?Äê\Ä«”5¹ˆ[ÕW~‘®´XE¢µjÛù¿ fíIb9È®N'×4_Ñ·±Gù±¢R²„AT¢RqMûtn^Þ/\VáÄlíµ«›Ä §XY·^Ã`Ã)•¨D%*UMÚec¸i‹6ÿf°êö$Òí¢LÃëæ:;oe_~²EëÿÅnee…}‹JT¢•DÛ4~ø»÷Ƕû–)Í’à×.—T¢Z^­My­E«öÿ±iϲ#éG4Š$á¨D%*—Z7&ñªÀ=³C—Þ¿3ØôÕ$&½&¹ÐÕÊÔà_p“‡kÖùk‚­àgˆ ˆº‘àæ•¨D%*åQpÃCàÃ%J<ê7hü“^4¥}Y'×vW1É—Ø3ëóò þªu»ÎÿFT%N–á4•¨D%*åQ8¤C´ö{þGA­Â¿åŒK\Z>µ‚LM¶#M™[EMõݱ<ì'O˜òª)_qv–¨D%*Q)ëòº)O™rÐùæ6J«š2&Sé² «ú\º˜ë•¼ ¼—WŽ÷ro’{ÑV1ŒÆ+þÞõ^Ÿ\ËñÄälϽ+/Ž|ïˆñÜóCRæ^Ç=€Kº—“á½¼Rî•467z/qlïåfx¯¦Wv)ãaéãUÆ0§ÇðzÇ+?C웚i´2bH5e¿)ܽnNÔæ^w"fÊ>çÃÖÓÝkëNpò*mæLGLæîq¦y‹{w¼çp¼Õ”;ÜŽ !uÆwo¶7ñ+ݽùž„¾Èµ{…ëxŽã8GÜóuÜ7§»w7xΓ][6yªÄh÷ÜvS{·¹þ´t÷ú»w÷¸þg¹£‚Œ×7N\]ÜøÈŠÅéäù½îý¾ž'ß?ìW#o Gº{]{xÁ8èÏf×î©n è÷*wož7†Ëݽn óztÔ—ŽálWÇjÏ®5Å=·É»7Þõc«7^#\;<—›Á®o·šÒÊÝëëÝÓñêîÆûݽΎæÃ>TÉÃ~Þœín®Fzfª®/ª6ttpÄ ED©õ®Ý3Ü¢®íÆëhV,až³õñîb÷^®ã£n¼tÏtÏ­ÍŠ¥ƒ™ì¡Þãú±Ã;÷?Ô½»Ó£¹n\ö¹þëº=èÆ¢³GsÜ8h—nœ:ÚÍrß½ÕÕ=0ÉŽv÷êyc8Î×un¼fzc¸Æ=7Û÷–¹>/õ@w¡{n™ghnŽ{n­{1œæžÛàêäšàžÛâæRêÞæÚ©c8Äõm—[_YŽ~¹qÐuÛÃ×>ç:xc¨ íÜ>Ø¡¬XD²¦ÞMÂJ½¸Áèu°¡ûÿ˜ê»ÿöîÕu“9È[hµÝ‚ì&Sïõq÷Z{„ÜÇ R[ÏìÑÃÝëèq¯îî^' º¸{ݼãzܽîlïîõò¦­kKï¬X(·Vî¹¾Þ½®oý¼sÙMÝ»ý=jœd ¸ñèS]o ›x ¨Ÿ«§…GÈ:†-½{½Ý½¶!÷vínçqa¼t »¹{½1ìì—ŽaGWG¬X€üvÞæ{8ØÍa-oAë*“kîúÖßõ5Ëõ]ïÕ÷˜Í@w¿A†cXèêôÇ«ÀÃVÞ½^î^;o {¹v·÷ÆÐ/îî^÷žOs=Ü3ÙnñvJ¾GsCÜ|å{478a¼Z¸{ý¼ñjêÆe@š0^Ýû ½uÛßÝoêW÷\³$cØÊ[}Öm¾ë×OPËMX·9WWOJììÝËõhNÇ0×£¹ÁIhNǰÀ£9/ŸæÇPin Çøycè×Àœ«ç­Qçt ›gEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWt±SÊ.*.K¸šÌŽJT¢•r*¸¡âšØ)«jDo¬ð«Q–õ¼”óiAíúÿ³°^³(¬ß⟠ë7JT¢•²/c j7øMvvîg{ÞȲ¾ñMn Çý þcëncƒ~·#ÜŒ[ñX0aÕ“Q‰JT¢R.eüŠÇƒ‘ ï úOÚ´í1!¨U§Ñ?,ú$ËÉ©© ‹õúœœ¼?oÙyx0jÑýÁœ]_ ì¿â•ËQ‰JT¢RN%†5sv¿ŒYúPкۘ '7ÿÿ˲' j¢¤»<7¯Ö_u²(˜¹í5éüü}—Mù0*IJ"¡ðÿê9^ÉÛ-}:pÅÑAUgÓžý—+eL¢R±tÉ\ÏÚñfÐsäÊ ¯ ðÿfÙ£ß5Jòž“ûËÎç)÷-"Ñä'-flæîy'˜¼î¹`øÜ;ƒÁÓÃæ &­}Æpé·+Ê®Ì5í½óË¢ÙÌÛûAÜoƒg:ö›ô¾<˜±õR¥ôkÞÞ÷ƒ)^S×àƒasŽco9Lß|Aæ \ÆäÖweLfïúr0ïÖ÷"z¯ëíÝ ûð[|ÿ<+w¦Ú_œ«¾Ð¤M_!èÊ–pªº”;eý A§s‚Âzܼ͂‚À0,ù·vÝ&Aû>Ó‚Ikž©&à{9è7akЪËÈ SÿÙÁ´ÍçãnûÞSÈuÔiÐ"˜¼öÙ ¦‹ËÂÄúšöÕkÜ6Èͯ-ãlž‘|êõ›´FÌ¿§ÌÛÄ÷ùÖ]LjqÜòG£õPEÖZx‹NC¡É/eÅbTëkt^~áÿ<ó`$é–2ùS7¾4ï0H)++[@¡Y»þYÙÙr¿IëÞF>ŽeLU¾,€\ªÊœŽjísßåbÏ%SÏMG˜ÚõšdÁµa+‘àø8l÷Ð9G‚.ƒ½Çnfl{Õ}ûrÜ·âë)½/ó=[^ỈßÑ&òkÕ•öÖo4mÛ/hÊX7íä×® œz«×Žä&‘âã—|\´íôŸê„™7šÌƒ'ô-ÕØ¦®;é8%ŽMdÚ(•)Ž˜74ñ÷Îí¬Ú_ǵèæ-®h’“—‚nÖ à²0[w+‹4uÃ9±l¾Î5õ}Ä‚{d1$Ó6½ ™u›¨MÜÈÏ`¾è1b¹<‡Š&â«ßc—=,ïóf€¡³o7 ¹@I‘Ñ>ã6݆.‘û½F¯“ß°—1Çl\ 5íhÔ²»´· °¡©oEÐò.ÙU¦Ž1K”:†Ì<¾‡4ßÒN=ÚN;z^ô¹2³äÁbªù”õÏý&m—¾ ›s‡ô¶ò]ÚȘ¦Zd]Íwmk`Ú~LL˜øÆH3¦´…çÆ-LÚ4hú~3¯„ ScM]£? uÍÞù%3wI_ù~·aË‚“wV>!}¦Mm̼ÂD‘®1µ 0cÂwxWÍLô•ñí:t±ùw£Œƒ5}XæJ;hu»åT0qõSAŸ±ƒî¦¾ÑKNH]S7¾ ˜²[îñ}ã|K49ÌÜþºa¾ý ‹Ç²Š§z¯VNÊŸt6DÙ³J–v§o¹èUYlHHVzüHÜa07ð;’0`ɘâ!‚šÌ½‡°I ÏpiÙˆ‹ÑKN[õ4‹>O~Wõb``Ï1¿Q_»Þ“£±È7a €RaŽ‘Ø¤ä˜oeçH½úL3 ü †æe0©Ë<Ë.òœ=oûÏ’{Hó"Á›>ö7 Å3ÔÇsµë5 %}þ1ïX(áfMZ÷’oð}¤×–†u´”{m{N,fWöÇóßͯU'è=f½ØzaŒR¨JžÃM|s`§äØ[‘yb,ûMÜ*ÀØŠÙÂŒ㪅vÍÚþ†Ýqò›“¬pL+€0‡™Õ2ŒJãßZu=G®²^@fœ0Q ¥ó{“6}‚Í:…cÍ8¡E4k7@~éÚ´ &­ÁÒ ŒÊŒÙ7«»¹ƒ?†0Q=£‰M­æ$…õ›ÉBiÔ²‡±¯6²p›¶í+¿³0Ùšwëû¶²ÍÂãwìªíûLU–ûØ‹YpØX â`‡44pÚ^lîaß‚ãó,ï(`"­¶ê:J¾‰ŒDˆD^|ÂÓ]r âCfÃ?»~ÓÜŒêÐv7’陇¤Ž}§Ëouµ3„¯SÃ¥l`H…÷Úö/ߦP§‚JÇ~3ƒ¾¶SQK ¼WD ÌsߥÝ6Œ é`CjTÛfžÃ^m] /ËsÔ"qb›ô@ÚÃØ ôµFže$Z¤q1%!ñ:&ʰ>f™±gNrr ÌabÃŒ# ÆfÚ:tÖíxÍÜ×ªÓØ™,j‰6„Í\çœ{MÛô ºY6háóÙȦ\Ò:4cËZþ4+;¼Z^ Ñ\e{¡éT4¹©'œÅذH°5‚¾íUéV$âÚõE½Zt•JÉ€4’€¬?ÀU€±¦Š¹Áxô€};·ÉÅb»ì+^Ik®“”¨ß#êD‘R±Õææ×’wD²2ϰÀÛõšì}ë\µO%/@ƒä7ß|‡¾X@͉zÒÚgeSŽ{Í;I‘~\xoh·- xíæÚ[Aßñ[ ê¶] ›ƒ¦ï ™킱Ã{DÔve‚ízNFÂ!ÀÛºÛhI´”é[.‰¹S„Úxé Cl¼b÷¾"ó«L”¾!iOXuF<-j×mìú4^ÆàÕ{9í™aê¡/!c6s)&37ܸiû‚h–¸aŽF{ûuV,J¼5YâEjBêb‘4lÞEÀ2”Nœä¥Èr ‡¯ÀÿΡ×`¹[¡ª¹HF,D¿ãlù {+àªÀK=VžÛ¤‡ÔÀ´ ß)¨#`§ªnÇ~³hxÖÞI ž %/R6kqt7¦Ûm ³ˆšT`"º‘Äwø^éÀûa¨ML3*þÐÙGÄÛ¸Ugº°›˜Ø·ÑÔ°7˧c,ØQ‘âi'ïªC>ã¦Ò9cJµìôŸ¸]~—>š¿xUeFÔ¥ÚmÐùQÓ÷M=xÑ øs³öBE»úŒÝ sÃ7Ç,9ïM¼­©a[¤â¤cØwR vøüc¡4A º"éò{²±¼õ·u—gœ6FUé °D2P1‹°QÙ{ŽZ-*0¥ç¨UæßUvÈ35`úà›*¥Ê‚7R)’7€_«žޙŷ¡•xýü’€é{Ô¢ã!ð¶ê2"x'®>jízN´ÒäÁO„©¼4àEJ¤¯ÔË»ÔÀ6lÑÕ“Òjn¾C_eì›u³‹0;ó¬u‘´}šmÚŠ¤ÚmèÒ u×ÑÒokG®'Ò&õôŸ´Ý“xï ëf>µn¼@º _ÎsÿÌ!Ú‡¼˜jèJÌ šw_aÚïÍijÈ—ÍµÑæZº~¯j@­ÆçàeÀMM, ^cÖ Ø°¦ÀË{¨Îll†u´Š3[ z²¤R¢7*.j2œÏ“xàmLÙp.^6ð?Õ @Ì#܃±ª}Ñ^ýÀƒ}˜o‰ Åô5}à}+xM»)Øc•±`¯hÔrùfvé6^úÓ{ìzÑ °QOZó´lò·jhjgWï†Zì1+…f‹7Å|©ã²àÊ;sÜÁ‘¾ã7 È2gHÆ0lãÌ÷»_.õê;:&Œ;Œ–±¥0§ŒG\¥- À» Þ2+Ö³¨úo®qݹ“¥'õ¢²™„ «„½O7X<ØËîÄ/`1Öâ à$çS÷ØgÍÂæ¸¶ì´›ï ]³YÕ¼Ã`ó|³póEL nÇŸï°Û›·Â… ÀQfT[U‡;ôá€÷ŠlÞa'¶í³}ÁäÁo±Òp+üxq S&âK¼jÛ¦@ˆû¸«aFQ[4àŽ¤¨’w›xKp'CE·öäla"´-6îâÆÆX(S¦ã6ô¬¦±lpª/-L…ú1ù°‰ÛX½FmC怔NE*¯×,œOÆÍ&Áoõ›´ ßÁVL Ó&¤^ÞZulß»^ä$Þ7d.­TÞ)^<´Ÿ£#à-Yë”Ô¾¿sîd5âÅß°Û™J—z‘\PG›·(ÒWAm õ[ˆ„‡o猭¯†ª­¼€ ’'€Ê¢Eme§‰1üöö7Dªf—¾Ð|ÓE†v7Ó¿6ø„6t j}|cÞHgH²&õ47R¦þÅž‰4.Ú3Ÿpž Àƒ6!ÕB½F¯‘:\©¸7lÎùÒ½º·Íq ƒþa"¡|›:8Jg¶ÍÎæJ;?a†¥dôæ>%fãÖ=e qÆÛ.Ì ‹;â\Ë” 26þØÂ ùÀ‹¹0’kóE"·ßeÑRNÝ+ï#U3&âyâ@-Ið¤M<Ã$±-ë~Ò­Î9` Ðv4OÆ!m3ýd/ú‰))ÞRPÔ©1(äÈ0œ$:2œ~@ Iq6¦àÆê@žû÷€WUcžc36»˜/'|ûCóÌ«¢Þ#…!åZõ?Þþ‰û;òÅLDûíq[Þ—vá‚f¤5êæ X^}x&˜{Ì=¿ËA©ã ӯ󲩤u`#•zýX xQlMî#íjŸ¸Gü o!÷u¦šìœœ ´}˜ߢÝ2«ÎH$n„7Vþ)0<9dS-'/4m_±ÓtŒ ídüÃy3ã“ìÔ@©cfç6|fÀba.ŸáÆÆûMg·Þ&:Nó™…z¦™ñ,Ïø5æÈ°5Õ|Ù”5%^Ã0$g^ȉ£ /]õ‰‹à•dÌ,ð‰÷ý5Mªc¦©"r•|d8ud§dLjSS¾t5i)êMx–¿9Ýņ"’#’fËÎ#BW²fF[¸Ò¡³ ãœX·úX#•"ÍŠÇC›Þñ~ÖÉŽû(%ÒY‰Ç›/—x;åx§ÓËÑqý ’3l™ÉW“¢“qünEn~í¿î:t‰“¢óãeé µ¿MNGYûçå›BBAª`1Å ºœCÝFÒ,+Ú#wØ;5‡Pìáˆvk‚y 3›¡ÂBnqqÃkÔ…‡Ã:ÃUþ‚@ìÔ£R%*•ôÊeQ!1I ŽÎwn†bM&¯9“ÉS♚-'Î<©ÿ†Ë³•:&®~:ÜÜ‹Jõ.H¹˜Ž°çæüƒMÛM©US³P ùN4岑Ò~ ³ÄFD=yýY±9F%ý‚»;êxL^ÿüÍÓwÓ×Xß]áÿå0|Ó¯ã¦çC/gÅ^ŽK‡XÚtË&è?¹Ô?ÓkrêÿjHF 6ݲsr¯5îÿOµëÿsT¢•¨”Gc Öü}NN^‘ÁžWMY“u“$»L¼íIïÞߔɦÌJT¢•r*$ÚàN¥fEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWtEWù_ÿ?‚D#ðzß@%tEXtdate:create2014-07-14T18:12:21+02:00s]9ä%tEXtdate:modify2014-07-14T18:11:58+02:00vjv‘tEXtSoftwarewww.inkscape.org›î<IEND®B`‚stxxl-1.4.1/doc/images/layer_diagram.pdf000644 001411 000144 00000070674 12405117057 020022 0ustar00tbusers000000 000000 %PDF-1.4 %Çì¢ 5 0 obj <> stream xœíXÉŽGõ̱¾"U€*û27{`ØäæÁ€a4»ÔÍQ‘l‘”eÍ×ϋܪÜí >·$ª;³byYï˜à’ ú[~îݧßzvwéÊÎwÝ»NæßËÝ}¾ÁªÀ”ã6*Í6oºQpA´óL²ÿV€Q<:ǤõÜ)Å6‡îû~óÝ {ú|=ü°ùOR§ —Q¨•:)!®Õ%À` Ч¡ÏYÒ×ß ›ÿv_lº×àNFiw!«ñKÐ6ê@Áx¥¹VLɹQìЙà ÷º!sg¢ ˆ«!VHÇÉZò¸’Öá箫ˆ4Îóè¡£!:³¬ê„ ÇárµZå]u¬sç4¬›åëaÝ,*›\ŒÂ‚TÇæ)®W5´Glìº7Œ\EÇ>t†}SràÛ/ŸiûcÚî»›N«è¹5LKI18ŠRrïB¹È­oHWK¡aa‘…±ÜéGJ»à‚ˆÀŠ«èTÊ)®‰³lµÊ»æYEf ˆˆv¯ ZFÍUSÙDWX.@ukn@ñ»ê«q=æ‚뙟?â‡2ÈËuŠj$U*ÑpáÅM7„†há=å ž3܆•¬S”ð­ J{4B—tTD„ÌfÖ©´•¬Z­ò®yV”™RÜêi%St.r¶º”Yõl^!Ù÷ª³Æö˜Ê¢gŽþŒ#Ê$L •‚-5ú9èq ®Î@íà–_õò,­¼­êlĘàV²p\¯«P£Øˆ•y…HÅ1"4•Ú$[Íf‘['¯ÀÒÉ+Ò¨(*9]ÑW[ÑW\o:sd¿f‚òë™›ßã†òÊ[hGK4x%vÐæ\ˆŸ[,5D>g 9YT.¨È)ˆ›6½‘®p“F SÐ-VyW}ª¸që⪠rÑXÅj•[‘âÖÜâwÕX¢ú5 )kžyù ^r'ú Ó¸TVs¿++²Ô[EÚˆ(ƒãj- Õë±Ò+Ïežç â å[ÁYo¹_¦Ê"¶r+òRm¨…SÕU¹\Š­:5¯ìvSKX˜Xµ¢grž’C™õ=ÃõO'ÛÏpW¹í"w&àèûÐ…ˆa\P9kMŒ0H×V|C 8%5ù±vïi΄|j–[I¨'»±Ð Í ¹) $4¼c—U%Cº=áæ£2Öû5ÐgŒ¸“§4 GË­[ ˜ð`£ã ºa¯Ÿº©¼7ˆJ0²àK×rªNªmQ›ŽW‹þô¦/%š]Ëó;ÉUt’Øcnôý'ÿ +8›²ÇFT)ìÍ-.üótyÁ¾úô%;Ê÷Ó»a”†˜‘ýûé8s±¿bÕí0*|¥½ï÷—·¬, Ácé{R³;€Y`Ñ÷ó4(ü6úëþtd÷Ûãí<‡S–0pI¯À£ h‘\¹(³W?ͧÝ[öp´âB 8öfôœ=©í¯»{¨Â"Æ úì§÷å}ÅB Úc ‹4öŸüó H”@M²Ðe*Rz˜~ºeÙü‡ó~ €]ÐýuP,¥T´²¾ÁÐgÒk‰é„)»ò3eKì§²J“Ë¡?ŸJMƒ#ŠŠˆDž\©†m¿»è2Ý–V L}‚’YÅ%š™fD26f[çý©$±XÿñG*Zt©]*ZRp=ïI:Ð= Ú£gTæ)¹À·‘8Ǽ@&Q‰ŽFÁé§íîJ°+pußßMÓFzô:H”—²ÒWû‡iÞ‡ÚPh/ÉXGˆÈöã]ÞA¢X{Ioqž`´ÉÏÿò 5ÓŸFªœËn{<â±'Õ‰s’Fœ3˜§byþU)«Ú¸9 ©¼¤¨ŠV ûT½­ðîÚ7O’ sŸŠlÄQhMHAöŸÍw`ÿzxš\1àÄÄP6*pIÁäÔËäÍyU Tõ”cÆ)í¨BÙÍÇâ«SýåJKÞ€9ípƒÐ”0N--è¡|J”UÝ.Õ>•ù¨Sœ'2ÛIzcûºû?£í¦³endstream endobj 6 0 obj 1751 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj << /Registry(Adobe) /Ordering(Identity) /Supplement 0 >> endobj 18 0 obj <> endobj 19 0 obj <> endobj 12 0 obj <> endobj 24 0 obj <>stream xœ]O1à Üy…@‚:F,é’¡U•öĘˆ!€úûIZ©–|’Ͼә÷Ãup6DOJ`¬Ó‘V¿E$˜h¶Žµ´ÅtLqQñþ¦Âëò™}¾«…ø(.•iw zMkPHQ¹™X×ä’É%9ý·»h2¿ëVh¦Œ•òÀL5ÕæÇïL¸ÅH.ÕjÆ’Í:ú¾|(*ÈÍ>ÍY` endstream endobj 11 0 obj <> endobj 14 0 obj <> endobj 16 0 obj <> endobj 25 0 obj <>stream xœ]‘1rƒ0E{B7@öŒFÓ¸H&“äB¬<¹}þ.vŠgü¿BKs:¿žó²é森ñ‹6–צã½VÊ›,[–ÉK\2ýý²NiHýÃ\} endstream endobj 8 0 obj <> endobj 15 0 obj <> endobj 20 0 obj <>stream xœz\×öÿ.030–eggÑ]gÁ,Š€]DQEAÀ]+öÅšDMÔ(Æ‚‰ 6ìm±°Øcì-ÑMÎðîúò¿ƒÉ{É{ï÷þïÌ;3gî=å{¾çž«V¹8©Ôju½„ôA1“òÒÍi–ôq9yþñé£&d§æ)×ZÈ&µÜÀIœ‹ð˜ÚßjçPEîÎEî.'yºÆg^ Œ=gjUΊ¬µê}êŠ.9¹“ò²Feø4ŸØÂϯ•ÏÈ|Õ6,,ÌgĤ?.ùtMÏÏ5ÎÇ—œXÒ³srÇ’!tôéBîÎÎÎé3*{Rnf¾OjZZzšòØÀÔìô1>ݲ²³rss,ئM[rìè30+½`\êØôütŸ‘™©y©# Òóò}¬éyéÿ›:Î'!3ü¥ŽËìøW9!­”c¨OFNžÑ¢Ÿô‰éãò³rÆå+GøtO9&Çš?&Ë'u\šO÷€^>½s¬äË,Ÿæ9ã|F¤g¦fgøädÔ‰˜¯ aT^΄Üüä­Yd@9yc|Èg^zvzj>Ö„qiéy>dT1ú'øtËWàÓ3k$ymº¿¿O~zºOfAAn‡Ö­ &Œ ÈÉÕ:ƒÜ“ß:ûãMù­•çü»õéàß3®KtïþÑ êæ‘–^š•ðØú÷µXQ®š§Ú¨z©z«µQÝTí¯î©î«ÎVWWª/©¯¨«Õœh§>NŸ8­rZë´É郳¿sGç~΋÷8_r~îâç²Íå¶Ë}ʉjAåQ»©ÛÔ#ZE3´m¦{ÑãésôMú>3™Ì|άb2g˜*¦šyÀ,ŒÂ]!Æ™8›q*Q8ÆÂhè fH#ð87Æp$ŽÂ tÎÐCDBcrÅ,¢ôàÙ#¦§™ÒF¬<2[êA‡­:œþØtïĶǫ%TÛ6ñÐ7žHPdñØ‚„õDVïÁ_Œ4`;ùaöÆêWà®àü¹b§–˜èÌXÇ Ú±†&,ªUíZ27Äîhe¡˜¸9¶/¬NÏÏn»uZB|Öi<»îèO¡f,Œ74[\°¶5‘‡µ­~/@¯^÷þX#"]S‘¹‚gú¢ úó¿î{_òæëð‚w,ˆ•ÛÒØ-ëšb_ŽÃA$úêFæ"@w _%¶=*}Gƒnö¹ƒM8—h¯#Ñr6tÅÀBôÛ:Aï  åÛ'BÞïÏ"]+Œa¸fõƒ˜¦|¶ñrªÙ‘Ê`¿ž]0]{]?ñ÷¯RÞ¶…¨6VóŽÖ±`§#s[÷jlÂvzN,¨žø¨&£Ç.éý& »]Å‚ w#—½p[Ü|°ô€XЃ74\,!Ù.Ïç! ø>ž*â¬öÚ w4DÞŠ=qýèQx¤‹ð#êG ­ºq¼È†¿ƒMp݆¯Ã×ðÍÑüƒYÛH.0 ßé;k=vª‹•£sm6Ñ $’··Ã‰ä'×}B q‰DòÓȧ˜aÀ]ˆÏ5$Ç.ÄsOéBŽê>‘+]D$O3àPH¢~YUyö¹˜˜K8^Ä~Žr»\PMƒþLn¨߉8|‘™x%…¬—n³¯Àe´¸z/àk˜O'O™?Ù”kݰùÄÎòC›%t‹mѹë¬2GU^g&F5Aèúîazó ŠM¹xl¸…ý›¶'^7žØ´#ÆøEÚ>‘úòËU˾6m-¶Žg“7ý‹ s¤s©A+K³Žšªö;øô "Û³,™’iðøçˆ¿75iW )l@vàé"}äó½+w›vï>øsISë|(Ý{À¸ö&?*Åo5ïžAý;¶Ü®‡É¸Þ¦bõ%lP¬k Ö !ú­OÂ!¸—§Àé ÃK²+ô°Wíìâ3ð°Ï{Q;wZaÂcq8îDÜ“9Ôôû®RÖžñ¼¨Qg¯O¼n‚lêá0F“`=ïpwQ±F×<1ÕàQgÏLÍ9(BÞÈc‰XÂ]ú%¯Ü/ÊîrU·-ªuÖÜo~^ܵ½¤¢Ú؃N¶ŽÎ+¯um~@Ñö¿À„Xb#àÒvev0†ôêÕ>´Ïñ×3ÄN4®ÿÙÓ ¨oýËç #qþ²ÓVÍ;wnáw»Ó«nßþAŒYÀ÷2¤oß!gÏ>|îÌáÁ}DTÛWvæAêNØé’¸/ºójl4á¨VH¢a1pdI‘‹ù–é!1 br߈‰ØÛÔD~û3 .W§e”¾T€Œè”6Éš7Ñ5mHì„Øs“‹g'®J-ë&” sýÞ‚ärb ‡ÿw¸Qd¯±2DX½‡…ã ¯Ÿ~ùæJ/Ü@ćHÐ Šä……B{š<»Ã^ª= é…{Ø¡ƒø‹øIFÃã=4Áägú[÷Z+Aãî¼qt­7MãhrÚœFÐ0ꃕzECˆü3„:~¦^Ò6òÄ}ÝÁ‡¢ÚôB¡=tÒè‚ÑÖ¯m¹¯é_Ñš Äñª„JH°A·Ê„*ݾr×+û·Ÿ¡ˆ‡‘Xÿ n“eî+rº—][1ÜBû€˜~&â\îÄ-qÁhO}zi›ýœÄEµ¾öŒáöG±š¡lÕ…õ{ŽJe%Å›·žrÕärèÌ"¹´° Ø ê³Îûv+yDˆ@†ºcR‹xñuŸŽ‡|Lä_O‚hÁD¼Ž Zû_ìߟ•ü¾ŠÆ†·Ï_o’ÌtèÈ^#«|ï5G’§Ò ×,üòsc×-¼ƒ(Õió-Û[¨tmøµä˜žÊcÏÀVØ{¼OÐ=~NR’û“¬5Ž€ÏÑÝ‚£ƒ/˨¶ ä$ÐðБ@! DZ[ hgiIÅu#°…Ç^Ám0º¶ÏÐjŸ>áþÏÍ }bkÕN­Ö¬Y¸xµˆ,r¹Ö±8 —€o¤´& ¸ó£@Ô–`Æ ˜öÕ¬ú“ŒuiÈõž_ˆe«JO5Þëy;á&™¸q‘h£‹6Õ<8|:'¹çÐñƒÄ.Q×i$?Q¬—0ž%¦+¯©eª¹°.y¸7Á9>GðûºÍ!àf ÷ÉϽ0u ‹&œ‡;ç‡ÇC8I²SÀøîÔ¯ç%.û1 ¦àÄ´F{â$z’`¨O>cI"¬G’yœ¨¹=fó¤cBÚȽSwÛÄ(ºáµ˜÷?^¹úFD—rO'—‰c×gñMì:=hÈz:H„ÔkÛˆá9ÉAmŸf`#±NPÂß}%î„Xý(~9úfDŸU}Mw`n¶#êb¢x:­æ1•¨²ð¤‰ä t(á¡ÐÇeø•²/¿âŒ4ï_B½½{fOÙ!‡û󨽡?®72ë³5é"쀗ԡáë“zëBv -ïb¡K¿œþ$W·¬´¯LU:ùÈÍønbmÈH`¹9÷í}[c§€ŠÇ¿Þc‘:ÑYËa3dš 7¨ª¡¥¤ù/ÄK\EÊe¯œ?xy³>g^M¶5þO$Œ¹4 ²Œ8xQ„–,„ð--t~z¾ôv¥„ G±èˆ‰¹)ÙÄ‹9†™s"~NgŒ›‘2XøsN¼rîà¹2éÜ·‡NŸw §#³Ã‡š°K« š+Éa6%pÞ¯ûîÚ;Ó/wc±z£„`ÐürÙ-µl©ÎµËà9ñKp7 &<ˆÌ3',‘aUeÚ$×°˜~‹ÆNÛÛ¼ýt®Ðr>‰©üMÐD2 ßùÒ|¬o×»Œqí Sotfn> N‹@媑—íðT¼¦šIôÁB؉2ˆx·W…ä{|@ö„à hüÒÑ™’;”ʼ"¨ºW“l‹Ëê.BvàOê9-›† rþÇ'?^$Þâ墦pœ€r‹l°ÇKlj9¢ftéè}y/dòOÒO—‰§¶o<\m.ꎱ™dâË©w.ƒ¯9÷‰ÍÂ~¢/ìyDƒ÷›ÙX”Rá)4Kb[Xò˜†»Gµ%žñºð²«ÙáÊ„Më—±ýÄBÑLû-<×÷­éÝ™s %)oØ!ª_Ö¸ü1&ëä¥_—ºo¦¸â•ÙyŸYLýÓ²‚“¤}ÉÔ¥Ýk60­[>oò~©rá‘FCÞÉ@‰Bõ±°ŽÁ[í©Þ$Ó§ðÕL§yɹãú¸¢*EP¼‹E§Šª Öa65Pv¨­q–#éâç¤{‡Åmý{­ ÀÛOˆ½B( Ó@ ´•"ñqãâeˆ£‡ö-lfŠ"Hl#¹+ (@Ûçø{J.&Æ ˜¿@wœCˆË ‹bðcG?Jîå(¯fà±Ürôcþ§"%"›Çm§ÔA@ˆ‰¸¾´Ðn²TË\ä?04´YE蹂M &…ú®!‰¬œGwH!àØ8S¼I×l»yä;¸¼ ƪe’c6‚'vƒBH½ÈO;1b$ßÈÚµ±©a¨ó$Í6!œ¼W==,«M˜iœ¦JrðEÞLƒúË·ÀÙêï:cõ*‚\÷ µVÌ0¸øƒ^_ëFaFá×¾4lýàK]¤a[­/õÁ—A|‰ø_åGÿ«$þw‰ø‚þPL…и…›Ã(…Äãb·S÷i.9ÐLÑd8«É7æäY²¦L[üÙT1…™»võ§«Mg÷ï:V†˜ˆ)c³Ä ÇN1›z1gV¬Ø$m^±±tÝN×u;W^}`ÔØܰkwÌ-’ž*¿Ú~,4—ßñïÙ”c-Î`nÔºn+VÓÜð¸°ë‡ç¶qa'%Šx% bå,~Ëûp8Ÿß¸ë¶Q#oPž}Y÷l«ºgɓɓÝÃúôï¡<¹¸îIÅÀL°Rï^o¯$~VX. YÅ8fƒ36õ ;<'Žx^^É“ÜôUìšU* B«€²Ø_ÍBÏÓ¬§X\§Zfä®V“ìºFD$æÈù ¯ÐÁèÛl+%«-öåvghyF–=rÄÀdbëj9·c‘1æx|Â^„èÀãÕKRT=÷Ç:dçÎ% ˆÔržÐÈ»ñ•€>ôN¦I1Љ¹ä§„B¶aåqÈt♤TQJÕ¤  ™.ifÖ°ä­Ä%P\fåy¿ð¸ Ôý$#QÕDù/1š|©$DÄW õ|Åà XäzÅ84ø*ÕŸDëx4î_ÐVÂPäÚÞBwVQ¡m­Iù܃0»& áèGÊ=¨¦ sêMpÒÑ›:Cù7å T%›¯ ÿRD-e%tâ6)0 -i¼ï§`ݸ[2¶ÍÅ3¡”R2õX@¨BUcƒWUˆŒÁŒ\È8kËoó•©pµ;ƒ-¸† S:½Íô©äq‚ àæþB!’ƃÃq‚<œŠ¤Ñ~V!iJ¢™aExD"žT²zÂ/úöÇþщab}üä—¯_Ÿ,(j†±×JYtl¥½×ñµ'¸Shj1²BX¸eEº}5²ÙŽlðµ áÅ7ø²Ph ”™~„(£™¼Í«Ú~Ä´òrš±–Ú 2Àüj°ØóìÜ :a5HwÓ«ì(‰­>}ÞŠ )=FƒOq+´u? ÝlPiE_¿!åÀ% $XÕè„@>P!P,ú~Œ­©X’ÒE+„’[„Ó—7>ì°T2~Yê0#¶2«ú‚)Ðñž@‡|Y”ÎîQÞvá%qÉ )D_–ÁØÂ G]. >ÜŠŽüz~:ª£êkÇ^]—“8×îR‡á"qR,̓ÎQ@käÜ·,2aμ§¿?>gêViÿ„ ÉqF¤»`oåW¯A¿.ÎUb”qÈœÝɼ ýky©áíŽ3ƒ‚cXdÆ;dþ„×ýþae‘¥ÖzOpV@M„€Š-r~•úB5‚Î<4ô-ÅN"¢_Ÿ\QrH:¶}Â>¤.C¥$-0ŒÑ„òEt…¯iÍ(UpƒæÍ°ª¦œ˜™¿_º˜¹³W¤Qc–GÔŽÞ‹Xg<)Oõ¶C/»smR„¡!B);½8R¤èˆ~Ýì\ƒ2'‰3ÏŽ4!LèƒÑ"“‰‚»EnBL§<íè­bôÃ6ϼ"O%4¼¨JnC|©k‘MnKîf«ÌA/~4&éÒ‡ nÚÉñ;Rä ¼Ž”³'h ÉúíMÖÙ<ùGÁQ ÃH^ýˆu| 9‚Ð:¸»N}A9:Ë]$õówôÇÎŒ¦ÿ«±ƒs"ÓL#â”íí ¼ŸgÑp– ‘8WmP~ID¥UàV#Èdð\¬«‚ûUð]•Ò"ç3š&‚¦¦°JÁ=$OÔ1H]^sJ@Žk yH¡Mxäs9VEz£ùؽMë"èÄ )–háÄ7ßYˆÖ•qFxÑM¾•<&¿%s|G²™¸¦Ib“ ÈP:“ ÌûÜÂõQsXgbòêÑ,Z¶ŸU¢îAÁÞ¡f$Á/ä j¤j8.'w|^~Á„8‹5uâˆI#'§¥÷Éè;*3kLöØVþÃZû¨T}T}UýTñªþªÕÕ@UKU¢j*J5XÕE5DÕU•¤ŠVuSµQŨڪbUqªîªžª^ªÞ*•F©ÒªÔªFJOÃEÅ«$r{±ê[ÕiÕ5Õ3Õ•Cí¬ÖªEu‚z¸zŒ:O=Sý‰z‰z½zú˜ú’ú‘ú­œ\x§úN-œ"º`»æ† ˜â± ͸›‰¬æÄiAÑQ#VsGX †°We]â¨')p#¿ƒåž¾ ‰ösöãMVAÁžH{·+œ,ÿ†x’†ý™«);ö¨TF!„°rÊZÂSûΫ‡%¼›Y* ¢_+ì=^(kÏÉQä\@û¼æ$ >^ ®ׯ"pW,¬g-*nP(»Þ²hÁÍlWnãAƒWßÔÄ“œSM‘ð1¸‰a,÷é ‹ a4D¨A² I>Îî`²£\IòàÁª{±štö’Àð¸‰-ˆ(㬜mÀýaûlA³A€Ý{Ø ·-æà­´'&B·µd¡ Ǻ€VØ»·|CÒ¼Bb4àù,€äqMwϪå rùa(¡ä+Àë2©B8A]Àjî×ñ‰Y,çzVÒüÂ6f5u9ëܼ~ÏNBa.*0†0),­Y(@ë°+x˜ˆ‡j Ç&±Ÿ±jh(8Ë¿þ˜Ç‚[ø<¶;Ç GÓöèÈô¹+%,ׯN&ÉÇÍcXÈ$ð&Ï5LVn*~'hàI1Ь¾×é˜ÀÝÝD^|3ŬjXvŽhyýFÖ{VËŸb/þ´ãÓ~Œ‡úXjÁª–’ަñž@ÙLÜÔX×Éåë r]ÅÚÑI®v¢^þ‘YçînG–.^ºpÑÂ… —;èî~`)9]¾dá’… ¹{¨yÅ…yãU媛ª{jZͪÍêMê‡êßœú9 tâ4Ãi·Ó)'pnælvþܹƅs‰vê²Ã¥œâ©ñÔêu‘zAw Ëh™‰dæ1EÌfæ9ó–ùŵ‰k×1®ÓÝ|Ü¢ÜbÜzºMw›í¶Ïí’Û ·×nïÜ>°N¬–Ùl;„ÀÎaÅìoˆA( E ­¨ }çâ¾Ü½Ì}¯{¹ûk7zÍ<ü<†{Ìð˜ï±Åã„Çwšnšåš#šÍsO'ÏhÏžEžç<¯z~¯­§m¨m¦õÓÓfjgkjË´ßkŸhß{¹{I^M½Zxz õã5Ék®W‰×¯Ã^Õ^tn:I×D×J×F—¨›ª›©[ªûB·E·K·OW£»­sp4‡8/.kÏÅq \7Œ›Æ-às›¸Jî WÅUs¯¹¿ëY½§^¯¯§—ôMõAúúîúý }®¾P?G?_¿Xÿ…~~½þ€þ²þŠþžþ‘þ…þ'=è<ûó>|Ë'ðƒùT>Ïã§ð³øOø…üZ~3¿‹ßÇâOñø*þ{þ ÿÞàb` ZƒÞ š í ¡†HC´¡»¡a !Åm˜døÄ°À°Ä°Ê°É°ÇpÂpÙpÍpÓpÏðÒÞÎÞZoÞ»ž·äÝØ»¹·¿w˜w´w?ïDïdïTï,ïñÞVïiÞ³¼yé½Þ»Ô{¯÷!ïc¸1è¨87Å:ê)ýÈþÀLhûèƒçc±Nš9ÑÄisún³š23'Ž-8ŸÒ”Ï–Qœvý¦Ï—¬5-_=oîWäË)¥_Ï]e*Ý÷ͦÕJ¥ùß(ûY×í¯ß•‰ÿÓÚ ¾Vk¥Bi|•dÕ[4¨¯‡è_ñ"ŠT-Wz ˆû¾1!X`À+årBôð ‚M·éï¡_Ñ÷q7 »Â|¦_C&™p¡nÒuSþÓ|ëfûç¹þy¦ÛpêVÃËÌ-™š„زÖ&„ݱ‡á RÄh!èǪ=÷÷K nÒ-×k=pÛëþbë÷ü ¢éåoÏ’vùêô¯F3tç±¾mK¢3÷¶/¯GOˆÎX‚|Hühíïöpý³=´íáú{hÿb3~Æø¦$…„¤þI|Àâ÷bÖzÞ«‚"‚¶ü‘‚#7‹ù«ó—÷^ÓÕ”¥S¿r¾röÚµÆÃ{v;U>ªß'â úÕ².¶Æ¦æÑqÁ#W§—äK,_Ͼ2ÑõÅø­–Æþ)i½{¦ìºö ±Q|SY[}V=˜?th|¡'O8pRjÈÎËËÎÎÛ¸uûÆÛ¶m?VÔŒ ¾˜¸ÆÕòõ´;Œö•öšS‰Å›4¸ß!Ù ·ÇÌ µCJ³Å-9ßÌx0Þñmèvæ`;HCcâ³—i ߃ž—7gÆÔ™®3'M˜;Á4a²哤©Ë笘·Ñõï¾²åk7I%ëV®ÿ¢ÂÉï!˜oOGÄnž- µòËW ¹kKë²Oéœ9 kt{*U0õIñP(àŽrxP…WBXlË!VÒ¨¶+n¥C”•W ¾S±3dŠÃsrû˜z'lÞŸ#”Í8j3~lƒ¶ZÕç1¾Üžu×tϾãT¹´³bÕÉ—FØÊœÚz¬òµÕ6]*:;3µ Kž=$/ÞÔ;qcyž4¹dVùI#6ãÃL‡¯â·Œã÷^{ÏT·2/…ØáïÌé-'*2~t˜¿;LäŸfÆG‡‰ü‹ÃÌø‹ÃÀâ¶ìƒ=6‹¡;æ-šò¹kÖš)ßl7–—••Þž“(ÖÐâζ Œë˜öUÖæ<‘:N.+àѽš5ìw$ñã _3ó`îañáV•MÿïW/o:tDÚ½sÓ®Uþ·[Ây“p$e'cÿthUØ·âÙ’’CÕFp»ƒ)ì†Õq›RÎŽ;¤ ‹4bÃÛÐL?üFÑ<Ÿ÷‹ ð‹µ½|n»LþâÄ4xËw”5ðص+G+®];:8ZÔðS«B÷‰Ç¾ÝpàœœÂob5vŽˆÀb5}oßáë—&u‹’þ霢…³ÄÉ ©8¦bÁšëæÞdŸ|4&ÜèÝ¥mPøqЊQ4ö{F0¼}õ‰ìN4îåˆÅf9–ê@C¦cS ΠЄþX ”þ§ôßÉX!c³‹ !ÕñCâ‰h<ÆA$ÊT _8fP¸ÈE Š>[ùпÞz—¤¨ÛœÔÞ„©€{à4S’;u‡ÒïW_ jÓ¯ßEcÕ:É1g$=[ùaBßü_ÐЩÝÓÖX'*•ÿþnî¥þGÅ“öûâ#©’¡É_PÌÌa=ê´>ál´4(öøÈc6jìÉ=3N(<%b"$ }x\ŒßP™ÓöVAÿìGpßU6{Êvñ-îËãzAX—ž»ìë‘"¬‡7ÔŽÔUCû,â¡QcpÁÞÙs§Ž! –++=ðFªk£É)]Œ¸¨±øB“÷à ~¢ùS;›;ø6¿.¿Üº÷ÓÏöޤìÔðÐÄ(,KŸ™7Tü†S°Ù†ÐqÕ© ÔÉG6Ÿ6½¹†UXÕ¹}ó€¨ê÷…î€õ?¶£ $P M%"§&Ìûâ&°3öÃÞaðÞöͲU¥"1ßrê{zÀFê껯VÍóø†Ã|›˜ï€¸Øïüô޼ÃEÔPm+e‰ÍQË8åZª™²â*ÒrÏ"…äÛÊbvʽ¶ÿ\Ì&XT·Ž­ª[Ç6ÿË:¶Üæ¯%2"éÖ‹Ðê TÏ¢Úo …‹Ž^¸¾ÜKÉþFr*Óp"I¦|@ƒFOG 9Eµ— 2÷nŽ¡˜—MToóŽ’‡’Ä éäö;4±ëo€ðoŠ#×6RZ³#¦f[&ˆ—wØëoêή$R& f|ËÁÁ­=,¼‘9ü†Äâ6 á¾8‡Ï™Рw‚LPýr ú¼8²”ŠëÙ1³ ÷ľРz]6‡–ˆ¸}«ÚaS˜uÚÊÕ«Š®;'=‡md€“SÇóÓV'šH6vÇͱЩÞw•­Ú¹S±zã”C&Ro¨ÁJxù&>=¯ó¸D±WffîPSÒ˜•àù4lÉ"ivmßzÄnÄ4ÞÌÿÇ=?¢²Èa%õø’ñžõº2pµxrûÞÊFÐáz7H|:„”Ä2‹ÊÁáO~H ŒJHì b],hiÄû°èoþ­©È8>'ÌæMa1ðb®ºà à…®Õ-ž`ÀXuêõOUýq ïaÐgìIˆüXÄpµ[þ–PâX•ÇâÞ «ë÷¢­Å–ߪB”½%O|»ã'ÓQr »ÌÞXè®Ý%õ3’Û?bmb¡ó‡ ðô+²A© VØÔ ±Ë95ε…u{KúÈRÿio ¾åû ¿ù¯L Rm8V¼þë69q56vtzÒ_û³WϺ´Mzsèê«_ÿ¿ýsJw÷Nˆ"còü)u2 6]P Vóq­~• ®¡3rÙ«œÛ%Ý®8òèWã->äv$Ô®1pL¦.8Ücáƒ+mŸ„7„á=…ú¨O£[¿wÞ;ùór™?X•Õùäj­ÒÁ{Ûø«\®<ü† ܧ7ÎevKè>‚Pᯊ¤Xºè‰‘(0~wý1[#R®ß)ÒàÓ?7?®¿BÃspKIÃÔµrðøkæ^ä˜AÔSDÝ¥‰VŠ(Rf,3;–1˜Îú‰Ø•íÒs6âZ$$ÚÞPÚ 3äêü¸œ$³!ºÿÐŽ DÜ É(îžNŠ–Pñ+‚þ.·“M™LL3ÞòÇv²ÿ«Rš8eʤ©ÖúS”ö”1æD¼íc× ’§ÿ³~‚Âè87>y°˜–1>}êP×ÿШŒ\ž²9I ªx™ðÎôG³Rú·f¥ÜšõûN©–ÿØ)eè=dHß>CŽœ=WQñûF)ee¬‹½ë5®¢ÖIÉx{–ÀeÏaQYNIffNNffINYYII™ˆš3‹¡DYk†Ý°—ß+òbwBøIqëyÈ` _“û¤öókGð“’mÐÁÐüžÞƒd1j%Czb7Ÿè`!ްK§I7ínƒë‰šŽ‚Ò¤Ùü-EΉ:9ýˆœÎŠ˜þubŽýCÌhEÌ"¦B‘RR'¥·"ÅL§\œ&úÏŸÚº“©Í|pÿpé£5¤zrðŒvAFì»ûƒîüÞÝ'7Šñö¦_&q¿—¤1_I³š žõØ?6©î›±gÒ·J>k‘­Vµ¼®ñSŽáä„™0’@ÏÄ#qrå”DEà:ª|ŒÒê# R¾ÓEiäd)gèØ†ãkO ¾ñÐ[qB[õëÛh¾xv)]µûÛãGö¦ M›Q+âö¸%5ÎTâ± Ôê©ov2jNÛ5`ºå“"Q_ú0=7j(ÅÅ^¤!|ÇIs©£U@vØÍD¶g<5A{¥† egg€²ã•kù¥ iá±r$ýÏ&e\ÌúÝIÒøµ“ÖÝíFcÁt4´4ýÑÒ”4G…v4\é[ÎrÚ+}ÔZkG×~hBÕ6±²¿2ðüåøÑP±2‹MÕ`b¢l<ÞG PY[/aщ%:ÌÂ×V6M@øZ,\¥MiÛ;›:'n½ÑOjOo_Ÿ»#Çuä±óªLäÝ2úö„^d°KEv1õ`4? ¸óG¶EØ ! J¨64Šä¾ fã©¿ùmU%˜§5x2> “•ͰÎâŨ «!i|± &[Õªálµ³ü%)¢ֳŒ¦ÕuÓ¦ýÞCãô°…ÇĆ_޹ÿ²ío0"5qÐðýÇïÛâÈÞá !>É84p•òc /"ip9E Ä“¡6—P:PÅW´EÜV²|Ý£mÀ±næ!‰_ÎÑG7lÚõíæñ™ÙùCDËBjáurX0ƒÍø)6ÃS*ÈvžÓŸSGNóœÂÏtVí ÓðÂí"Ò»¥ˆ(ã‹r6ÍÝWn„ fœCoÑôƒI"*À˦þÉŽ O#4™Ãf¦Qì2h$Át3žÄà6§ºCºÃi¬¬É!‹_7åÜoÄ.ôÒ¼…3§ƒÇ‘Ê%ž.>üùöFôˬ*l0á ÌÞµ òšZTƒÌÓçm#Møzú®ÆíÏQŸ'”ŽG•(,3Aº8æ‚NÙÚý×%(_Á£E¬Vv¥@}ÍzBFúÉ*OhGÕ§4ɬUSp7¸U…—ÐhÒ ³7˜ŽV°íP «FÐÐ×X³„2äÚàqsv^œbBvçµãPªZÑʾad¥H ÅBžÓ¬/oÆ× ªZ·*$¿ã:DjVõ ä ±Š>Tºö̙҉É$ ö}ÀIqŸò݆mªlT',±@X"?bjêÁ¢kl jl©}oU#Fž5KÐLŽŽfËk`Ì"F× $ö©Ò!T:©ËÀGh‡”’¼n¤²6ŽŽ ûŸ@ãu-¯q°——±)¡Æáà ¦zlyònq%~k€ ¥³§‰¤¡JŒnUµ®àD ¡0£Ñp9 dÝ®6H¸!À†ädLž–ž“-b)vÑÐñî#ð"áö˜=|"îriíô=c#Œh¢ ˆÌ">Ï”…°ºÎ)}>Ù±Ž.ª”¾^Ó{aoßÞ»÷V´ÚDEµiUýQi¬ô5jhH‡Ã Çp  ô¼})‡ÅŠäîEíL¸3*¼ 9—Ï\†ÜËjb.‚Xd“;ZÑcA§ŒÚÐÐ>veB½lЫ Yä• UèôQÂÓFð#Äà–Q¤8Ūëf`§KŽ©ˆ(ô4@3öæì›_Zu\_ùÊPܱ:¥ÛêàêÚ¬˜ça",ÇAøÄ†?pXÂà[£ø?:©ë:©›ˆ îÅW”„ÃÊ/ ”cš š‚¡¯‰x_‚,)—`‡‘ïH¹H8äP; 'î“cX4šÝWƒ0šVUk&ዉÃÀ0ò5ø.®k¬ÊœŒåuKìŽx;ÃÌxf†aè©ûþ@xE(ÑD·{ì°#¾?«©R±ÅÂYÐõ¶éH ß]¢ô`äÈgT ݃¦R“Î9‡†åa•Ø’ìôûë ²S))t‚Íqö1íe™âÖ@8ÚA&²Ï&sVõe2¦,RË8U}ZWÍ@ý?9‹Yj±ªÁõ¦3‚‡YôPÐÚj‘MfõPüçl²WÛI©·Üö˜„£ A1þ·¡¬JäÆ,a‡ EEáq‚ˆp—­D#™»?Xhâ\jXkw–÷ jx[팾(&F ×Þ¡XÔö#Ž8ÃLô{/а5Ô\þ; 9ÑËçNÝ&•OØHØï-+p,ºÈ*{ÌÊŽˆÿýª«º endstream endobj 17 0 obj <> endobj 21 0 obj <>stream xœMS}LSW¿–×7(¥¥¶.Ciý˜S„ ÈG™­eXÑ)`f4£Ú¥…–ùPƒÎèt¹ÿ ˜ùÙ© ‚ÂP†(ˆŠÎ¹élE}d격«ó#sÎq^w—l·¨Ùò^N^ι÷œßïw~Aâ Ä0ŒÌ`²V82eU± ±qÌ$!’Æ cE˜Øüÿø7c©KÅ{Ç¯Ž€} Øår$b˜ÚO› g]…µ¨¸J;9oÑ’)S§Æü—‰OMMÕ®¨{]ÑfX*­EeÚIô£Úbw8K-eU3´zÚn·®ÔÙëœÅ•Z“Ùl1®å›ì›6Ój·:ŽjídÃmB\\|, I´£vŽ£ª˜^ËÖÎv8lÿO „ÂõeCEe•ÉRTlµ—¾‡Ðx´-DÑb”‹òÐl”‰Œ( ÍEóQŠDrª£dô ò2ÉŒ‰ùŽy”Ô*Š­íµ‹cÄY™¿» Î-¼ãb¶«‡X@Ð OØaÒ|›%×ý5Á:vâß3Tð6ø`<ñË„aììžWÄeî󣕷.«y`ñ7G>o;Ü}¦ù"À§Wµ¶X›VbGºxè’`_ýéâÞ‚¬¶tlÀ8–/æ”'¯ë%2ÿ„zåaº½BªWw„¯T$™(H8I$iNd‰a ƒD aD§™#éýX¢$Ф8J"â}0 ä¿<„0Ì?Pïñ¿áaz¼"ä«ì溚ŠÚ¥fã*#æÈL2ƃ2!Æ@Æ•K«KNkŽV»Vúˆ»Ë.%Q‘XzÐIRˆŽ”;ªƒrˆvÆò PþÛ\üG+;äo°ø÷³ž7q7%i›—9ËpÊkC”šðæKjǼ ÷Šö@’jë¸Á9/0÷ôÚàonû©y5ºöÍ k­»×ìÇG¸žŽŽž¾CŽ%Û¢¼ì–{Ù—¦c.!kn‚y‡õ`…fõV|Ëv´þÀºö­ý¬—r¹æ•ù9–ãîÍQ2ðQTn˜åb@Í‹@­æ…*w“pÈÕ“\b„VæŸ9ˆîŽª­§ðu~$ô¨ôѬòÖ÷yÆö˜HÊYNI•>í᥶¡‹š©¼„|)¬Wô×X"#òiÑ$„Ècž€B?†PªzT½òÝáa€ö~|ÊÙ·ôç˜ó¨˜Óé>4&S}•›SæÜ,È;’™‰ ªM¹œrïåØV ÐHÑ>ãEÏÔ#0¡ñÜ—Ðã¢Öc=!|¨G*å¥aý DÆ£Ò endstream endobj 9 0 obj <> endobj 22 0 obj <>stream xœÝz{|E¾oUýªzº{Þ“™$“d^ “ˆ<ÃCF䂲lB@4D$œ]QV‰P²ˆˆ*/]ž‚š¸«Àº¬xÖÕÀ²^ÖÅ!)ί{@ÐÝ{½ÿÜs>ŸÛ•®®ª®çï÷ý½zB(!ÄNª â!wåæóÊZ†Ùðñ•c«õȯ ¡çÇ?8=|ëŽ*6œ"„ «¨šX9’Ý{ÈöMï›U‘èo=Hˆï¡IÆ–ÿì{ïÒj16vž„ Ž-–g°?Ž'­&UNèêz8©¾oêø±W׋aÖ«rìCUÊ¥!Üh O[9ájÿéF§ª©Ó¦'ê­Œ}†«˜P5gìÚ¿`ÿ0!–—iíH+ÈnòWÜ}²–Ä!D¦ l5ž›è0Ò„ïÇaÏùüQ: Ÿ•|aø~?†s2Ú‘Œ#÷c)Ê×ÑÝd'ùGϧ‹Dq·ÑÛ\Þ˜ë;ñ.½ Y!)ᕼ¯çóy=ö˜Á+ø|R‡y!û¯ä³ùq>›”;£ƒŒÛØ©¥E4‹Ô²ZÚ›úiovŒìÃ=WО´–vGÄÒHi1öÜDf2þŽ~Ksi ­ÇQß‘ïhk¬€ž§Á/#B‰ÐI-yšz°¶›Ã}I¾%Ó8ÎJž¬h$ï’3ä÷ØNÈdÊ0@;шéÙ@&#eÎP&¯%Â+Ø%ÒDÅÖ³K4‹2LBjކc¼ŒÿŽ?o‘:”AGA/ÌG=D#­Å]œQ*è,ìg¤Ù¸N{—mÇ3î!§ñ\¸:Åf³Zršn¦;qÇ„ð}¾ó˜Ñò£+l¶„a0iC"6Ä¢‹ÄJ¸8Ç"øä·ýšUÓl" Ð(qcÞŠFHW"h+ÇZIÆ<ój[¦ÙÏ( ›ïCdæARŽyÀ|›Aü˜§“ æif‹ßÌSÍ<ÅÌ“ÍÜG½Ä³úÌšQšd–=fî¤2ß;ÍšQj§6ò¶ÙÍ6;ÙK8µQ+mÆÀ¼Û¬T'ÙØf¼ÌcØf´ÕÌ‘ª™["FnŒPêŸo/nK¢Šy.aæÜì托ÙB͜Į̅+·‚”Ð|¹­h–p¹-Ä%|©Ÿø~.\êÿŒÃwþ!ᢄÿµ ¾•ðw $ü-ç%œkÒÅ9 M:4Åø7_ëâ›|øZ‡¿Æá«g’ÅWþ‡ÿŒÃ—XùRÂY _Hø³„3þ$ás ŒÃéS©ât9œJ…“«ƒâd9|öiT|‡O£ð‡£âqøý'^ñûdø¤Ñ%>ñB£ >¶Š†0|l…°ÇGqøçÿ0 'ž³‰Ypü¯8ž óˆ¼pÌGñõÑñÂáC»Äa ‡–ŠC»àP5?»ò»¨8X cüwQx_Â{åp`±Kðnì—°OÂÞwºŠ½qxçµtñNWØóvšØ“oïv‹·Ó`÷.§Øí†];mb—vÚ`.¶CÂv oùàM¼!a›„­êSàu?Ô%ÜgK6ãcs^Ãþ¯¥Ã&|lš ¯Jؘ ¯HØ áe ë%üV‡u^Zë/IX뀵1¾ µ&«qÈê ¬ÂǪ8¼ˆ‡1^°rÅ.±RŠÚR±b¬¨æµOGEm)ÔÆør ËË$<ßjp`M0v–âÐ¥axÎK°iÉ@xÏJxéðL2,vÁÓQø„Ež’𤄅ž°àñ¨X áñ(<&áQ ¿Î‡_ÕÀH˜/¡ÚótxDÂ\ s$ÌŽÃÃq˜%aæƒëÅL ®‡ÓÓÅŒ8LO‡iqx`.Ü/¡jj[1µ-L‰Ceî‹Ã½&K¸G¤ñ61)&J¨È‡ 庘 ¡\‡ò?Nãm0N‡±e>1¶ʨ[”ù`Œ£%”JÔîn1JÂÈ»ÓÅH wcíît!¡$¿”0ë±+Ã%üB° Üå…;‡úÅqŠ/†ú¡xˆ_ÇaÈ`·â‡Án¸#ƒzÅ  ,r‹^(àEnà€þqè××+úù ¯úÄ¡÷íÑÛ ·; ×mQÑ+·áœ·E!ÖÓ)bzÞê=p«zt·‹ÉÐÝÝÊ¡«„B/Ü"¡Kt.H£PÐÉ+ Ò `/ï¤ÛE'/tªæóm¢£:Æx¾ :ä­$äáüyë!×í“ ]Û®¢]Úú¢¢mWhS7—Ck 7ù 'Å-r‚†hZe!Ú´ B–2‰]dÆ!â„HŒ‡½Ò!„@†_¢áL~ÈØŽ:ãžn‡4ÿ@‘6ü¸¨ ¤JHqC2®–¶ù¢à-‡$7x$¸±î–à*§Ã%œIàÜË.pTs;¾±ÇÁ–V<š5¬Õ\·ƒãšU‚E‚"t¡H:ˆçq€r4jnÁ$j/» n v Ûiù£‹h›ÿ?.ò?½ÿ‡WÀðA+d ¯ëГ·Ðn4YÙhÚšý&UÅŒ“Ü Mˆ«¡©¡)/ÉqG#îH'ÍÓ ½ùKYcq\úö¥µáP¢£0=D és(l/YÄé-Lnª«a`>l`cØÈ’DØ-#ššqâÜ‹Mùy´>¦ÑÒ$œÙHº(“n9OºD£<}y0¯7çÎÁ]¡_I¬äB¬E@€‚ˆ X‰U ¨1º¦X5`±(à€ãX€’¨¦áºe§fÉb¥a»¶%ç°‘ëÜ×÷uàÔwJ!n­{S~JazPqÎâRñ¾ö¼±,ÎȤ±œ hD KË…l-¬u‡ŽÚ(Öž†jí¨ÓÜS¸¢jz:ór¯š®#¸X”·R¢j½++àjW½Ø>LV¦Úw°7øêN=­””j4òÃ43EN•/Éõ²Kú,íGûÒçX¼E¡R2g'eˆž1i¶cš3H3ARbÖ;’€âj>`þ,rÔ¤øn:Ÿ­»|R4^,Oã¸%W¾íПÏ!/ÇnÆÆ›êÖ­Á÷Ñw¹ïÝÔÍn¾9ºÒ½ì¦ n ¥[Hºßáµø3orjB°p{ ñBÌœ½Øä’_º¾DjbþåņUµ¦…Â`a¨0\, …KôÒÀ¨àèÐèðÝ‘ÉSSƒSC“ÂSÃS"Ó­ÓmÓísBsÂs"5Öçm+‚µ¡•áÚÈzëzÛzûÆÀÆàÆÐÆðÆÈM¥´”ºÛÓ¬LÅçMÑ õyy$³UŽ;Usç‚NÙ¹´=-èÔ*’Ïù‘Ù_OZøë3Ö}ÿ‘<)?þüóÓOSëœG¹`韎Ó0u̦\¬—ºÜ2¨¸ûí©‘ü£»ÿù÷δϠ;† î;(Éû¨þó Q3~C¼+9&Þu"c1P„%`BRE=)4£Ì†o5ô€¦sQ“Í*_¤0]S-‚c`€¸´ºêC@K$Àw¶¡É“@ß5¸%þnž%½êT=W®WèóÈ<:O§M×ê«ôw0Çô¹îò¨ZÈUoÖ¶¼¯è§ö×J`.~©L‚{PLTÊlÓÉlú0Ÿ!f«Óµ'øcâ1õ m9¯KÕZíMu‡v„¼GßcG,ûÕcÚ§äú ûÔÒ¨~¦å"F™j&à}[Ö‘sYkz„µ–s[6ÐåG©K^qÄ;»3¡+&_ù‚ø.‡œŠÅì6æ°v†‚B±¨šàzçP(M`Ž¥ Ü{Ô׺ÊÍWE_ÇÝ]éw"ðŠ3ï¸ÉÕl ïì‘÷ó®œ÷\•`—ãÂðêcDf½j¥´46ZÓ4]³ZmV»æYi¶4{š#ÕÙVm¯µ×Û[ÛÛÚÛ[‡ ÕnZ7½›µ«­«} V¤Y‹lýí3m3í;ÕÚN}§u§m§=êP‡êкÝÚÅÞ³õ˜ÖšA—€É“M`f縃ôF`vÌS¦}2¦büÀ±=iÒyIƧž›{ï™é÷LPÙóo{/6ÿŒ÷òò:´ioÕ²V¿ºu[VuuêÔµ0/×®×þ¶~SР«_ö^Fœ´sìvg€8·ÚìàŒ@ÁÀøÜxsb/4ΨCg.AôUêg@¶ºœ«¦ ´ì'®kZÒchI¯©%‰©%SLBç_lr¢p_¥òO¤8‡DWM”®3>x¸0…YŒ³ l:{ŒU³çØb¶šmcufÚ‹©`”ŠÚ„@A²5™ú„OñYZ‰VJ+K)@ÃÒYél)t:û>´ŠxÑG™È&(1Ä®²À¾Àñ<«5ÊrûrǶ6òW¯8ß »a;¯Óêôw¬;ì;ÙûÇ!ç'ìKVˆK2Ô,uÐ6´™Ô“úim‡…lÝïíÇ«FÞ•¢ó²•]ºÜy×Òoº ™xÞ„¶ÏƒxN!±T;'T?é9á;ìÚê ÌNú¹ív§+qŠ3¥ûâY$\aaÞ¶2µŸ,Rà€‹¡•}·"J’…G.³»|ýÛWUËZZ1ôÍ)û±-çÒÏLIËÊymy˧¼lݸÒó ¹*A¹*À}øI—Xš);Ž£ö}›¯IAÙI³ô²o·44ùM×$å¢!)yÛÆ¤ÏKg7êR<·ƒf…‰ÛE"Ò‚K†Ô¼ürͰçcÃ^û¥üPn¤ÃinÉ«ˆÍSùy[^xaK~y2¢]0÷Ñ.!r6Š÷_hóÒ¿§MÓÏÓÆý3´Y¾ämoˆqÌ}@ÒÆ´Iv;ÐU °×úœã°¶Õ¢+v¢º<®æuIÃäꕽò¦|ÃN^<Û|Àm0úSö¤@SniîÂ¥Æú}¶ÎõÜ|ä&û^ÿmK3/Û>eÃN OD®kE ´‹%)kŽåüÔ!P0òC‡@‰2 Ÿ!£‚‹«aÛ*È®º¦MùÿÖÚF~Îu#Ÿ‡f~8«@oøº ü‰«þOì&¿ ŒÚAGO/‰f@Ï&­hhÍ;“N´ yžÚ—ô¥`/SR§ àq±@©!5t9,çKE­²Þ¤; ÕÆ;B›B“éDÙOÎäeÍqP.¯Nüæbèb_öÓÉí±ñýjGÕ±ÊFŸºÊsض,#ÝÇTŸôfvg· S0ágh‚³¦µ<š07Ð3`Ø&_ÄT7Ê0 à ½y—}åS÷Ÿ›;ãÙr H3©J{ÈÅ3Ë&ý‡‹u¬xä‘Û{˦¼´7ì¡]åþ%sgL1ö ?è-V˜Ç¢²Ã¹GÙ‘¶HÛ‘ª x‚6§–šÎ-´%û\–d?D Uĵ°«0!¹›0ÓH¦+37³8…—š:¢ u']+$b§Ö+C•(à9zôÈÆ?OŸ1}ÆŸYÿ9 ä)ùIË|Ö í{J,)|ÇPy eÚ¸ñcÇÊYÌßjÿ¢?4ˆÆÝÇ+—_³·WuJð-òr™¸P·'T9ªQ&ªDµ¸ªFL¢x¿oºî3̱:y*æUØJ^ÒA1„6&²šÇD³lz gQÄÊüàç~áWºY-–»¡T)±Œ±L…‰b<ÈTf[À£üqñ¤e,EÇp…e¤%³dÑUíÍú‰"u8+U'²26AÌdUìA1G}œ-OªÏ²çÅ ÕWjîԌé¹aÔÞ«[N³Ár°hl¾ùuº¢E¶¬¥Ëv Ûf)DþÙȱŽÐÙ¢ZXgÊTã ÚY×µ¨nAiSì³ M5$NW:p½;Ñdcâ[„ñUâßË]½Œh¦BcNÕ©…˜ÏRCšOoÃz²þld¤ÿ’°ŒÐ'±©–©úR¶X]¬½Âê,uz§ˆ#™úÁ帙F¡+-€!4ýÔáê­Ø1‰N„ êd­Ìñ¨ºP{^M½&f¨‚h5Ìð×tú•ÏÉ å¹D46ŸP¼ ïÝ|¢—w_•;þÊŽÖà…ØÍšBü!+ù(帲ÊqÂ>:’±* TÉJT»f·öÝÛ-ÛˆíÐ1Ô¢é‹\lF{üŸç¿;_xõ›Â Üœá‘ž9w„†K#SÂS"„‰Tå<~*òBø…Èká×"o‡ßŽøòƒy¡Ûƒ±Ð]ÁâÐø`YèÑ`uhIpqhmpu¨>Xr•Þàsõ Ñk^ñõ ã5s‚$ÌÙÚªûG°Þ#Ÿï¿mþæO©“f~üØo¦½ÿ‹i_M§¹ÔN/ *ê}Ç3•­o™¿¾¢ôÈÚ÷¶gübHûöÔø›©‹Fa¬æà³H¹‰ Œeg ;æ;šÜà_å ¬±ÐÛ·f­ñ|FN@¶Ø1_¸—Ò­õ5z´8ÛdÄF¨kˆyÞ¶!7—ÝŒ:)jì™t"†ƒZ1tÒõ¨nt)×ÕÏ8&/RÛ±õEt’|Uî¾çÀøÑÛFÖ­kš:ç¡iUsæì7ŠÞ¿Lo5~}³[~+¿GhJç‚Úu ¬«©]µniͺÄ÷9X„òšD†Æ2\‚ÚÔ5 ]E^r([u†fÞ¢ »½ÓêuXgÿáK Õ´M×màÙüft·ò=¦Õ–j•¡ŽEºhÂ×@cg„̰èýAÝi¾ü@ÖÖ×ýDñ~Ó¥÷à+¤y5”Q2ø­-¦O#‡‹Ñˆ9R¸[,²†œp¸×ˆêaÇ2úGØHëeyÑL3WÀP'füá:{Ö$êYŒˆ†Ë‚=p ýŽpHº†Ìcx>EÛf§ºüîøŒmë¦Íž=mÚ¬Yk[¶):fò-‰:A¾U ]^Yµê• «Wo@îg“aä<Õ鋬;û ÖÁW<Ÿ?Èëù%tpc¢\Ìï+\i¯¼¨\²f]¹‚yØÈ±nüSˆãÿ8ú_éñ¯— ©?ª;Í mµçê.®ýnÎÈO¯ ¼5¼SHÝ\Ãß'$ c2/Úz›á£¡`(ÄGì$Ù쟠,Å™™9›b¬Ø{Âä±ÃgÜ5vÊ´v½¦ÞWžèAèbÜÝÿí¥þ¸z\¸ò£†«'àÇH…˜At¥ É~²[¼K–(ˆÎß%“EâõdßCJŒ'|K6‰yfy5;„‡“Ý ºbÌqË{È&u8Ù“t•¤Dñ'ÚÅd“å;œ¿Çï$£`6®·}ø™s˜ðöðëu[vŽqvÿ %vhqRðÚóŸ4ovÓ&`ßNý_”£ endstream endobj 26 0 obj <>stream xœKaa endstream endobj 13 0 obj <> endobj 23 0 obj <>stream xœ½” xEÇÿ¯mRHB¡W“v€RZÚ¤PhËz—R xTM“m²1»½¼o”¦Pð¦(( xTD@ED‹¨õDEQ<>Q5‹©Ç¦œZ¡| ¾owfwæ½÷;³¿!€ˆ—ssó›ÜœÑRÏ9EÞ­/å¬u“Û?—)EIÚfM´¤#):@Š lÖÈWP´ 1Í͇ZM°V±¼¿V±*t¡f¸V±[«U¬ÐŒ„BN%Bh%m¦mF _ÍZd»Ø”Í»šÜv«Mdñ¥s“˜ùèPjFF«n:2År8Ánu²8ù¡žsð®yrŽ,–-{;v3³:š\6™,Îâ«49¸Z–gwØ].¾þøÄi))©z¹IËb•vNtšæqÇÌ6“Ûd9·À87w,QÉÉÊmœ|›œ¶¬¿æIOò·X ïfòê1ÿò1®Q䜂w þðiVd2×ò B­™œVd˜a`%|ƒofI¹¾¸0;·¤,× 6ŠÝßaáD“Ý!Nð7îh [ˆ ( A?„ ? aG"1ƒ0C0QÐB‡hÄ€a†cb1q…x$`4‘= HF R‘†1‹qHÇxL@2‘…‰˜„ɘ‚©0b²‘ƒ\ä!(D¦£3P‚™˜…Ù(EÊQJÌÁ\œ…³qÎEÎÃù¸&Tà 8ÔÀ 층p`œhŸ—â2\Ž+p%®ÂÕ¸×â:\ù¸7bšáA bZqc nÃí¸wâ.Ü{°mX†{q–ã~<€ñVàa<‚•X…Õxk°áq<'ñžÁ³xð<6âlÂflÁ‹x /c+^Á6¼Šíx ¯£;ðvâM¼…·Ñ‰wð.ÞÃûøâS|†=ø_`/¾ÄWøßà{ìÇøð~Æ/ðBÂAü ºð~ÇäÇ:€)ˆ¤¤`êC}IEjÒP? ¡þIi ¦!4”¢HK:Цb4Œ†ÓŠ¥‘O 4š)‰ôd dJ¡TJ£14–ÆQ:§ ”A™”EiMVËæ 3ú‚Õÿ½uû†åùÂ[bNè²l“®×4õ’ØÑ»X—Ø)‰Ç^%1²÷ µ*¯±³Çh¬.äŸÝÑ͚߉‚⠾ΟÒ;TåÍî ßåÝî—ôiƒ«TJoK—¹Së×¹¼ƒtfUˆ£#מæfÿ¤Þ¢UHa mR{›wj›r‡ªS½%ü`c„´/2Z¸dЦï†Ö…­žÇÓºq½F³®U~\¼È³ÈãiÑô£ÈãÑW"}Ð*¨ÿ§càxØO4.‚DÔ¡ h< ÈoÆ-¸õ/¨Ÿ è§ÑŽuXZ ïÂGøŸ`w7Òßb¾û@@¡Fáq HÇѨ£X÷@˜¦ÐT2Ò4ʦÊ¥<ʧ*¤"šNÅ4ƒJh&Í¢ÙTJeTNTIsh.UõÐIlï‚5«÷GÍ×¥ìÞs˜WióR•Ϩ¬’ Ú£SžAî}:™™#=§—鼡´¿'¥7©Ý;IÕ)ˆ<ƒZ+nõ†•t„‚¦ZmìjVÿ uÑ È endstream endobj 27 0 obj <>stream 2014-07-14T18:12:07+02:00 2014-07-14T18:12:07+02:00 frob URWGothicL-Book endstream endobj 2 0 obj <>endobj xref 0 28 0000000000 65535 f 0000002071 00000 n 0000028240 00000 n 0000002012 00000 n 0000001856 00000 n 0000000015 00000 n 0000001836 00000 n 0000002136 00000 n 0000003957 00000 n 0000018233 00000 n 0000002197 00000 n 0000002754 00000 n 0000002363 00000 n 0000025197 00000 n 0000002913 00000 n 0000004354 00000 n 0000003330 00000 n 0000016586 00000 n 0000002270 00000 n 0000002300 00000 n 0000004687 00000 n 0000016851 00000 n 0000018454 00000 n 0000025422 00000 n 0000002518 00000 n 0000003637 00000 n 0000025122 00000 n 0000026826 00000 n trailer << /Size 28 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 28402 %%EOF stxxl-1.4.1/doc/images/pipeline_randomgraph.pdf000644 001411 000144 00000014314 12350112610 021363 0ustar00tbusers000000 000000 %PDF-1.4 %Çì¢ 5 0 obj <> stream xœ­UM1 ½çW䯂´&v;ሄ¸p*qD¨Û-‹:]mñ÷y™i¦_€iÕC'Žýì¼ç8>ûÐ~ûÿåà^~0¿þî™$Éþ—÷ý‡·Ž+3Åê-Gª"~p$ólæL©xU¡œl^çT©DõK×-)1à;BL™,©ï9úzéØj!- F‘g„¾>äè–^EGèUžŸc龺ÎkòØ­ÙG|”ÀžC*dÕïVpzÌ!à씋%}Œ[Àe QL™îÍRµú¬‰jŽðJ–©dE ´À’3SJí(J!ç½HN¡ÄeÄþôÂo]äJm>5(SC›c?º÷(r}1ü%Ïi-—™o_<Tã[hU®6ždp–…¢´ß¸*‘ -K€6g-AKðISP\šEˆC=²täS©c',— [(¤QJ+î£E!®fÊEàÅåàô-s²&.s¬ÄiäEKš|í(gz²Æ@Ú(Ì’æ1T> nZž Nr^ÈEZèøTP_ÿ‡Gcðx’³[TáÄ€«›`ÁÌ–Ž\ÎYäTp_gdžÚçQèHâiøð U0ø8>fÅ=Hô+›œ2iïÖ ›§ïýßrð¯Ù¥­ÑŒ‹Û¦‡¿nó\ÐX’!*å¨1úÅà®v_¶7÷ÃçÕÍzõùûÝêËð|ñ͵¡%!ŒÛx2¿¸qWÓö«W?·w?WÍíÍJ­h â \ngïÈ“—*˜ò(©¢™¤$i·¯ñ ®P9X0q¹uüŒ°_÷ˆØ[D…P|Gˆ [yÎÑ׈ØW1{쫜ÎÎ1=¢fT‹/èõ2> ºb”Q€×¨‡(š£W@yhQ¸±`:âÝä6„¤@fø§P)j9RKC¡øÉ‚$êo§6> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj <> endobj 11 0 obj <> endobj 8 0 obj <> endobj 9 0 obj <> endobj 12 0 obj <>stream xœeU{PSW¾1sÆ"®‰)"š›éìZ;¾¨mwÅ>¬Bµ]ã+<}°€!˜$$$$ Â#„ðH"OA­P\ÖǪÝÚZw þÑݪ»3ÛÝ9×¹þ±7îìì¹sæÞó;ç;¿óÝï÷ûXDÄ ‚ÅbE¥Hå’âmb¥%~M","Žà1<„†¸ÎÚÍj[¿ÂÉŽ`"~axy™ó:'°ÁZDTM4Uƒ¼Ôúë¬ÇK8i‰ÝN½Áor´Ôµ"tNt@b³1£p¾d4Ühâapõr{Áé^Ùg$-HT®?^”ë'Mn4 ±<ͺ±7S¦?x@ð-°ÖŸÐIÍ)e±Ì‰– µ3È }Û¾gc•ͧ_ßB è¸6a.æ>ÿ7à˜Ïèµd¹’¿ts ½‘ŽüÍsÏö^ÔÊÙ²ûè|öùâƒEËÛŒX¼@qƒÜÞ'¸l)†gÀ/©ù™¹+AzÍ:‚þ9ͽ³óéüõþ«S$} d!ÉyÓzln)îJ'š€Ô33ØGÇGÅÕå–JuiI†dHÕ¢õ in£;¯¼öÕòâ@Á§‚hü„aíCëë%v3µžßæpÕ¹ìs•e‘´ ãVëAÃ†Õ 8ô^4± NΚ\6K« ÝØZåA°×ÛÒø¤9Š’¤ä_­>MW~´”¡(ÖÒcòQÛ}å=Ü/qÂýÞVü|—_7Dš9µµV»½ÅV!K½¹ò:œjuCaÜâŒÄäÁ3Ë'Éù‚mG1’ÅfSfHežžR¦¯Â_>Ëèx§¹¥¡žÉ—'r¡¶ZP TwÆ}skþþLñБQ’&n)Û }({q xeî¼t§Ÿ¹õ€e–Šäz°>ĽŒTŸwo†œ»£J`­¨ª°Z$)Ù'˪!oÙl­²ÚÖ£šºZG-äÝ»Fÿ–ó«óÙ7§Çzg‡å­š"½EbÏ¿'ñ8à-ÿƒǼØGíî0¶q,áýÌ)Tñ ߯É4·^!ñsz'à éÁbÛ‚ŽCÚÒf4šUj™IŽ`î¹á ’׿¥7ÁÕš6Ê„t#øàjÆ/Œ·‚ÉÉÈ]€'j¨ñ¶~ÑÆP}Ô2'ž§Öõro|‡7ÿ)†w™:|‡¯7šlK¬mS$NhÖ2ôªG³»Ž"¸mï Q‰Oëïíöõ4ØíÍd­ËîDÍ00Øsq: HôöCúÊl‰Zk”¢ÈËMe-L]êž]ð<éšKqèÛ=2Æ„èŒäd¶ ©Ø¨¬Ð ˜¯ž$ºyi¯š‚aJfQ;µ2È <ÁÚ%6þ›…ßœl:{-ªA0¯’a†ºÅèç¤>ßrP[œuMȉà—å4IÇÉ ªõ S `;I¿A¯}ø.^qcÌ÷ù0y`V²ZM³N[dQ ¸+å1^#§¿úúÚô©42LÐ=¬Ä`”EmÅ»ø'÷d+"1Ê)ætcÖ¡Úˆ8•·-¡¢Aùhfw::ŽNé$ÒS§UI(Ò‚/ßÃàÙÛ˜˜ÐðV~ë£á hõ*=ïÂÿVÒ›ƒ8'Èú's·EFPôjFOó]cÝmE§ ½Ðmé@ÐßÕéÍê?r(M•YHfUæÙ߇ çþLßå }fSb¸ûx¼ ÁanE!¾‹£A¸=(æð;>Ó‡ ƃ¬à}\öM Ȧ ¨uüñ€ÚfµsªEÕXˆàö½‰ñ¢€äNYªÓé«lŠr-Ò"ÛtI›”®*@gaÚ£¬gõOÌ Æ:ÑZ<5±×Aó›Ö¥Õ¹P;ò œ.{³Ýºj=ö&t]ô†‚Þ4ƒB6¿qÒÖ—ëùCÖÇUÓþe¨<ïØž\:òc¡)³gb†lÄ?ðoG® ôëåí‚Î|w&Ê…ÉŠüÌ#9·Ÿ‡ëáì5ú‘ûèÇ_2Eò/œî£;R" ÌÏw×{ê\(¶÷U?2”h1ïócñ‚Ï+[ЗcÀ£2°†¯*)Q*{J‚!O(XâW¯ÐY_bw½`ó›8­uî:FIƒ-ÆSäËB€Rå"eöQƒ©]‡ÔSP]Ÿ®—Xöëc£_t1©mœe=Y¢ðÃîbÅãp†¥ßUv’¤›Â†d;ƒQäiõ†V4ñ@õΖææ>ÿdÇ‚S=ùŸ´œiÔf³8œy‰ˆë hâFp~r¨ó ‚sª]$Ç8Teå1+³Hé…õòúòN4‹O¼2÷wË(<–*ÐÍI©( )»Dð!cF¥ŒÂfD¯™a…GþÌËšã ;̘ë0›…7PY|yQ¡LÑWèï öËIšŽøÉ\´ÖG¥ùpŠs~åòkçQQËQ«â?|Qý endstream endobj 13 0 obj <>stream 2013-10-08T11:38:20+02:00 2013-10-08T11:38:20+02:00 fig2dev Version 3.2 Patchlevel 4 /home/dementiev/tex/stxxl/random_graph.figdementiev@i10pc57 (Roman Dementiev) endstream endobj 2 0 obj <>endobj xref 0 14 0000000000 65535 f 0000001116 00000 n 0000005651 00000 n 0000001057 00000 n 0000000906 00000 n 0000000015 00000 n 0000000887 00000 n 0000001181 00000 n 0000001282 00000 n 0000001645 00000 n 0000001222 00000 n 0000001252 00000 n 0000001926 00000 n 0000004086 00000 n trailer << /Size 14 /Root 1 0 R /Info 2 0 R /ID [<60FFCE69EDA3345A6CACAE5E56044953><60FFCE69EDA3345A6CACAE5E56044953>] >> startxref 5915 %%EOF stxxl-1.4.1/doc/images/btree_uml_small.png000644 001411 000144 00000312425 12350112610 020361 0ustar00tbusers000000 000000 ‰PNG  IHDR¼£A°QÇgAMA± üasRGB®Îé cHRMz&€„ú€èu0ê`:˜pœºQ< pHYsHHFÉk>bKGDÿÿÿ ½§“” IDATx^ìݘ$Uõ6pþú)FT²D‘$ JPPQPQrD•¤€äœs–%g–°¤%³KØ4a—h"'$(’3l}¿ºwº¦¦gv·'tœê§Ÿyzº«nݺuï9ïyO¸3%Å«V>LÞÿ x‹ð¬‹W1ÅTfªþ%Š+#Pxäáä„C’sKÎ9¶x·øxÊ'’üåÏõ˜gÅ5‹^#P€†áõ¼‡ÑÝN™œÜ|\òî­É›77ýûí[’wn-½oIÞ wÔãËðkü~¾=åÎÉ-c‡Ñô.nµ:@ê4ðÅe«=@ÃÄ““¤#I&5ûû…®yà ¼ïà‚G¹èõ×oN’¶çž¿úþûÏ÷åƒ^˜þtÿÿûß ¾oö›Pÿ;’{ÎJn-@CµUÑ~1IŠIТ#Ð: ¡íàƒ5真_l±ù½ç™gö_üâGÏ?ÍÑGo³è¢ó}éKsÏ5׬ /<Ïâ‹Ïùå7´—ÐCþsž²_›N•FZt·Õx#P€†Æ{&E†dZ4ìºëÆ -4÷˜1Gwvž²Ï>›ò“3_qÅAO=uyGLj=öØìSŸšyĈ;;G<ýô•8‰ÿþ÷úG¹ø¥—RÖÁß{ï=Ï÷~ñA–-ñ\úÞ{Zˆ–(@왢‘bf<h˜ñG4å´hXxáy'O>…'â°Ã¶žgžÙ&N<)I:“dòyçí5Ë,Ÿêì<5I¦¼ðµë®ûíïÙ%–Xð¼óöüûßÏýÁ–Ç@,·Ü¢]´ÐðÌ3Wmºéj¾ùêW:üðß¼óÎmò4 ?Q€†¦\£E§›q ÐÐŒO­ès#ÐB a÷Ý7ýÈGþ'‚Í <ÿüÕÑ qî¹{ (&XrÉ¿ò•Î9g‡¾h‹-~8ÿüs^yåÁ¿üåüÿüçGù[ŸqÆ.øË9çüB[ÛŸ‚#£A@»T€† VDqH1C1hŠQ,ÚhÀh!ÐÀ=1ß|sŒ¹Ïu×qÌ1ÛŠoå”}Ððì³W-¾øÛo¿.ñÚkcWZi‰yçýW¿ZóG?ZáË_þâí·Ÿúë_¯ùÙÏ~êç?ÿÁ|WlÄ%—ìW€†œ¹E—Šhä(@C#?¢oƒ »ì²1‡Â‹/^;uêD ,07ÄJè æÿã7ùÆ7¯²ÊW9&ÆŽ=梋ö1b§çž»ú·¿ýé¿8Ûe—xõÕ‡wÜv?<²U ¦a+¥8µþŒ@ú3ZűM4-vÛm“üÿ-·ÜbÈކÏþ3gŸ½[æž@ttH.MÝâþøÇb¢)Nb¶ÙfÙ|ó~ó›K e#®½ö°¹æúÂOú­5×\i•U–~ê©Ë ÐÐD3ºèj104¼õVòØãÉ£ïVGMžx"yï½F˜vµèC †ë¯?âX§6+±ß~¿¸ñÆ#ßzëÖ˜ qǧïµ×ÏŸxâR¬Ã+¯ÜtÄ¿… bÊ%…pÈvXWÌãC¥¤Â{ï¿îºÃwÜqý}öÙbÊ”S§Níoè@Ã_0 µXR]ãí·“Ç mR[ejÀßy§¢§3Ð@¼·srí1Éè£ñ}õQÉÕG'þÖ¦{]—+]±ìßÚô¡W¹æèäðRÜ0L^­º²%+€Bð.‹^Ìt*ûÜã³SÚ§NÅZ#2NfUßsOrôN5Ò&©^È¿¡#ºÚ -T®q2íP ‰]a›÷Q;%ýkÍf@ ímIçiÒ·J¢$ ”yw¼ûþ„—_ó^êÁíœÚ%1зJÏ}ÿÃI/¿>æ¯ÞôÞû £üûÚ˜ÿ½vSè@”×Múžœ\}L‚o&¯.Ð /‘Ž,Þ­=EEÈFYÖwÜ‘Œç,«6éxç½ñõKáMhÓýÑôB*Ïß|ûÖWÞûaЧ;>ø •ùï¾?~†¢þé“^y}ŒnôÿºC¨D&'·ý)¹û®šM€ 4œÒ¨wÛÇ;^H—$õýë f¯¾zS®Ú]o+û&“’)‹‹ž½à‚½Þzë–R‚Yü5O·ÆoÚš­¶Úr"ȹ` úwå•—âK™ñ±”^Ù¹±Æ/ßÛž"èaF–¼pMòïÑÅ»ÅGÀSn;¹(#]3µ1½ ¥ ¨¬AùööÑ£YvÙEWXá+Ë/¿ØÒKùç?_]‘õœ(î-䣠î–Õýë9rˆÞ{oœò'+¯üÕ)SÏ”Åw¿ûu>Á×gSíO?}Åk¬pÙe”Žìó°j;õ:’ÛN,@C~”;$šæ3ŸäÇ=à€-—\òK ƒ§þÎ;ã“‹íŠ3@T9@ðî»·½üòo¿}«úw/½t½|vÀûïßf›ŸQüÿùÏuþýðɪô{‡2yééNtäë¯Õàwž>ÇŸ›i¦™¶ÝvmS?Øç™gþØ5×pwp«àë°èŽ'¾ûî8­}ðf¢‘¡Ã0 “;“¶JFíŸ\¼_ñnñð”/Ý?G¯z@í@CÇé§ï¢î}òÉ;×ÔÄN;mH¼“Ûd2iŸ)ˆh׉éñ%9¡ÃÛoß²ÑF«®±ÆŠ Q ‚œ_o½ï¼ùæÍÄç>÷™óÏß3ƒNq¢Ó3{õý÷'hüñÇ/•Õ¬+ÕÀëG;¨ÇZsEP€†òR3W]uðç>÷iÊ–üÄ'>¾Ývëx¨¿ùÍZæ:ë¬ÒÞŽ kƒvÜqƒ­·þÉ÷¾·Ì9çì.þë[ßZjÅ¿²Ùf«+¢§ôÂ8¦”8²'Ÿ¼LêâÛß^ú„vˆ³ ¡ŽÖ̼ñãOP÷Æû;ßùšpôM6ùþÜsÏúéOâÚkŸ0áÄÕW_ª~ë­Ç9ñ ÷–û®KZÛwß-Ôëm`Ü0Ì@CêžPòh:4i%Φ,† ŒNìóû Bf²h*Ž@ឨ7VÈ®_SÐðÇ/~qÖG½Ø2§¿X`NYBLD†Ÿb$ÄÏ~¶ò-· ¼¶¿ÿý¼­¶úñŠ+.ñã¯,À¶n8R2ѾðÙCý514Ð矿ý"AÉP áƒJ'´ 9ÿÄ£Ì7¹Ê¿ÿýz81ÅN?å”´6räÞ´½sÐA[åèðjÓ ! §`zz RÐ ©ì¼óöXýïÐߨ$Ï^º9t¹æš+J'Ã(x/³Ì"f€ä4´Ø(¤üøã·÷ì=ÂþóòUWýúB }Q`¹<æ½÷Þ|›mÖ.ÍŒv`â#ù„¡Ú®¨r퀜¦àWˆÛ€œ‚ƒ:ä_ÉtJ4¥<Žù$”݉êçì¿ÿ–À©Ê}çÖ`¢ àÃ4Ls—K’åöÛO³SÃtA^Ûc]òç?ŸÕëúþbEšóß{î !°o¦óôM7Ò]e‡aªßF9˜l5>¥„Ž AmS©È¬»µÖú&4°Øbó‘ð˜`š[]•HØr ¤BÅ¿È"óÊC¦ì\p.È€îçÝp$8ÐÀ(¥õ¿öµ…/¿ü ?€†N¾Ì>ûç6Úè{¡|ûìT:a“MV£‰ T¨¡ùËÙ¾Yl#çƒojh\€†¾˜ðæ›eÊ/µÔ—žxâ²o|cqjÛÎ=,~ Þ#ˆ=rlÙí=iÒIJæ­¿þwÕܵ% „à²zæ™ÑfÉO~²ï†BýJô;†(§ìUÜÓ²Yr×]gÌ1Çç±fé%ñýÈ#^§v>æ˜måU’O°ûîû xðÁ‘o¿}› ÊöÚkc•l(@C¦ÉÚÞ|ó–_ÿú'“&e[Y`J>=¡Í.P0k)f%#:΋Q£ ü½ÿþóEªÑ „EÊ …*D­½öÊXаÑÔPÑöyùèG?j8˜„gC’ðÆ–[®AMð} Ÿ!X̘c€Ê‚Rà:w.éqè¡[ßpÃ%9PƒQ€†^ !B€ ”·àÄq" ^} žÙŒ  a2ÚyÖYgùÝï~zÔQ¿3c"h¬0"”ˆæYgý,¶ÀtÁ;i`ô°{‚†Y<{ÓhPÉܸÌÂSNÙy©¥Âq™"ð&Ð@ÄóJ8Æ´Ó¾ct©æá ¢ÑßYÃèG9bªL'†W—(óÇlÙsÏŸ{Êa3§N¸Ó¬ƒJ×^{d³ƒ£T f€Õ@6˜Šoü};P+Ñh ñ…êÎ$Eûäɧ*¬ô¿ÿq:tÑ<[o½Ö=÷œiÚ¸(ÆçªMßÁÊ7ý÷¿}x.xFÕ@ Vx‰4 SÐ@„Œ‰NÈëÀ_`c6Om¶¢¥ Í&òV·uŠN¶ÊØœ8fðh ¸*bLÛª«.cñþã£,mR=2 pÿüóϱÚjËryÓN':4öT r‘°‹ èµCžvØoTn ÷D…K·‡µ{lÀ×òÝwŸMÂÜ}÷™¢˜õtî©HmJªZüÓŸþ`ÞD( nã@ñÿä'ß„Ä7œxâ?þñJ4=xÐXÍrV9Ý4½Ç§qd¯Äùx –¶ å døË”Ó dò÷¿__k®þŸÿ\ߨ¾‰T »”ˉiLƒù 4Ác  qжYù¶o8õÔ¡ÌsÏÝ“.Çj.½ôB°©#Å` ^1b¬ýöÛR\­ƒc5vÓ`*b„_™‡Øä" ‘MÈSvQŒlºóÎ3ÌLÓR߄֚Q&áÓs ˆ0 (@Ã0 íRê¤Y¢-U5RíÝŠ&®QwD=>¦§,b®É [­›m¶Z(»žÊ‹Žšw¹œèטE©)§hÙéñºÙ'úÆÁˆ©Ø¬Ð‡œûßÿ¾Zk¡Ð^Í&ÊðÙð “©s ^ð³x”Ls'(€Ðà/ĉ$(ƒØyçp•„‚9@Ã/ÔfŽ:Þsßyç møe"$Ì™½Œ—¸ï¾ó™#½"+óó!ÍÈ  átâ qiÎéÿþ÷hl'HZ€†!†@~ÏYE††µËž˜T†Ëý”BšI˜G1ž¯µÐûËø ‘î4B‰‹Ò…RœÑiO9âÈÈEnŽàެ±"¨h8µXX”Ê/ùc¡ï "ž !eÄÁ ¾äAC„DSužNáT‚jO‹6(1 ¿À^Rížò³ÏŽf—`|ìɃl'¦gÍb(ÔóèÍ4œÎ1q±ê4lºéêÏ?µ*1ÅÀ § ¡!CÚ‰‚†Î–X\|J}@ädÂI‰ÊJ!ïþŒÀ{¯yë7LUh²?gÕçà÷Ç%W9¼*B÷D`êÓ€ià’¾­4ˆvV•…Û’CJT¬LnÙ:<š\°·H(hƒ#ŒÏ‚—žÀ„쉔iK‹iÀ¨&vºÌØ.w—ï3GT¶‰iØrËqO ¨4\àtð•"NyFkè md9Øß¾ ¡‘@ÃØã¤oã‹ÄÖè¡¡¾ùøšW„œ0>Ùk‹äÜ=ûíïþÀîÉÙñÝðC7rŸä̽’ÇkéRåŽLwÃ*Œ¢ðÃÀ=´ó)„èè‰/¿|“z/’ÐÑóeóèÔò’ÇK©ÇàÁVÒ‚¿ì6î~§óž¢+J¥ÊÓ„ g…š¤]*ÿ)áBÔ$_†\Œpºïùe¯¹æÐÆ.Ö_E^Ëã ÐPåÕTyó·ßžì±y¡Mj§dyœ»wbŸ°Z½fJ/Ô>)‘°ŽGE6ïVÏ×¶iÃjï‰n¦¡O–åSd|(ÛF2ýFš¥r³Ê¶˜’F ™çs1º7ìÉ”×i(qÑå™…;ÄÓã¥9&¦–  ¿×*@C­4Æ ¯Ã=që …6©&%=n9¡æLCºËeÃnXÕ_ñQ?­~1 éÆ9m Î`pïßïßÎqÒÎ:}—'»$Qôsp §ñ›‚äoµ§@ñª÷1 µ®\Ÿ˜†4 ¨1Ì@ƒ «öÜ"‘¤pÎîCðÖ0e÷‚Ö†¤KE#Ùx:#÷MÆÙöxÕ{ ÐP€†"BµUF`˜1 ²d@©ö8÷›·$ÞÓ?¦Â_ß,]hmŠ¥ÍNw9Ÿ}ÓûºÓú¾ÏVÞüÕóݨðÆc‡+?xÈ8rûiÓPo¼®_€†aì#>ý‹ÍôZ`&'W]Ä4”AÀÿsídÌ*æQÚ­ªMÝŽ|îå A§hÊ'ž·qñ I‰œ(åwÓ`Žù÷s£+,kÿô3WVRmÌÕm‡ñö;éÕ?p¥lõv;w@š³^ú¹½yÁ"¦¡!C 4d[º´€Ølü[˜œÜvRÍc&ML.Ü'¹ÿ‚ÄÆ}Å»UGàÁ “‹N}´a¤K•;Ò+{Bá„\Õ„.ÅcsóPó5!–….–Ÿµ.zìA•Ò®*œí'ü*“B¼¤=ôz](‹pÌB¶A-*Ù…ºr1(²ýì³wW7+‹}èú^ É™½ö7ÉÇTvGeÚä]|gz©ÿ|g”@½X™ËíÙ¡nU®½G£G·KÑ úãGüT*ž|”hŒñ¬v¹›4Ty5UÞü·'çìUh“ÚiÒ.LFžÜ}wåhG†ì‰‰’ãvHn>&¹é¨âݲ# úáyû'Ï”Ë6Š–Í—yŽª4îvÆ‚gÜËx”`éCjs0A‰hª´´cu›L¥Q(2…Tï*É Ò6Ù€‚²µŠÔ²ÚUqØa‡õ$^ª9K”ú›}Ž©óuŸT¨-팗îÀ·[£Å]B—ìÇ€B‡³Ô”t98#6®¨¾¼Íü9ÞÀ’EKŒ…²c;Wþ<tTÛ•bšcÚ5®?8¢˜\­ Eî&*¯öT(ŽwQô‰2YFÆ[2êßþvŽdT¿ªïn_·»î:=tcœnøU áĉNtE{te[ûÃaTlT€†A*‚¡;h8z»B›ÔNÜz\rÉÉ=5 iö„?¥”o³ÎÿîT§¯vøA*jüä}jjcM«|#ùc*m¿g7*¹JGèRE¯ðkxØðuOP]j7Ù|„ꢪc½ðø>í´?*Ũ¾“½nã6f¶­¢ªŠVÚÙ&U 3<üðHŠYé 6ø®òòÊ=ÑúÚ<öØíd`*SÅÚºæ;ßùšÚ @]~ùA@ƒÒÔ“'SØ*1Ä6K†xjÐïºë&¹ 0ÛÕoøîw¿F%;^U(u'ih½¥ÅõÁEí¢â¢šRýÚaÈ µôM· ›Ë)ƒï0Œ…3Ül¤(Ž?~{nå29;ÚÛOÖ`Î#“‚†%—\5â6mÇe³.ûÄ:ÑP¸wµœpû;î˜n°²ø÷į„%D¢ Õ¸qÇÛÂmžyfSxŒÐ¸ê™êvŸtÒï¡wmÜ ‘/ÕÓ¬ZNi†Në²¥4¦aDÚ¤"y[x$í£–éì¾Èdx¾>LwôÙZ%jÂåâ›òÍ.ÝQökü6H!ºÍžÃ2¥î‰ò”Ë!à‰Ô›n:Š‘TVᎢ*N0æ¦oC´½öÚXÆéÆÜÑT^Ð÷¦[ÕÛQW‡H"ˆŸßxFŠÝh{ƒÈžA¯þþ÷sufF‡UhÕø°á™Öiè|å••X ¶m-A¥|ð¯<ëL{)ìÈ 2´Í©™Èž¯r){Úׯ3¸‘B=ÿü=qø¦®~ô£o„’Ïm¸:U@½ûî›°Ñ]K5Iû_[ÕöS/Ìhôœ¾lÃm°ãó¡ èj•¯D‡¯d¤ÛÛÿt ; 3”»fÊ#ÔôÓžjþ:¸q<Ÿ‚z”~E„€gž¹k+4ºãs¦Òý/ø#rFhиYíŽ({·o§>›i¹5§ÛÝâöý„˜H8#l ç.à[„C“KdÛj Q\þøë_ÏlÉ]É‚i¤:nŽÓû„Ì\fÝoâÄÑfƒ‘°‡º00mJ´]«Ê*–°Ž»V ¥Û0s—_~@¬äÆËfÓÁpî@$³é}ýõ‡ú^Kgï»ï¤Ä1Çl«'¡ÚÛ˜ÓNÛ™& 2ÈÀýÊÄÅ]-¬>·ÙÐR?ëŸr™ªOìb_w}«]±Z¥ÏÙ„Èœ¯é1â-·"z‹»Î%;lYù䓌¤¬µ²6ã¿í†Æ.A¦I­©ž²ÜiºÕV?f`¹ùÎ@ ûUfé>Ûþéî>Ó%öÆÌUÎû³»cfQ<62hÂr¿Ã4tÐèö®´[ÄrË-J(د4=RÑ`AFРª´ùª2¦ÙúW]u0ôÀ¶¦2iÇ]wÝØ†¹»ì²±ö˜ •¡Xá眓î;e2˜«Ž´Úñtm$ðŠ?Â>ϨîS‡­xíc"þÑ»:`*’ƒÇ·¯Ý̃d a“MV “°óôÓwV ¢ÅŸß‘Œ5ÖXñÈ#c#¨md€2ÕyÈÒ³'] áÏÚ·ß~ûüÚýÀB-ÄbVjcë‰]=ïºë,W$aÇŽ=Ú8 læŠü8ßÿþ2|7È ÛÃ:æí·Ç©º}íµ‡ ë®û)SÄY3ã"‹+;«`Qô ¶"ÀóuQãÅx…ý'³0Ã,€&/·ã„ɇ"vÙxñË_¼Ž#ϳ D{ì±ÛúÒ6÷‘9ý’Ÿueöp‡]fЇâ~¢‡Ñ$¦coÝ»‘]½7"á¾¼• qÛméüÏ)Êìêe—nÇJÚV—Þ±ÆíƒCÔ<÷ÜhlÜÒKÙ""sÂâÚø¸ã¶‡˜46gÆ’‚&¶Ð»ê*FKÖŸ:ƒ76ùüó÷uõ‰ÂlhRØ9ë¬]mÑÄmI,zHnxüøí$-uû{3Sˆ<ÊÞ_°ËÓ‡òkH ëž‹-¼ð< 5Hd{Ʊ¤.¤2fÌÑF™BÖlºéj숑tj$ÈЮÑ7j::!ß= Áæ„Ä™‘]tÑù|±&#ŒJ0·´Á(Ì|4Y=`;™ê˜ ÙL$5BØüKÃÈß¾ÍMùžô „j»IIÓËîÅ¿‡þ[T'«'%Ðò0 aæt¼üòص×þöSOÙE×’î^ó4D üb´AÜoÝ’¦Yð ‚ynÝ(ÄŸYdæ`1 öµ‹ÛíAeÎ<õÔxJÔ7À ç‚m35Xª-]&¿zükPº±Û¯È-è„­3‚¾÷½eò yè!LÃd¥Â_@mGƒÕQZ/íø ¡WzH@Èèž ¡CoÏ8c×+®8øßXÜlÇI•ȃ°™§E× a–0jÍX`Mxg{ìR½µ­0®õ7n±q0®‚ådsÚ‡¹Y ¨dª ¡1AÓy¬}¥Ÿx"u¥å7©k)‘·f@o‰MœHÚOât;é¤?ø—|«¦Í‘fQº 1UB¶ÈlñE—Ñ‚ÍÌ4?ý ¾Ãô¸gÐ$Ççµ£Ùã•‚>8 DÖBÔDÎ5ùm–MmE²œ¯0꟡ š«mÖN=ug–‰ÕQ–¸„Ÿîɬ´[ÔÖæË/»Ç;Ÿ}öšÕW_β¥Fc" Èšzè!x( `6ó›KZqnÐ@YS9¶¾® º1F-àã°ª3lE.¿üb™|ÀT¬ñeÖ·m80'@–;ñÍ*«|K©œ>‘ %Qƾä"u B†BBawÙv,9ÜïŽ;n°ùæ?Jhô¯|ež`À,¶àší —]+‹YÌ ä¡_ Ž1ÿ º_MÞYÿúÉ„p–k-¹ä—#t€%¤ØT·@B ŽôÌð ä>c€‰{àF#ì<ý$=3Y~è£fà Ã4LŠû>ôVÞÌt+ÜL0ÇÌvh•¦·,™Ñ&°©ÈP&ÅHÀßܰUÕšk®R,N ©#€PûÙϾ%êм½]Í€` qj|îsŸ>÷\lD^e¶wv޾³­xã*Ó hòÅ/Μ) 4X:öXpÁ¹z8Ëš½¶Fl ¯†d ¶?gÏ-°;´`íÌ¿ °±´SèL®åèÍ®@H¢Á—Xã$Œ‚…0 þ‚¾BYÄ÷¿½ô¥—î/’ƒÓñ¦?ßúÖ’$9.šaäÈ}öÙgsÈzç¯6‚ï£ £×«Ú‘Ó`zSr¦:×£«¢Ûe¬#ÀSš˜µÍxƒVYei7ºƒ¸¶¬¬2ÓØd£&iT8±6|™(®%L{"Ýé¦"D\G¨ IPЀ5fuwneä–C‡pŸ‘çTO`,Òekýê3E¢ý š‘6 °¦So­Ç^Â?õŒ3tizë ¦·”2`aiÓG6—Éw† ±­®ïÉwMìP‚ÜyÐî7°*"´Ð©3¥Å×T=AÃd†>‘äyP®–º-þ2Ѐ …»a ãà šTµ–-:Þ¯L rÇ@Süf ‘Š6ðH<]| yGs?ñÄe†•ib|íø§7öA³üÁlã¢'nø½ U;t³¤g²\;Ô@Cêžð¼ÁpgÕU—a„™²Dð²Ë.Êfò,·ØâG~h”ÛÜ`A–QË/¿ø£^Â`q\=Æv¹M·C‹í:ûìÝô6Fƒ{›Ùä²§[€†ª ŸÁ6ÞdžU}è-3 À7H³ÝÊ¿è¢}žzêr(™‘±ÅÌ^áã€8E' ¢ükB:¥¤ÛHœ=öØ”º5ÿÙýq©Ó¦10¢çTigI˜e Á)l)@!¦$„Ó¯²‚t8p<Ó^§’I=}†iB@Æn7Þ˜FüXA»ì²‘µF€Æ4K_ÂÄDRNa·[ªº”‹ÊL9B9l¨ÑáW ASîËí8b@Ëȼ'±cD!+¾ÑŽ_yj@1²‚Üg¨ÁIV"ÁЧq0Ì­óÕ߈«`»\†ìü.ÐÐ hµÈÿyæ™êEþ‡ÐÝ.ïö¡‡þšÊ§D¨v‰?/½t#šŠL¦>7Þø{×^{8ü­êŸþ§L&“ÌÁƒÜ†3céšgŸ½š~!ÌM?[ÔF·‚‹ ãL ž²ôFPgyÝÑAéÌ2˧‘ˆNqbˆœk×m|!ãÐržlæSI¾9/À.ËHŠÍ€Ë©§þ‘îøë_Ï»{Fþ–é¬v€)þë_¯i/\ƒãÔ-±>Aƒh CFU§åaXƒ¥»«'hèxç4ñŒ]EV+|hJ±Ajtêúb‹Í4~EHÑÊl úž?د£FÈ('IXÞ~û©þ5ôN'˜ž|r”ÑD¢Ò»Õ!‡üŠ”Ê0ž´bR:.¾x?Qè!1lZ !ÿ ºAa }üª‘%Ș’€¡KóþèQšq¸Õ<Žî"w¼GÛ øb*Å@6·£)(Ó°÷ÞÓº¦» ÃLEWÕxª„•í×1-Í4¼újòÀÉ‹/v˾éîrY1žªíU³˜˜”·œ:5s¾– ~ôY¦»˜M˜ ®5-ÇgÚ`d½jïÎøF‡»}Ãì<¸ü_ÿ*ƒ,ÓŸ3™ç5¶–wåæOŒ½ÊE\ Óép¿æjî ¦NMž}6-IòöÛC¦ ‹†*ÓXS"€)jQøœ+VÖAÆW`™ƒùßj+éÁ#j€€v€èlú[„/á Âò8X’<‰ÂnèÆ!¯œyÎ+MEž€íNï2M½–^yLoÚ nÆ)²û‘Ð$?üáêtG¼h ˜;¸Ý©m×e@–ˆº:¶¶ðo¼ñhh†»AR è"WÊ,6Àê«/…DW¦fÅ ÷ ¬ô”2üç?¯\yå¯2WÒŠ©Q#Œ¨® !ô ó†Ž ÖI£¥æ5ÂÎSŸŠñÅ®&š;DI{H7ß|¬€)teÆ4àK¹£PÜÝ _@ƒ=˜ßþv-£ï\ ÁÜ¢È%¸GРÞÎ:ë|;ðHå ÁéFÏü‹nЀϠõ…GÀn9ôÇY ­bÌÏ@lŽ{Ñ7í34ñ°ª>`Ì<<€äzÌS‡ÑyÎ9{ä¢ßÛ™P0oðÝô‡é¯°«åñ-è†×_OËÖqD²ÚjÉÜs'ßüfrÔQÉ}÷%~,hà£!+ÙÖ!Ù²¾˜²Ír»î:£šk–lØK áƒ’»îJöÛ/Yf™dÁ“ 6HÎ<3E“ï¼S¡Ê+ìô„Tå–½öÚ<‡åTx.*‚†ÀGÐðc‘ñ&‘ÇO|^4 Êru ÌÁÈ0J‡&ŠŽô˜°$.õ5 48R#Èï2Jp#­Ó˜Ö,fNdž~jí¹ç®9ê¨m˜”¼Ô™*´»f_ýf¾BÜsLMb`Sð¹È†”iASP"¼„ZÈ\k­Tñ%Îð4¦*¤þFýqkJ•ʃ°ƒ ,7Ä—  † Ý ap¬4hHkÑô"NÛ°µÐM(ŽÛ+PÃÆ—#ÈE„¶;Á„Œ 0Xè#-¸OcHâsÜ$»ßÍ{lxŸÍN‘ž&ÀñhÕÝvÛ4° bGÒúÁHêä¿ÐTö¤=üUn¶!µ"ÒTizL ¢?®+–B̦çD÷ó¹F£gZî¸0=1 ‚zà{C…9‘Óˆ›$r]˜KV¾(YCÑÿ¬ÎIÃ!<«=¹êˆdò”Ôþùæ~?÷\ÒÑ‘zhò½ï%³Îšüßÿ%3ÍÔõþÈG’yçM¶Ü2Ù¿$ ¤íÍOáöÝàÛkUý¢½ù ˜¸Â‚ÓýáBêp#t/€†þl¸a2çœ=æÃG?šÌ?²ÞzÉ©§&û[sOûÆ_¶ÿùO2fL2®{Ýa¤(¹^•XÛÄÂpmÛi2™;h]¾€d@°N©mA9> ˜cŽÊ/Ð Åâm;pá|d56…´r¥8†v›¯"<íKå`I‹…ÕÚæZ””€! 8(i–ÿþW=äÝš,²È¼1‘ÛšòÊûÝh@'j7Ñr†×õyÏ=îô¼£Ÿ-¼2à qË0“ÖØÃ̽‹ú Tȯb$5<¡)“‡Æ :CòT´êÏ4ô-Ö"tøä'“•WN~±E2.eó*P9Ì┆6\/ùúד¼|>Ì·9˜ØÏ•C¥tn ÿK˜& Brub_ÍÉ Âj$S4PÎvÛ­‹»ëÛ8¯†(&}¦]4çìZ&¨ú\bAú%œDcJÓÀŽ‹ˆŠ¥WiXÁîÔUô$üz\ÿ]öáLÏùz4”–yIÓgÓ;aÓŒà!ÇTÊJ¤èøÌ;e»Ý®¥Ÿz{}Ú=3¸¡Äð”G‘ô"]óΪx¹tÂM»ý” *õªÛi]jvZ'¦ÉµæŠ)Õ„¬o˹'Þ}7ùÇ?R „WXd‘T[°)}¾öÚä•Wêåž(a”îà€þL•ü4N›ŠÏ-sÖNK£ç£ÊŽ©°…†Â Ygh¸ù¦”E5*õJà>ñ‰d©¥’m·MFNžy&yÿýÁïÅù•Œ@¥»\fÚ!“íù%Ó[_”}“?+•äˆj:(V@ê)Æó3¶÷ü/û¦·nJ/ÄföËñ]r;Ú”ntGÇɽ”’Òéíì–”é 2ý˜×Gñ§ì›Ô´8 Eå¬Ö† Ó‘)Ù Š„S`œnõÆJ…$|Æ©qœ²il‡T‹FêR¥ã™N²«jÍ].ß{/yüñ䆒{ïM|ޝ>bj6‘RC„L1‡ï¹ç¬ŠmÚxgKHÚåy‹ÒÊœOïA"9ºršˆï’Ðú8,¹Ê§SCÙ3Rü£ý{ÆŽMþõ¯D”CñªåT †xþ€ èá ?Ä-kÝ/³ƒÝßÓßW3y’ÝQŠ3Rùrˆù¶k¾Ëeyé zÄh•ÛF•\¥]Íkµ.hèS&ö V;.q•Œã¼Í›ÀÖ@,#ŸÉˆ²ã³™ß5]cÕ6~V Aloðn–µ'ÙônçÚ“OÄcªB ÞŽ·´ÅvŸWÌø¹9œŒ{ÕÝ+³¢ÚyCa‰ŒH‹½J½u’ã'ONËPVCæV¿Í"å²–¸`º×ªhèkAU¢b*?¦k±T2ϰKer£Y™†ÞgŸLŸ,­°Í(Êû$ÊZ(#¸*l¿7‹;ÃX gÐnù(¾úé§Å¦ôXrFö½âò»b&_› qÚ ‘‰´’ ,ž—1V•‡x…kÝrËq‘SR†Î-ˈŽÆ4ëR”2sЃ´^ eÀL ´A—¦ç¤ÄÌ墴Ò9¬&’T4ÍŠù„’•ÚÁÑ6Ú;=¬‚uB%§´!p 1ø\7òÑÁ|¨¸º|€7NU—xlûåB»kDˆКÒm'œ°}¬Á×bq†K¬ì€4 !ÃÐq÷K—¹+Ñ&e—ëïŒÂã‡hHÝ3"MrÝãH®U²½ )º0´“ÏoéSê9&Ô÷Mƒ%ªË…ÃTúüb‹‰Ô´r”ZR'|‡)hð qzž—¤ŸóÛ5Éüº,ÛJ7†¾%”IB¶ˆeéàÜm HWðT°ŽŸDt«UÇÁ)kW7„tÍŸ*=ldÕYÊOÚDÀ\ý‘ F‰ÎyÒb´@L"ˆI™†PvúÇ?üáò1˜E2zb…4ÉÀ®h½ØìG66Âg©Ë£FI?›¬€]¨x“©üÕ¨¤Å«¬‘J•HwR7ÈP jå•—RQ ¯«æ©øå.¢MþJ—ÒP·S€†F 5ƒ¡‚Ö¯‡ƒ­ à¸,X~:SÎ2 +1…Ð<ƒ¡B—"gb¨<]³ìs}5hàÊ•uÊ÷²,b%¢³2—DÆ|F¬×›ÒÉÂ::„ŒJ† œjvdÚ¦V‰1bKh1ßHvÑôB¬C¬¬\í„MÿòFd9ZDö†m‚;ÙŽ¢OÃ=–Ë¥+æCQ2b¹M0±[¢p{ßxWk ÍvÚ ì½VßÉÔ‹hiÕ˜†é¹':¡[Ùš)~9T!Ñ&}4”ºŠu–jèA¡hÈ’2Vï…ªŽ$„+A×Ѓra,r â±ì ü<êüµ¯-ŒŠ t ÙÅæ3e|Ê);›iÚî$d‡ŠaÝóÀ¬ïé"m‡ f›mùåR­BpC;P"gX¼«'zèŠX7‚x€„lO÷ÇRR®Wjû¬3¹£v5ÓTËáUõ‚œ‚o°äe‡­^ ÷DÃhß&íHO÷„E„N›†…V¦ ò²=S™[°O]ÓÃëg5Iª—<4€Ëò9+t ªßJøt`ò˜ ¥M(bzkŸŒch·£¥1Ô ¸_à»þ ¡sôèCiÇ2ñÁÈfÅ”K2TJ ¥(Ú‹™‚ÅR˜”s"&Ë—øX²Œ˜óoÑ*µ’¨Åâ±fù¬È’H“˜–©AÅ£ükÄFÐ ±øÀ7ÒXÃÎRi®›nRk’vrI)ib¤mxÂÖÝj›©j†O:ï¼=T£´/'nui€S·Éšdç™î€ˆ««&]‚î1™`ÝÖ:@1«¬P(4žè^\ [5TLÆ0eO*—LniA椉íE;¬mKÍÅð¥/Í­˜ºdi+;ãtÿ†§Ÿ‚ST±XJúî»E –͇2LÉ=±…9fî yç7™ÕÈLFì¡ÎÇD€b*ªBOIJ•z"ý7ʘ°ü%º@ƒ%©ñ@*LŠ]­î¸¿TšTS7P·{w¢†ÍÛÞnAÂv:²ÝÌdÁÃñ´rÜÈa†  6RáºÛn;ΤÍö4–šhåòÊ‘À.jù´ñÄe™ö𥤤DöŒ—LÁ:ŽŸÑúµâ&ÆÐ¶M2Ø‹‹£)H¤DŸ i`‡Ý´ë(êë  &šb ‰ n.®ªÍnê:‡ôSŽwV@CR,UélÛUjD°{VP-1Ôç?¯Ë̲‹½|BÉ­ñ uù’BEÿ¢R¡ÖèÛçµ ò ak(©´Às¹D¬NIwÿ/|¤¼/Õ’"ß}”HÝü¦8' ®LSþYа%9®{vÚT LÙ(½% M ÀlP¤LûºÇ”Œå§p¶ä¸>hŸøæ«Æî’àØi7®'ô Q«ÔtT?ÞNfzŠ,ëÓóR?L:LACdæíøb$pwÓìx<¿r.h'TŠl$TÜ&”piWFÌÌThÅÓW hsEޝ’¶AO“D•{-ò õáw¿ûi¨©š‚-˜ t¼6óä¿ÉLrõ´ÀÒ˜kĉ¢ðÖÈ›oÞj62ƒ¬G+…ãÃBs zbêš·|ÑÒ÷’¯9fÑ9+—Ö lÂs%øe€ý4ucš{ýæg¿¬«"¦¡pB®”@ƒ‰g†SáÈ0VYÏÙÞnM[¶ïc¹™ö`1zXõ?áÀb}ìþê*Æäç> )l¡?–[¨¿”nÿÇ?ÂÐi²C G0vªìòÐÁäv\Ý¥©Þ1ÐJK nQPøÉ®¥ä”ž 0¬kÇØd+rÕ–s@3öãîéÌ4عõ “ þTÂÑÖyxKŽØ,n+WF:&[ò Cñ¨LmcGxQð€žp*Vð—‡M£"È2\«È2o-£F:õ8áì®§÷”]æŠÜ¹Xűú ŽL#Ô¿s‘âÑÂF W™qg3b[k”ŠC¤ãn¾ëeÁä%¦¡ÓÞ'aóò«Ûò¼Ý‚fyyMMD†ð FáµêiŹ(ÌM›ƒëªÓõÁ2MÌc3ŒGy½õ¾ÛT•rgá½,_t8ƒ†IÓÈžhó4=\û¤˜l - †’DA¬t0å‰ ›’˜¨‹/>ÿ¤Ié>“ †zè¦(é£@—fEØÛ °M¬;bÖC†á hô@àE¹–uŽÐ³:}–=‘Ê,Ü›ú–$h«‡pª+[Ñ­¦ePŠ-m{Ý¡“±¨ZéÝaéég>¦|q ¦ÁÍFЙ j8æ¦i0ŸZ…b´ˆih QÚ°*. "zÞyç0µ€ïÌ-Èš2ߦ%Û‰P«Lš¹KÀÒÙ“…Ü›Ÿìƈï±}Ühbx«¹æúôo¡EšÁaÁ”Å‘\É€¤Â\ }‚ÀåÔ_iãåjÿW× °õ ô(mGŽÍyÐPÂV팡l1¢xÝìñ0Sh}“CÐ8¹ÆN0=9‚Õ—°!;ŒT "¯ó¬³vw !LѦŠR“Å kM;þõðàX/6Â' ~šI>7mZÔ.1!høþ¹'GTO 4ö eª]û°ªÉMŽ“¼ÌÔKiúì{qìfXc4hØ A„‰Ø©4µ±\U 1J†šªÛ—^«þæ~ŠÔ.EÕ²u*H¹,T—0ÁeƯ˜Æš]f ÔĘ×@ãÂŽYL’Ë0÷(]Ç#?E]Åcð¨vqÓN 2ži²ùx-ÕiH÷¤æq³ïIì ‘Ê *©ü./é½÷fuRkÉ’q­¯‘öØ>2› -Qªnn3É›ueÙd+Ügm€/›Ðù£üB"ÚX¶ŒTp’ hh4ÐÐa- ƒm5ɶ le¡r¦+›~Ú²±})| ©›Õ¤´EÄ(µÁ!µjÇ_—]vòßÛ÷@ƒ•E\_rIŠÌm.Âùæ›CÒ½÷¦±A=§t¹[Ó÷ÛƒÐYð4—ŠE)í¼ Còé³UÉ$¶²"ÃGV„ÍëX㤮 !È‹)#Gîwè¡¶Z˜œiF_€½¶Þ⦛®ÐAXœô%!Ësa(‰Eú5šéðí%IF'Ò²üÄŸƒw%¦!»‡ 4.^ôŒ±f°žÉáâ©§®`œ±Ø4B¢i*hnm5bæð¹OÐ`Þ”ÞÏ@ƒàs › ®Ô,ÒØä¶Å°0\Ì·àxr£àÖLV=w¿Š>l´Ñªa ïR;nÉmÏV;`•f§M­.t³ &‚‡5Ó0]EØg%†,Ü)iÈxӹ׫rCvLz¿€è™P!VŒäÊ£–Þ™]1ö*;&ž^vÅ6d,‡õÈú‰—^ƒ,ÉUÌíŽÕê%.³p³ì˜xÅóÕŒˆ!5!ÒŠ·Šç(^õ1 í¯½v3ûä“‚Þ:³©OÀÓ–ím&3g¢-‘ZÊ»½}–IF)Ø,žšÇ=V cE>rè\a7©4°-xt‘#ÛRÚòìYɱ+¦Á‰ ÷¡´i7áwBƒ¹3fÛ «Ãº&ü7ÙdµÜog Pv½–^¿Ì¹A\gÐÊZ¿—¶K# "”ç ìbXcø{˜A%¦á ¯ª‚·#Bq§8 ´(~$F•“t\G¼@sÇL 0ÂFNðƒ˜ ()N,O^÷äXöP'Ï…ì2~(FñN0¦H1ʾÌ=áš‘e; :÷ñnÿ4Á/ù »è•©†i°“ŠÆ!èÄ÷‚8S¾ò•h=äH¶½˜»€“L Qì?mÚO<˜}QÄw8F—ê:“zOÄ4 rqV~zä5fÌQ½ªÌVÞÂtŽLAƒ(ê6fF¸ ŽAHo]£±†äÖÜHê²ë÷Ìž  ¬…°_qþá¶ŸïRÓímÑÛ{þù{"w¿üåy`b BLƒiOGP.x‹¸1!9og©0ù;ޕ܂ãÿûõmñ*X DDcAÔv-ÃÇ)h'¦øûª«ág(öU* þ€`Bd^aî¹g:"ŠúSOÝÄ©ÎJ¯p9Ô4D›£·ÁÑ&l›=-Ðâ3î@–Á ‘Ya‘¸`c %4G›*ÃÞú÷¿SÎÓ÷P‚GÂÐçë’©‚9ôéx~-}¸Ò“0ÏTÎájEkY#Œû¸¹*OsØò<í¤_!>eì¯Rí¼t ÍQð"̤î‘”¡¾á”B—Ù' Ç n®¤ãã¾æzb+í8ÎYä ð!ØÂlÐ+ÖF䣰PØjã¤DpÈñr¾qƒ!®Ž!µh˜œLäάWd_Ÿ‹¨B)0ÃÃÊ2Á2Î`†'¶êhhPÐ0-·`PÓ”íì{òùôÓÿHy ,c°‰4b¿q‹c¸#¡ƒh _Š¥ BØvE§ñ2³oi%Œu(½ÐA,ã°K©Èifš¥ç†RmNôŽÒ BÚ»®rYv`Ta.!’…tD*ÛÖË?»áÁïuõ ¦%Y"YÚ]· 7RùÂÙéÙñ©5à5áßÂÎùû=ûy›>ï\ËñôŒ×Í7RhÒhpæ~˜½™Þé“ÝEüÏÎpFÍ5¯Ìÿ”‰ì.š×$Æ‹„=ÍŠì-˜†iMìè8˜áÃÊã€ìó Ïêa`åì­~¡ŠÊ¯Rù‘­ ÐÐÈ a† %s•ÉÞ(üãÚÌ€rfd^Â.…Mßï±Ç¦¡ÀZvJ¦Jò}¨Ä-{ÒC…± …F¢7J…Ò;¬g¤m¬_½åÄýîw¿ŽÉ¡?7¨H­{0é$6U¬1÷™o •jÜ{¬¸Ê BÅ­¹Ü—¢P5=Q÷p-~ªkvÙ´Öl Ð0Üäyv¿ ÒÊš•a«n¨Äô˃›2À9RæÄêmåÃj6i¦u/]ߢ^ít¼zñ§ÇÑTâŒ/]b8î=‘N*–öýÉO¾)‚5gé¶Sa–[n1ñ­ê Ñë0„”¹ÚÊ„ªG“äâª]&Fù¯˜}3bÄŽLL °0!´kè¹jŒí4±F@„’A߈¨>"ˆXª·Pp¥ýD…‹Ã•E¦hŒBa¢}©j©áÂÅcá¹X?ŠòvL~pA`y(™Íœ4|ý닸 íÏ?ÿœ#Gî-¼K%9MN;t‹äºRU™PŠí=r¤å¦9lЀrÙí‚ËD‰ë¿Qq7í+¯•b›â6d%á’ §y«Ä™èÓžÊÃâÆ¢Z!§¼Ò'-L’c]ÉxQsFIr ûJry–<жžò«cd1äDóàÀŒ[ jÕœèu/©IG1øž—+ Ñôo¶Mî†ÎÇí’ NÏ]’gÜh‰ÆŽgt­á :³LZY»r·¸!ÔÍe·K‘L«]&yG«d-Rä¶odÓÚ¡"V€CAÉ9óPSÊu¨h¢@¤c|©X¨o߯…EÁ<]ƒ¯Þ×RK}iÙeU{N‰‘ˆ‰åþH?–"$%’Y»–˜.iMâYL0B!ï×Ì´5úÁœéÍAÃÂîÅô£ì}fŒÒˆ¹ì¾‰;¤èƒt2ÅÊ’f|DªË.³NÍíXLˆ1hb8Ôk…_¯•€)T Vç4o4ô¶ˆRé'ÚFàMw;Ê6«2T-KUoØñç´.­²"Œðo°Üâ¤UŠä‚+Øb*ͳpd¾ÆCŸZÏJ´Ž¢¶R&4Þô3Àôt?£ãß.j+éý”W%‘LýñâÕ4´Ñ÷A“õÐyo¿=.ì¶·‘*iÄ,´5›©ÝÿÒK72PÂŽ âD˜MèP‚øškl¢`TŸ‘«å³JH*Ùg åã†ôÅÉ’zôðS‚L±^ ÀµdîFBuÝg?³/»™oY…Ê݄ɡEz•ƒ,ƒbíØã¥–Zˆ˜&šÙp±|iîÝ­ri&™S-=’ ÜKÞîìÜ>O hwX2 xuÔ¢,Å"¨ä˜Û¢“úW]ÎH†I{.ó=•»}Ê”´ò˜HlÓö ÃNž/&ëwbíV%o›øë+à ?—Ò’‹ œp‘°ã ›^NVvz¡…¾¨xd€¸ ƵƒðWFqXk²š]4Xüù Ò²™Ù垀t —`ÂfˆrŒ•„'*§¹#3ðˆ#~ã¢î}¹åµêÕ€wi‡aË®Î^¸ŽWE±KÑ¡®Zc€*@Cƒ‚ ’€*«èo^ÙBàUZ{ØÐ¸·è‹²®óüó÷ÞsOõÑÛ¸Mc^¼µÜy}Ò=ƒ¬˜·ÑÊ‘Õn*AŒȎϋ贒´õÎîµ´{\Ôa¶±µTi+Çßq³:Š ©Û*.½ôB~RH%@%ˆÂ•ÚÏ ‡:ƒwBí}à¿êYr’RNK.ù¥CÝZ’ ó °P[‹[ñƒJ…09äÝ2’ÈAªEb4¡öÌÄ Au9¨U²»´çu—¶&ãHÃ2;h8óLÆÜ]Ä·ºIì?Oh°_óÚæÁA:£JsôaÉ€‹D[ÂZìë»ïª–n‰©@ŸÞXeI%€ ´K~›.§;·Lý«+bðCc§pÎ2&?±®eØÑ¸ mÛ‚ül#ý™•ynœÈVEh Yd^&/’"Ñ s£CšÖ¸qVi›ùËͽmü¥ç<'úI?íþe  r鉖b†štd—»gX‚†h‰¾üòØŸýl•§žâ/P™.[±@ÃnyЀ´€€A`Ö™<*½ü²”ªN5aXí|*ÜQ’2 ˆ}•KàZ³bºŠ©{B%±'Ÿ¼Ü$D$¬¿þw€i%nTºå PB”ñdY,, Ó’ ]¦аùæ?ÌmËž!Ýüœï öÚQ¥ŽAfK-S„W¡„tŽÅU±#x‘PAïgb2\]…‚hàÎP G'†PÔdʳÏ^í'%OÌOË¿ñÓÐ0p!v¤gEHÅm¸á÷žx‚4ëö2STsœ '<‰bh€5hº†í ÚÉ:D,Iˆàns:Ê›Á‰Ö&úò¶jr‘=,Þk,Çzð1Ñ h°“‘i¬ë[K…ýßS­F Ï[X\‘uM‘…•ÝžâÝwK¥¢wç‘SÉè’ÐÔ&Lw¥‚4Yç3€mȘ›ÆÝ1 ¹cèu‚£üGʳ¡¤ü6l¦Ó$‚"[¹1#fÜܬntvž¢' PÔ)ˆ„¹žî³ b#ŠqÈ•΃D¦£Í?íMªKޱ¡‘¡ -Lr%߇5h˜F´i»@+®8Ð,µ÷˜|Hj;”%¥×/Ùy¾ž ”  Ì?q‹|ÂLXÏ=w “%—ÙNTA¹Âs© ”¢ÒBÐ bc™6˜ˆ6ØL&LÇ£^Ê*úË_"&ÐÉS¬Ç<'Z«—‡ÓÝ@'X ¤­ËÙ`E95XÓ¿qÏ_Ó›G&à›vüåO€ËnX׸ýŠÉétˆÁŽ*1¯F> y4)ÙûÜ4ÔHaÌø2¥].)~ª£6Ï<³†„yÎë:ø¬ÿûßH<ØW°QPØ·#Ûèiz¤°÷¡µ`õ ý¨B0ÐËa•U–æP3«g«˜2 ‚™VZiI)KÔJÜ1ÑB†E€iŸð÷½îKHÍÀ µõ hNÒêqš…eÉ(çL[…Ý¡ÛóLC4´Yò@L€CU¬¶®L$(ªŽòÁˆ•¼~ã±\¡ ¡Ç4À°ÝÀØ+ž¨abŽ„R\T u4€oª…k3n2jGÎdCf|Ãç==§f¦¨²8’.Û…â‚ó[±{²ý¯âáÙJzHJJOÏfÀaªs”„ˆª4 1vá…1v²Â[ˆ}޳ÎF*éUúÒ÷éœÌ_:­ÒK? ZaçpXf¬ÍktD‰i0ë(:›¶îsi‹WÏhØ£€Šc¶oˆt2£ˆ ÄìA¼f¢ó*¦Ág*9„N¦éQ³¬A˰¯@Åütm³š`<‰q3*Œ¯«Ób‹-6?¯àÙpXeð4o#³oM‡ù?Ñ%BU^Á÷XÑ=ñÇcœN&»Yë7sOÐý,!M±ýŠ—óôWD]AC¸‡Îë¯?RDkYÄSoÐw3¬Ô =x´²Å€8FG‹ígylhñVì¨)œ/ýÍ&ÝÏqLÓGûNç÷õ°K ! „Ó…À7ž øÂ& ϵ½ˆÈÍ¥³sO¤\¸@þ k†Lš°mñu€0ç´ývÐоaABˆ™xÕs}ëéøhCN0ë†à»Š áÇl>2Ë DÆŠò=ÇR)¤œ¦ þéìñwƒóÉÙÔ,@ôQŸ§´Û>ØCÕ`¾6 Ÿ‡Ž@Šš4d„å6Å®ÆE›½Í4ÔÌø2=bÔG¹¥ä/ÈëÈÖÍ@ÃÎ;oâ„&#ÞL~ˆü?ÀŸI¤ר¸¨D˜†•¥ÇÄ k·'ÌIÅ\¹á®˜†'ÑAµó)“Õ‹Ë,³ɉ±ãk¦b¢Q'}I1R3Ò]Š`®Ã 4P.”WÞœŽ á¼óöð%•D¡°Ë@&‚IŸÁ¾œé^ƒõ ©÷·×¦d©jäæ¹ï¾óàZSú@ zRÐÀ BØz„h~tißìunŽ%=tÈIÉ­¹æŠB^5‚À»u§'‡´è™±6‰‹„Þ;(TÛU ZÌh­q} ä׿r|DÒI߀‰|$#ÐR€)fê Ãê0ápþþ…ãfž˜j'bk»¬4ØÓÁ!Á²;¦-¬?¸¬ÀR¤#³í¶ë84ç¦uãËjÃd€Ì¹ù² H3ÐJ‡uÆìDrè˜!â$ƒÀ ¯5?…­°:`ckå˜Ó°0O…÷å-lGà›Á÷ÚÐ%¢Uª¡ÚMÂ\:å€W{ß'’±®Tì¹YTA†Ø÷¡ƒVÍ6 Ð0cm^£#zîrIAo¹°ƒ8Q;yĘ‘L)R—9D–Ædêà7¿ù©HFÒ’Üæ’ “ñ‘QÞÿô±F¸½óθ[oMS‹Kħ\†› Qä‘iCuàÂ7íÀ¹Šo&{]‘î`¤Q[.ý€‘œ«qœÚu$*/d¶Ý Ì}œÉUËsË-¤¨eDa­¾ºB&iä„ ÔND*°¢éD¸#ÇXõ¡[ ú¼™®hÝͬg¦e†%¾™ÅÁyßÎbF.ÑÊ‚­Xæ þ'Fÿ2ß›½¶qjƒÎØåe"˜ÅL›z#\.V”rnx~“`IóÆ¯žq(àŸþê*¨ðƼªŽî îXè²´S™ÌÝ› ÙȘŇ'"Wœîºy'ðäû\€[:ãuؤ°ÃDT,aÀd@€-ž-øÀ‹œù âxêy@»ÝÂŒ5 ÑQÍCáNÅ%ô€Ð›¬> qÎÂw]:Ñ%OA¼½;5ÆÜÁN4  5-TMI{Õ3'¥`†¹fmáÛ/@C ÁŒ/Ó4LC5¦¡ˆÔ9 FƒR+d)£ˆàbðP@³$#êcMÄ?oZ¤0§eÈCÂRp:Q™#tÓ´LÎŽ|¥Tš’(^K…?Éï02ŸÈ¥ô¤$óÛ ‡\µèÇ9Ï‚“«®˜O¤æÈv²wÊ”SœBŒS°BÚÇRr:CD;€ÒÉ ¿bp­º†Ôk>£;‰nÑìÝ;½5óþÆ#Û¯Ä÷Rnök™ðÊü¯ùïÓòðN`©cyOmï_•á;ÿoi[âRkžEþdÈJÏ_j¶ûÈðMŸåºz;ƒóƒûœ“6b~üaÇõ{kêÓ©œ}™wœg;p–5žÎÅ\ϻ֥‡2Ãܺúèä‘Gg¼ì[ãˆ)nŸfUjŸ~WìvÓ¯«äN]­BåÕŸÛáʯU€††Y´)h¨$Ê/æyµ’‰¸Lôe¿¦¡ë‡ñEO¥P6íûÔ2e‹%k3*—.N‚ÇùGk¬ 8l§¼Él¿;î<½WðAï…–×VÙTÏ4WìR¦Ë¶¦#.ê&%·Ÿ¼}sòú˜¡}O}í¦»&ÿÂ?.IÞ;¨–þfÝ{cLòf ½õÂ5·ßvÜ›/\øi¨ï®¼ÁxoŒyàÎÓ— ׳'U¿ú ïî­›“Q‡ oÐÐÇreÍÌ0—g0‚™‚ÚXq¤Â\ÿ¤ ÇxDc+VO©P÷£ÄäJD *Fý†ŽÈרžÎý²–BXñ óK^\äuQ‰¡hi¿AØH¾JN,@C#†®†6¡D¨’»&œ@­ ½8}cÌß:O¾éòÞˆÊâ1ÿ}ê²;ÇÿÁ«7ýµf(«ûuÅ}ýQ5O¹|èÁä¤Ã’sOHÎ9~èßç8Ø–uì˜}“=·KÎ>®ßÝ;÷øä|¨Â}•ÕYÇ&Gî•øë{>¯:ƒ9Èô§£“ç_héRåŽôd™4k.~»KÉ9 U2µ#}5boC¹7ÑUfUä šØ¾M´Çå¹ãa1±j¦3ã£ËÐÉiâ<‡TF€ù·S†§t¡àíT«C¬€21F±|d®çùkåÕ|›È2ÆNÇÍr² jî®Öƒo˳€)!¡ž˜ ™^ûÚ—“… Wt¬3!¥Sâ{_#œ™•qÌ#áÑÅöáxå Ê.ÇaZ^€†*¯¦Ê›üñä¤Ã+ó§%èRÑzb¿µ@…bSËœÔÝsZ£zת°K•fL øOTþˆyäL]ç85iØ÷{ï'SnO®¾&yæÙdjÒ¸ý|ìñäñ4n÷<ß©†oؼz€öî˜XÙ¢Œl s…˜éBYÅ·ò¦AŠ€E=Ê\ŽF¹C™¯I­Ò 2Äb©Pv?We,ÒÕ«~ bª·`Uù墷œÂe+TEÀ•/¼<µpñæ`òƒ—;}¥B»}R:'ùà,<½ t³H1aÈ´¸Lw AÑd@¸Ín¯Ð³Îz[(˶½€Fpqt ºká2è_ÔEŒŒáiÅ'Rí.XGqŽï`›:¹‡óõuLPÐà'YéYz¶qèÆ->ŽáPúæK~k¿Š/suAÖšò“ÜlÙÝ‘o˜8ñD'Æ·8à"r^€†FZ× «JZ¸c5|þ%ÐPÃKöûRÏ>›<ð@òç?'ýkòÖ[ý>½f'|ðArÿýÉþS³ šÞ”@ƒØ% ˜Šhkó‘Mô™šâÒjdúH½i%† üÌ»‰f÷ 5II¦BKЯ¬|ÙÛ‚uä¼Ðý¦ 5.µ,_q6êZß([¢ˆiAá¾`åæ¾hjö´Æ}¯ŒÍ‹@€)âa©[@GÑI\´>5)["’Zvæ¤Â]]d8,Î h&äõˆø#âPCðùÈIÎÌtLÃF®.3ÈéYÈå­?râ …Î¸w(Ê&àt¶ô Qî!÷¬Sý¨ÀRd¬IµÛniºu†T"hPñET“Ü6·pï½ç·PHgmùAÄ´Ç 7EGÀ…´î~ûm)¿Zx¤6%H$ˆL?ýªŠ„©X©V ú¸¼!-E ¡ÅÔh4¼ÿ~ò·¿%/¿œâ†ÿ;yøá†6—ß|3¹çžä7jôôŠËLgº@C'5¬”ˆÂYóÌ3›ŒVû>ä7‰ Aþ•7UÇÁ6ƒ>pÔ¨ý©$M/kæ¥^S˜V“K{m´ÑªJ† 4ŸÌï¾6¯KAƒªh§œ’–4Uš†’FÈ/Lcl`"p‘(êåŽÊUãH•×)H t‚«ë¥ÝÕPîLx¶r–‚½ikïXÕTÆW¯Êi’ƒní™DÝ&ÛmŽ9>ïF”-;¥‰ñÖ+®€ ‘ W¨ç˜ÎÃÝÆ­“ÅÊä“è°#![ª›Ûˆ Ay‰€ºVÅß@-†hƒ$¹…ò` ‘ Œ¹K»ñ¸SüávÐ*êW†:rÂÝoÅ‹¨ÇjÀã¶C¼tÑ4² @Ã4ã&ï½—ñ~˜B‡_¬ÑØ ì2/¼2"X‡âUß(1 ¨{v­Ü*%V˜°td)ÿ*Ur@F™ lPŸÔØÉ'ÿÁ)pÏF>Ž5Áåö9SN.‘;‡áÌùÄé.;½¢öR÷Kº­íd1 Ô¹Ôp™c6 ª½ƒïówœ¸tЯ|#1xÝu¿ƒ~§m~¡«ú/ŠÛ6ŒþXÖþŸÿ¼Ò©X:-Aw:ÌUìA1ßwŸ–ËzR–†º'ÔHðbä¹qU(¦YL‹_”ƒkí`)¼9bU=´»U_[½ôÌ@lÁ“…(­fǻҸý 0.“ð 'ŸlNÀp¤ 0u¬¡ àÉ.8NŠðh:¹TtçB?Cøj7Û‘ÜsVr«ÈÐâUŒ@1ÕÆ ‘f`¾ÓÁO>™þeÄSÉï¾[ÝQdëbRD¯úŽ@˜;×Åo?ý´lz¤G¦ÉËÈŽEÁcý1šX- Ø®6v AWÐR(>z‹¾W‹Ñokž%Ã3›;exBÅÒNüÜNŒÕÊxt*}ʰéÏ:kWÍ|#Õ‚òV¯hPå†Ë~лÐL`,ÒàGÔ=°"N áV8ô€P€#RV[WªzÏ`‹4PÌpŒ° JÚþbÀÄ Î\(mŽÚl¡^tÛEcæÎÃå]<>—U¸Ë@d ^žíÈÏb®¹¾Àêõ%p€?§ØöØcS¾‘ƒ±Èt@g<€GŽLc ðª›C!Lö˜Œ@%à œô¬m•n9,|“ \’Å`8pb¸'”šÖÉ1¦Aqk\Dbó­|LE¶.ëŽi bN™ŽWTzÜ1"ú@[ë[ÀiÔ¡]U”¢Ò@„àIÉZ“Rq¤²°e1 üÑ¡.cÜ>nFûªì…]ÁÚÕž aò_ÿz6È"ZBDŲË.v5ìPZ˜C …QrËžï’Ue^ ÐÐK®èÔ°Æ ´oð=ˆlh–—€MIÜPÇçÕGq§>ª ¨¡Ë PÀ¶±Q_$Ìôá–[Ž¥ ±cÇ]ªµnA¿ f´N$'¨sù¥¤Aeĺßï½7A©8iZ†?bõR(AT#7?ª?²Ð µÍ@À —¤`³ÅDqbøm¤ã0T»ƒ]´æt½‚Tõw'98ô<-œ'ý!Vko©aíîÛWïÁy1Òoö‹¥!óB²†PJ[l—¶º°U–Ÿ™æ Æd±9×@g ¿IÏÂy1%†ÁÜøë.¾87¢‹:Ä’ºz«Î„<±œ¾Ô ¿ÂaNä÷ñÁONŒ9 qÀ{²)ÓPÇ•V\zx@cƒ†ü³@øÿ÷¿ÍôpÄ^À <,Å«.#ÐïŠYY·n‹<§{—PÌ×M/•1b'›°{s%à*BÊb,0PVo´÷åòeã²QñgW/;¦–å×%LYLCŸ5ìòª¬ý®Â x ±²?â¶ÚÞ Ê$“DúŠçȺè£CéÄ|é…²2¯qÐò%òç£4ÔeŽ#P€†j>uìHÜPÍž^Û@Ä“R•3uB5ßKO|ë±gŸ¹ë~ûnqÀ~¿ˆïƒÜò¯9+ÕŽïƒö³Kôq#ï¾sëÕ£~þ9±Ó;lº˜Æ‰ÉÄçÿ}Õ±Gÿ®mÒ‰ñô?óØ£}$TnèÇ¥äJ?Oéç#ÓþÝgõZjÅu‡Õ4hxúé4š¡¹^"7…mÊ-^µ¿ÿ=9r÷ä<ÅéT;­Æûð䌓“öLÎÍ]âÂ#“‘Gu¿/<*9ÐùvOŽÚiw1È{ü“J¦÷¸‹l¸´ìŽ.8¢Çöër>qdÛu’½•œ~@rþÉG ýCq•£wO&ó ¯bЍî4hÙŒu“7À ¯¿^ÝÇX´Þ{ļþFòêkÕz¿özr÷Ÿ“J^«ÚUÞ|+9ûœdÃ’çžO\®÷¢Ù¶ÉZòò+U¹„GpßÉK&³Î–¬þƒdÿ“ëoLþñÄÐ_ËyÎ…(F ú#Ð< áµ×’§žªþ€Tá B1Šà†*Œk›”ΣjhRÕ×¾û&3Ïœœ+× j/±Æ’„y¤Š÷rôÑÉG>’Ì4Sòÿ—|ö³ÉØ•Uµ{*.F *@ó€™dt“¾À%ªŠWËŒ ¾óNuo"Ùl³T×.¾xZܬz/¸h‚S¥ÝI€’¥—No$¾ˆƒ®Öµª7JEËÅ#$Í”xzðÁf}d¤ÿ}÷•šõñ•õ[}Ò¿ü%á/«ö‹?nÅ»í/~QÝ ÞXnõŸÿ¬Ö=edCÄ _þruaPµn£h·á>Mz¨‰—ÊTöÜb¡¯¦¶8Òè_ÿªÅMÐâóÌÓf™%i—ÄXÍ0$zTáöj¼ Ë,ÓM6À ;ï\$$Wc¤‹6‹¨ê4hP:É_mrU[pƒJ‘E¸VU¹ÚÓ©<U¢ñË:Ϙm¶.Eû™Ï$'VûæR‡‹)úÜsC!#vÇÉ^{%+­”@@Üs&Ê0¯bŠh¦hÐ.0ï|׉>znc|ImTÎ ;SÐßxõÕ4¦Ú¡ Y¯¸ä¶Þº 4@”n ^ò}€•*¥7›ù ó¸qÉ>û$+¬lºi"•ºx#PŒ@óŒ@ó€cŠnöÂÌÑs\ K®yæ\³ö{/ø±JÚtZƒòØcÉò˧¸a¾ùÒ°˜Ú¼€•j»Ò,èÃeÊ”¦gkóPŠ«#Ð#ÐT Þ1ìTº§n¤1fpzÁD6ýª'8Û=:åó—X"ݳ­f¯È©Ô£«³þ¤H:nGW¼Š(F ±G ©@ƒì‰ÖеïyŽ‹m){môèP¬.~% Ç.»¤iy-_˜¸¡6º÷Æ'ÒD;ÒÕòA×*F ‘F ©@ƒ@ÈÖ f€ø Þ–º(¡FšÍÑ=ï¹§ž²›-ûï_ »¿ìy€Jò)jHôÊ+)nPs½Xͱ*Š^Óh*ÐÀô©Ar|mfB¤»‹à†ÚŒö`®‚¢8kÊлÃh¶3 ±Ý~mR~xî~8-3Upƒ™±Å¹ÅTsš 4°EZ)Ö:7ÛRTs~AÛBÕ¨û‹ ^—ˆŠˆ@–Úd;» BLŒY¼Š(F ñF ©@š¡ÅŠ1»£šÑ¿7ùš G¨ 9 µÑ—ÓŽ9ƒuyÅÍ)€§š9””¾ÑŒÔÕå-F †#ÐT AœT‹Ošùˆ-^ 8H ‘€ê4‹ƒ fn‚Þ÷ëê¼i8€šáLƒ"Zêš4bk„ Pô¡Æ¦ Üõ2¶ª÷´HaÜ/ò¹x5Ôp«C Âhçeò×1nàM«¥Ð ²1kVM«qžuÑ“buš 4Èþ"³jfëÔ왑‰ŒÈ:ꃚÝi]ßh[±#¥ê .©ã‹]‡‹Ö%´‰¦kÑÕbj5MXäxKFVÇÊ 2ò‹W#Œ€P£Ñˆqî’ǯóðD€û 5íŠëS¥´jz'ÅÅŠhúh*Ð`´1ù­ÊUÆÊ Å«î#  bÍŠõëfÕKàæ¯;”mQÕ¸S¼wôOKÚ ýšÅÁÅÔuš 4­ (±fõåŸë:ââØ,ˆ¡Æ–tåwV6Q 1Õ Èt~d,q‘’Y$4µò§VYŒ@ @³"£UAƒY+74‚Vh¡)Þ¿[‘[ØU¦Õi}kDDµ»îªƒþvûðJƒ BÿæVqt1­0ÍÍÖ¶3Èb2±n¨ËâzñÅ4V¿6ÕvƒLüÆñaÅj µÙœ"?\ü#â*¸óêî©ØC,Î*F ™G Ù@Þ¸å qÒPèFë%‰4ø:¡Š8&¼! ­ªGã(Ë矯Ou2¨xRsº6ûb4øÔ-ºWŒ@ G Ù@[°å½þq[ŠZîƒ\à נ—Â.—i Ò l(d# Út­==³1ÑrØ—âUŒ@1µf ráCµü‹ý„ø-¤aÍtƒ‡2äÇAµ††*9¥oFÿQnL‡ H‰…º\½fó³¸P1 3hh˜GQÖˆAPdÜPƒçƒ`@3ÔÞVØ­A 2êEaóÔ+Ø‚ËF$Šr™ÍòêÙ)F Ÿ#Ðl |çò&/6%â·0¡ªú¸QýÀYCþÓ¿_ÀÔsã„5ÄÞê¿I½jOÅlÌbo̪®”¢ñbÂ4h>| ÃÅà†ZVûn«‚m*‰·)B²GcVès*»„°©ãt%}÷݉ü£âUŒ@1Uf èzÔF3³ªöxÒÊ ä`«ïUïv­e&{½ŒãÁ ²í¥—Ó@µÎµÅ4 Câ‘·˜,*7 ÉÔi/C¯©_VA£Uk(O:[­‘ú}ç+‹l̦žêEçlš 4>Ùõµ`êõ!R¸näøË•h²JÁÐÆ/j¨ù)ê ñ±2–èÈÚï‘1ȉZœ^Œ@CŽ@‚â²y‰åAN÷.Âkø$ r¸zŸÞÔ¡ ùÛQXB˜Kã¿Äl²õë^£L7uE6fãO˜¢‡ ?MD[?%ïŒ8ÐáPH»+'¦Þ´⤆ÁǺ+ãJÓSO¥ø¦îqH‚"ѤG¹+yjÅ1ÅLcš4ˆl|b¶zŽžÀµÖ14½z·Ví–[ ”!?Dbc›eÈõPúºîé%V³€­j/‡¢ýb4M0ÖiˆÏXH®µnè׌oP†ü-ƒÎÖBS¼ê»9E~ˆô„ÕvÙ˜M1sŠN6Þ4!h 2›EVVïySŶ•¯P†»îj5=ñê«ÍÖ§€Û ž5 ˜âÖpTU¾ Š#‹ŠhBÐ ºÙóå†âÉ¥å}†ÏÞ]ƒ1êJ Hë±SUæ@#Wk({jº*¸¡A|‹"I%Ñ1EˆÃ`Wqîð& “t@Ýý£uŸ+1¸¡Ÿáƒ ÔnÉÛ]ùÅ&zAüÈüãe±•òw=hF¼’U›?F‰ÃÉGèoϯçœk eÜÇ™ãï0|¥ áOAOjÔwÛk¯9ðÀ_žpÂo½u«®Þr˱{íõó矿&IÚ¦ÛçöÛo?m·Ý6yöÙ«ú:²í7n>ï¼=ñ‹m¸áªü«ýëŠ5‡¨ý¾ûÎßyç |ðBŸuÐ*yšɸ“Z4H¹, ë2¡†‡ÇTÁ †%†è7õ]Uõ£Ø@ƒ?Øm=VÝ‹Lç;Ÿec6T¯6¼ý:«@|°Ä |úÓŸ¸æšÃ"G½Í¼óÎþÐC#ƒÎ†Ú§N>d"~ßyá…ûÌ1Ççî½÷¼€ŠÚÂañ§Iï¿?~ÿý·üìg?µÂ _ùÑVøÌg>¹Á«¾þúØØÎ‡Nœ:5Å%L~Y:½ãÆtâ-·¯ÙpX¼nvL¾'•(ïzÓò íÜ Y[ýZ“Õ>¸¨Ü`„Á&Κæ5¾û;I8ã‘üM ùP ¥¡qTVÓpKMjÐ°ä’ Î4ÓLßþöÒÿýïõdz-ÐððÃÑÙO>yÙñÇoÿÛßþô¸ã¶{æ™È(´ýã—:æØc·Ýwß-æ˜ãóˆ?vØÖ;çœÝñNüÛßÎÕÈÖ[¯õÒK׿ýö­Gõ»ßüf­gž¹òƒ&\qÅA¿þõOvØaÝË.;àwpm®…ŠØf›µÏ:k·—_¾ñ¦›Žže–Oûf×]7Þ{ïÍ}ôbðâÿ»á”SvÒȱÇn÷ÜsWWFZÔ .Ä붤q²<0¶ý:¥I@ÃK,ˆ˜m¶YŽ<òwG™2 ˜Û©ð\8©Ù—CÃ&ÉòÀ7ðý Ú$ añÅç?ðÀ­(ã…ú¢„ùçŸóñÇ/á&àV1b'Ž> Öÿ¤I7îx]½ãŽëSä—^º¿¿k­õ-VZi ï·Þºgð±}tôèC#h€¢ã•Wnºùæc.ºhßÍ6[Ý_¼o[ÛŸ\VpÌøñ'üýïç^ý|%H‹wßÿ³Ÿ­¼Ê*_}úé+¿úÕ…–YfX‚ct¯á#Z4pW•¤§¥Q8õf´U¨ÿs˜"W²ø*K5Ìuî\Q{-PPœ{‚“‰Øh/ Ü•„̆r Tc”š4,¶Ø|<õ ê|̘£†K/= In?ä­çœóó\°Šâûß_v×]7áb>Þxcì„ 'ŠK8üðßÕÞ~æ™»n»íÚÏ>;zÒ¤“–]vѵ×^e‡ÖÓà¹çîC|â?ÿü½…S<õÔÏ?µ«8×÷|ë¯ÿ](ä±Ç.k–^úË»ì²ñöÛ¯ûÇ?nôÏ^^0 Õ˜°ýih ‹×´FÓûÄÃkx‚Äúð eÈ?]ÄRk,êQÔ€ç@;ÒÚ͎٘:j¬€†|ä#¼ " ÄB"~úÓo}Èê«/Ç/ðÄ£~xä ̹Æ+ MXn¹Å¸'K.ù%Ž @AXCd&„&¬¹æJ~) u±üò‹½ðÂ5œŸÿüg4£|ô£=ûìÝ{ì –¼êªƒ¿õ­¥¸!®½ö°YfùÔ­·—>‘5ÖXáë__Ø—{îùsÇHÍ(@C½õ‘Šr?\ïN4ðõcpð ûa?l·ý¤ÒÔ…l ŸT,âù ¸º_Ã4-œÙ  AìÂw¾óµSOÝhxá…kÑâ0 Ü b9Ä+ˆ0¹“(òóÎÛƒõ¿ÔR_ZgUü*7RìÂ×¾¶0Âw¿ûõ Nˆñ’¾ßxãïs%ˆ‡Á $Ïñy¹å]o½ï,¶Øü#Fì¨ÍsÎÙCP’ÎøóŸÏ>”¶¶“] ZÞ|óf§¯¼òWlêÛÈ‘ûgG}ãgxõ–wO(_ߌÕsk)7àê[C‘ÌpÜ„2´ð3¼}à–Z¦r kÞÔmÌœj抙Æ7Ú’!Mà€ñØ~‰Ñv‡!$M¼÷Þ¸9éůÅ:¼øâuS§f)—mŽ §Üøä“£Þ}÷6‡!$A„F2mÚþÆ·hÊé/¿|Sl\ ¥sã7*7àBÄÃ$ÅP¯½–æd‚4Þ|ó?iV@CHÑl{é¥ðNñoÃ#†á=!ÒeY=·ýc&ãoK&ޝÿ{¸ä®;èLµ•'qõF©eVƒc„2°M‡Oˆ{ŸCÊü}ê© v.A7{¦¹gÜà­Å™Òb¯f Y„|Õ„œVNK5ôòÄb ù’ Ó:,ièyzvätNïQÂ!×±Þ=™¡Å_¯ZžióEŒWowdzG$7™Ü~zrûi=ß¾éýeÙ1CúïäS’£vO^èöÇ’ñZI—ô–Ñ±Ž²]»†ù ±Ôb»˜¶€SlÀ—Y'ðB÷ {+½RÐp²jÊ1°x§˜Üê!-TH¿z†ÈEg&Ï_Û³üxçÔ¤ã­÷ƽó¾üÝé”%oÿP±0Uɬ:¥Ç²2æªgŽd;÷ðäÕ×(šXE¼°Íž7›lOY¶$WܯGŽObþVF÷«3Cu°y«¢súØ[º×€a›ÿ)““~•\~`réþÅ{xÀ•%î›ÜÝÂVYTEuAÃèI•VÛi§ ¹¬»î·eýN#¶ýþû/«£ȨQû;^}1eÅî¸ãtn-ÕO'O>EÝþù·¦NHÎ=là Á@Åà†Æ´Ø,Ýâ‰ÜÞnm¸Ue˜Ö Ñ-føºSå•msŠüøCiº'±¢5v9Á4\qH¢‚áÓWÁ[MÆgGA;±3Ï\™ü{ôP68$÷Ø2xè7ºÂ+x5ç†UnLäW­@ƒ XA¦ÍvÛ­#™çSŸšYO©X‡"äi¼Lø·CŽœ]5=dôÚûDPÌϾº"$°Âï¿Þ:ë|[N,®6HÉïq’9/‡ƒ ÆJ,:ÑÖbYæ°;¯e¢ÿ*X«38DKKº¢D/5ò쵬b[—w¹'p¢ù €}nöߣïùóY¥íÖHvVû;ïŽ{ó±÷ÝA)æ` §—@ç0pO¨fS=»*uOdLC› çÒvÁ…G¹Hîïž{n&·G±3ÎØÕÆhðKE¿™¬¾²ÒF%n5;&ÛÏ©ì¬,0^¥,°Ýö2Õ ›[Æ¡òmæ[‹Ÿ³N–uµ^‘† ~Ý–„$P¬Ïê-Ñ !}ØÀÌ3\‘siÁ*†¾úêé:2q¿ð…Ïnºéj’qÕ¹çž3Ç?1‚†=öØl¡…æ>ýô]œb®Ãòz%KVALa2µÃ‘=¬&É\à0ð@CnÍ hh±à‘E(C¼Ö Y¦5xòÞQ–cƒ':">Q_Œ™æÍWšhh#ñ«ŽŽ Pám³$¯ëÅïg#¨ø ^V}ev‘ºŠÛo¿Žê¾WÏà‚ öö}HMlcqÝvÛq矿Oî;ï _»ƒÌÆ»î:C^eOÜЮ\#C‹ÌÔ€@jå}÷§°’]+ãgWô×µT\P"Ú[V¤îi-^TMè¿üåìÀõ6¸þ®K÷†hàÏ®Û 4¼úêM ÝokÅGåï®´Ò’Ê‘ÊÖUH•'ó! .8·<]uHla"˜ƒÃ®'p·ª¢ª—Ûjå ÿÏÞ}€KVUižgfþ™ùÕ1'@‚‚¢ ‚ƒ  c’…!ˆâH¤‘& ’i&çÐ Ðänº»ªnwMPÑÁ–_DQ ‚ˆt#Pÿ{ö¾uêÖ­x«ê©§ŸÛUçì³Ï©½×þö·ÖúÖa;Ð=Å=媡5U×Ä+Ó ¬p4j“€D€Ökåk¤ðI“µ 8/€ WËæ2££Ÿ]g3*÷+p °‚²L»ï¾9³@X’•Tÿé;ßÙluVóR1²ºÓCÜwß­°³–yŸPTÜ`ƒ5:h[;.å%yfI3Ù5‘YüÌg>‚µÍl“ 3gí\¨"RFó\˜=û†wÿý·vü7£}»,…!lÃüÍÀ2Ñ ‚«Oúu¬­?@™Í6[÷Ö[“BgšntPth˪ÜáíÐÐ<}§2зö[ø¯½öHƒõᇯ FbŒÙÇ>¶ªÂ'/¾8÷¾ûÎA9Ð- osâzë­¾ýöŸ{ñÅ9ÆôÆÔ(»×½îÕ|Ø;EøäÛÞöFî‰X.¥Y ÁÂÃím¹íÞ[ïz\•a$ !lpgŸF¶¬ÃoãŒ×L?›ç<­AÖqX 4ØÙ¦ÿú×—…òŒÑ"åb œ¬®´0ð-·üäu×e'¨Ì»*pAmhE•]àÆÅCXò?üë;=åDˆ2 XÎKöÛoë­¶ú”>Ÿ?…õËZ<Û* ‘é»@ûY³Z¸îº#×Zë}bÅ4E«Q?&Âlƒ >,êÍ€µ…!tIÕ ° tÛ3îã¥KgëÞEܾ~·¥{=šW³ª4À"„&X×!ƒ·½í Š¥>úèÕÜoyËë¦MÛk=¶PTíæ›”CtO(£4ÙPùÂ…§ «¯¾¢ „=sM)­fî9Æ@oh`G"ÍÛ¥¯X ÑÒØ û`©É]sÄ^è@çg9ŠÏÅê tè®dàh fm¶N«âX¢òsçžÄî )À†²x6QW]uØ6Û|ú¥—x%,ç¸ÀÚ‹>¹òÊïÛî“D$êÌúí¹ç‚ÁÁ‹¶^ȉõ×ÿ Ö(ÉÀay¾å–ðÜ}7†¹N‘†à½E<Äc ŸÌžýÃR eD'9Î fDðr8‰ŸþôÜÔ­ 5œ‡KÜwßôÈ.Ø€n#iÚðíWL‘¯ÁWª_¸.rÂF®ï›èmÐ`6Zöš´ï<÷ôâ½g)š,øõòÃWüþŽißsVÃ$φ½Ö£3ŠÓ§ÿR¯Œô(†˜ó»‹‚h$7/Yf\ËU‡Œi^€pGÝ»Êï‹u®ÈtöYIiÁíÀMbFõ»Ô¤Ó0è­(-Àö9YQ„ømºJ¹Š †$²²tb]_­ÓPÁyÄs³Vœ2´»Ãs`DÅ–ð9œüCgz ÚÒ«^`Ì I“@é?,ž¾Gñ²CÇ¿|JQ¾PótÚ/9¤xý‘ÅiSšbZ?Ê¡ó_BüÖ]ôѲ‡ÌmçYuøâÔ¨§aOG¼+*n7¡OE\¤“³1»¦Êe­Ë*ÿÅŒß'Á7‚ð­íô@ÂE4LÐ*%î ¥Ó#^®~§ÈzØo+>Œ5«Òc±«ZN Ç`üšážˆÏ»Ûù/}?”a\ãÙÊ4YÕªŸÃc%ã“Wâ´ÏP¯0,ÑûëL:ГEƒÉÍÈìöS.‡y=(½4)¨*2‹4ò“³…×6 ¹è. a–¸ë¤,ƒ½dËÆ[›™µ¼˜|CÇ7Ät€~‰q48é³Þy™†“:v Wü~šŽÍœ ¡O'Ôòz4*jÒr2hÈ‘£h–ñÛe’ ïZA4¯¼a!BÏ=wË>û|E次Œpz ³'fþn6h`ÑÄ“zwà+ª2ô LŒ÷§±,uæ:Þ©ýx÷+¹±[œ2\HBuÐh¨Ž3¨X™*cJUýÆÜñÇ òZÖ¹ Ã;ÞSúÇgŸ@&­(5†¹1‚ ™Üß‚œHˆé T¹Ä1A“ $Õ8Æã6& MËLL²O¤,K)1Ã9aÉ÷Ýwö`ÅË€¬)oëOÔn¢[t$ Ÿ¾*CÏý&²[VÐ:n°úcÙÐE àРÏDç¼FÍžX®ðÑ$ÒÑ2'å&ÄÚ$j€°‰’Ñ¿%t;uêNŽMVEN=u² „óC:C¾P8%$CV‚FO„¼ôQqCŽŒž”ÎÐTÒ‚lö¨•20ûP ¾'Ð# ;3ÙŒ×hˆ›þìàŽLÃ^dJ¿øÅµ¥e¾-H""½ž©Q€ 6ß|½=öØüÃ~§ÍmÐ=5)›â!L*‰FôÎþ÷M³¼²™æá`³- 1¸ÁÖ§y¥ÆëødèRWĸÕqwÍ> ì¨Õ¨Ù÷«}Ë0G[wÕùÄ• *’Ò!¯Ñ@CÞ>‡#« ÐÐ@†Áˆx³Ž"‡kIœwÞÖoZë­÷AY—ÀÄ´i{ÒcéCoâ4?ÿùy²7Ó„ö(±àÅ 2-bTÓ´‹v”C< üÿøÊFs ,³Ì›³ˆ›.ŸØÈ•ˆxzü$&´×·¦Nú³z4؉6) /€†—^žCíÜT!s–Á°95W”œX{í÷Kÿ­HãQœ-«nfÂÐyTi‚úé‡>ônõ²ãÜ Lò•¯|ÊÌQ±l™ÏM„„iðä“×saÜy癃-·4°Y¥Ü ”A¼X“|Ob ›Ú^ kˆSq iØÝ¥2.ÎWâ¨>w34*h`¯K?þ8µƒ;Ÿxb¦ò{¨ŠÑ@ \ áØcwÞ7Ñ­™/ô›“ÒVD!-ä§œ²{.wJ±¸ðî»Ï¦“KÈŽ:õüù§ÓWøñ§rõî½÷– hÜ¡V\qY»&F„ªK8à¸:³Þ' ¾“ò¡4Fú'u³bÝ qcä¡0»j_Ù¿Í›w2º÷Cþ¦yè¡Ëû¸aØÔ# BoRõ áñkž~æÊꪞì¼óžþ–Ò³Î!ÙVZi¹5ÖXùê«§—û›³ Ӱ‘‰<é¤ï˜04%a^%'Ö_ÿCBˆš‘T3¬ƒTûaÓÔ|3†Š¼µ 4°¹vi ÜÐe˜8ž°…íµ°†øÐä`#¨º"‘8ý•Ñiüb2ÚžR¦™j- 3Ø>©žCƺÝvŸQ‡b î” ßbÓJ«ráòËC0”J^åhæZì™>ŸœyæÞôõU«zÿû—·ÃL0³¸`âÃ~·V©eŽßV¹ŸòšyűóÓŸ&ú¤5ì…ÿ~íkÚz†¢ã.@*ª«ì¸¸ v4Ùzë Ô+îã†ápC€¾Û&•®dæRzF3¨M5a\t†,™ÒXZ­ºÁx‘ˆœÄ˜†˜:]à B­p‹?èF“m×”‘íÈG½ÆSÎUVñÜi+A‚×F­íÁ v]ØõNØxM|ñnW üM]ØØ§Ä/ƒóï®ü£].˜D¡öfc–@ƒ1H=>õ”2’q”¸'¬¾£áæ‹V´øƒèž(1 e aÆŒÃÿë¿Ö ¢û¹¥Ko?㌽L!_6H(UA6K ­»dÉí¤ÂÄ$ú„Žd¹Þs…ƒ8?eÊöjWî³ÏVÜÄÂ)œ;}ú~¢WàÅœ9'€#@ƒ‚U/Ø<æÞÞù?|%5ëX£ï¤¨z=bõúfhþŒÓ jÁ G|‰÷^3Úg]Ó€¦‹N;5Tj(ÛÌQýÇàmÄCë‘‹;ôÐí”Ó|è¡Ë††u+AÛÝö²Œ¦„´^óÇ7vÕÔšIÁ¿ÓökÃï«ÆqþDŸ:¿8EÅí Nﺫ¨½Ø7˜`³ÍÖJ¶(Ï=aÕì1QŠhƒo}ë‹8ÿr¦ÁBŽiˆk|áûßÿº‚U¡þÎ,l0ð€’(„h8‰õ³ÑâVõ¥Ú5h3¦hàk T”E{ˆ²qÔÚæDæêÕsăbÂ<È@gÇôéûë³m›!~³ªC4z4ØÃæÍH[U§añâ‹âºÎÓ6w’ó¹_üâ$[&{"§Zôë8&¤Em0‘$^ò½q –CBK˜KbËb€[ X±(•Ï=Üú—E®£âÂZÿxEa a×Àûª±)LÃÝw·yã^cW³‡ FŽŠéÍØ ÙŸh` ”•²ʘ²4p°Ú©?öØÕÈQ¡WjùV¸'ºÄËþþ÷38TľæšÃÙÉÓNÛC¡m`ˆUom2t6W±–Ü i@ÓZ×¹Ò}°oË‹m&œЀuhÔ@H^pÀ:ë|ß°Ák|ûÛ›O?=ÓÊ}ë­Çáÿq¨ï}ï»>õ©ù–)›>}_QáRÃp >w–xp©d!/ºèàOÜͦS»té,n¤…|4Û*ôíÆ,¤¦'–“wÙ›)=çÒÅ.0žÎâóµm»þú£D6p"«ƒõ¥/­­o;íôy¤Å 7Ù²æš+Ë­Ð >å¾c¢çATÞÐÐðôeßn•®iÚd\á*m ‘Ü–ÑÚÊ—ðmÙ`µò6[v-ž&áu=7ˆ‹ì:7_Íth½‡eTq'«;~T%ª•– ÀºÎ^=üð1Ù€°º;@Ò™}LqDWàeãçÞ‡v¤V:€Ñ‹Ä¬ðøã?ŽkÈR ÖZ†ÍM¬¢H ±F681ž…áðÜàúÓŸf ¢Ô%ÀBþ…žëóK/q7ÜzOŽ{Ä=£‡šQ³jT÷DÓ†]$ʇ`[@ƒ…-F“µ,,+–ìºMaË@â`j¤®£3m<ÅêK¿¡-î¶ Þµ˜ î×VfcŽ­™Í)“®)KFV5¾+ÈVÛ‰m< =,ûIEÌAµ¸Böà´lS±É'bÎ;O‰K‰ }‘†QðM€sK¨á¯ö€†á~ÑvÔ.­I!#¿ƒ <õTÃÆ^oPÖew%4ã³µKLZ´I¬X³eÙ˜cƒ†îÛUã9ð}Ä0Ö^·w@¦Aà^Ã_ ‚KX¸ƒg"ŒHÆoQgU!m ž*kË`5ûåýP†f«WÞsfo¾±xéEÅÙ½t×ñ÷uï£ìÅ1”ÝŽÌǘÙ@u»h°Bs @U¥¼úRHP6îœ(UË?ïjÔnÚd“O¨aá÷•bÊùò&Ï¢”¯p#‰f€@ŒÉHŽd@6‰L³H]9-)É54þ©O}ë`OÅ0Κu<*¢\‰¨¢+,Œ§˜²[à6oŸ¦·—]ö=uª(>QžV™S­~`Äpñ¤½ãžÄÞ7üUrOHs0ÂÊŠAs‡²$a8 ÒYwÝÕ –¥¸uÉ’Û”ŸÀ•e~ a'Å]pb¤ÓŒ`sÉøËŽ1 Z{ðÁ‹5(Ÿ'A¿lˆ=ë÷D|Â’þñ¢Ô׋CÚÊpñyÅk¦ï<³xÇ“ö]qw“ûfGúý¨xú¾ÅE‹F3 E+åËn£4ˆ¨ƒƒ@ÜIC^%Ш±á†k¨U¬Âÿï¸ãƽ Og+ G ³½»Í•½ÚQûï¿5èà‡ D°º‹a\qÅe,äJR¡%~ðƒo=þøµ˜†PÊrŒ˜¦7 P°ÚT¼ QóÑ›áýÊW>¹óÎ_ ! 4¨±‰Ã‚’^x0Sƒ!öØc 0âë_߈ù=þø]˜âaôKØÕ‘½5Ü×nú‰!0ÕgSÁ]`c¶¦‹HôÝÏ~~V)PRL™ nþøæ %°çß|ó±ïÿ X8Úì&¨0G)T§£@ƒ'ÃHMðÿú×Eï¶¼®¸¨øîÌRU àê¨wþåbî•¡º$ãêÛÀß_ž÷·gk!Üãü¥/ÍõßW’¬õ–]zWo›zð‚⬓‹‹îc”A®VÜÖ—xhìà Ž5iH”F 4ˆ©²ùùå// …­ÝJ@Ùêp(ØGíºë¦ª?àùmâäCÄ‚“Ç÷?ê> `m4» >‹5ÖX™IdÖ”èîÀ9ËrfàÈàZK1Ú»Â=Á‹ñÛߊiˆ8ñ˜¢âÃ?úÑ>èX>63—;Ù' r ªà¡€`¨G #SŒ[ODWœzêîFKw†¸Ÿ·9,¸éÐ`ÛJßi‚{ßê9<ªNƒQ‹—3yŒþŠ!(ôá‘G®Ä7dùBë 3ÇHá\¡@Þ¸5u`Í9,\üÖXWs¶,H§Ó@*;[w%$¡ ÜIí e¸âÂâo”'íØØi±Z3ìÉÖñw2GoŸˆH±\î».†R‘ • EYk¹ë¼¬6iõ »Ü¶гN4˜ø‚ ç†g5ŒÙo‹iÅN0Ä¡,¦!¦?¤ L>Ÿ?e¥•–ÝvÛO!ذàþûÏ›Åý*t`5Þ³Ï>[ùШC V]õ?  ¦Ò)›nú‰ƒÚÖÏ[ÁÃÜ3¬Cî 1 @/Ivô²®p‰ò˜ŠÈT„‹qÄ7ô$R 9óÌ}ÖYg5t6B Ó £ª\ôh0L•F±séä %)±”}guÔ‡ý¶úø4+)M JS’Rõ’ô“Ìéd"Ï9¢øìscÚÖ`g&U²Žà?B¸õ¡ é£étз[zË[^ò­ZãóüµÁ;”?zŠ$÷y¬˜zÔQß|ík_í\t¿ý¶~Íkþ/úJ~ï¿ÿû«ØÖ%#§×)¡Cäƒn°xÅñ³f Íða<¬úêÎÄÖ Œ Ê ­‘Koê…{Ä7Ȱq2j•K1 hT¬y¬ÓƆ"ŸÂ…ŠÛЋ-àÇ+,Zô#Aß!Ì'ž¸Ž VÈ \‡N8õé§o(ä9ñ !da4<÷ÜMZûë_ù²ã-gÇ% B›æ‹¸-8/\T”¥€3ƒ_Ëb'uXªšpŠ¿ü%)…+fÕ†ž;|x7£{=‡7\`ÿÜÓ‹?™^äB“ÅÐÞ÷cW§Oí,ÐÀöñ7¸'Dš¦Ù’v£Ûå. –ÿ¬i¸“…ÅpÍr߆ìÜœP5¢a[lñÉ]wý2˜ÉæT¶Þ &¿ñÆc—YæMd@õ%t|÷Ý7gX‘·çŸÿÝ­¶Ú!qÞyßå#ãxfÊwØa#›34²fgÎ<úµ¯}Y1kœÐŒ²m1ííøãäÚ…´ºfجƶ9Ð`Ìð— *lÖ(0f .’£@û0*h0 •^˜].uÝó ÜyçHy j^û£”Ž^­åPQÑjÐ2¿5¬7!Û`ãÃpE'ŇÁá›Æa”æ#ö­ÈiìëöÖú a‚óðÔOÛ½xÉ÷Š—ÒÎ÷ů;¢8mJñ/Ä4ÄgËC1®dWvYüšk“ 1Xøå¼í»ïÖVw¡é?úѾŽÿñŒ !õG D¡³­¼ÎÿüÏÿvp6¯¼ò;©ñ8 ‹ËÙ ¼úÕÿvÁ-]zû¾°6¬0mÚžZƒ!I€ÑaÜñïq‚C%ŠLóuN‚âD¡ñÆjÔ¤9毟rµ„ÑŒÙZT“5žØ?,>ž N9œ5®rY¿‘í´@ÈìSb›8)ÆäBiæC ;©Ã¸w h8úèoUK~ò†ìþùûÙµh‹‰…tÊgìí`åÚßýîåÖ\s壎ú†°vN _…úìs—m¾ùzÊÐëEH|øÃïÆ7”ck’ýšx³õ‚&*ŸvÌ­{¨§'bøb6f-až£‚CB)¹§we½êªÃ¦Oß7¢¥UcŒÙˆªaèN¤Jµ˜3GBÐP¹,±ÃwE(îàÛyî¹[‚ÊHRéÇa®%Ê].œÿºœº>Q5‘:™‡˜×‚ƒä,§Sß xƒ²N¥S-¢àÍp¢"K"·×^[Ê¡te¤Ú=÷LŸäƒ:_ruî9(yÁ‚Ó'S:~‚T£™^ j)5öuöéÅË+ÞzBñ–v¿o:®xÔ~Åç$ÛØ§”¶†Ô•N©ˆNöÕ9¡ Ý2Æ7ÕêH§wuÂXVdØŒ²‘©ø¼ÒÈ–L^öóîJW›h0`Dêà:!§!sVØr”£=idÐÎ’€DJ‰½s饇¨ñk_t ²Ùwß­H-•Ò%’\ÿõ¡¯žÍí¶Û¦ï{ß|ö³ÿ yX’hØÛð¨,9ÆdêdÆñ]PI˜fK¸®Í›–cƒ*VytŒU_Ž¥>ÌœyT¦š`2Yä шÌò œk4q6ådÝsÏY0´,ÍžrÊî9ší=ïYNìAª1Y.„£Ó=ˆ5œ·o¾ûîÄj´ý­v µP‹ 1.u7"² šJsvT(Ãh #M”fpãRÊÛ®–çêÒíO` VEÈQ|,NûNš—; ˆ)³´L2æŽ Rú¢_ Éò*+ä–?‹ËŒ849g…%­å*QYã-·BjÔ´”e1ˆÎ±*kD-d [‡v$2¬¨ƒ¨,%1çÐC·'ü¬Ay¿´ÄßsÌÎ+¬° GªŒCM}K…{4¨Ä%RM}K0Â%¾ð…Ã.YnÀBsF ’BO>yw¦#¥è[ßú’ËÉm¦x­Ãåu³ºÈÅ0‘®öhÀ¹Ñ è|Ò˜˜ñÞ¿éâŃö¨£BÒ9ï¬âñß.^tpñƒúïÉü.û^ñ¼ƒŠwZ°ª–á-"Òî|"‹µ\¥•ÇÀ@üvYÃîCJ Á2üãO½á†££+ª‚>ôСöĵ*ö…òÓIU*ˆA”î% š%rÀ{Þóë±…âZÔy»dýP‰UC)䦛~°í¶Ÿ LC¡ø¤¦–,¹]tŽõž?BœÍK/Í“ô … X´Aœ•^³Í7_Ÿpˆà\Ñ<å‡|")¯PÍ4Ðð‹/>Xí ÙIÄ­‘.óÑ“E­z 4@Í–¨É4±[iDx-?R$fÀJ]ë@wÙùÅEgŸ¼®(Ö©ÿžÄOÀRwÃƨrYãÈ·!¯øi-·ë0“ŒèPM¢”@ôi{ ª•ª“©B4\"ˆn‘}XY‘ èüòßN] P i‘ñX˜‰–B/¼p+ÐÀÄ–Uïó9 BjŒ¬HŒÛm·Íx@°{î¹¥¯Ù8 hBœmð¬³öyC±ÑFÕÜP…PÓ°e(U5D1ê›Ò€êþàH‹>>ì°©¬BE!Bs¢¡—]è¼è)Ð`î‰i˜4ÞÇv™’†\Wpƒ‚{¹\; LŒr#I•K¥›ÒòÝNÂ÷û?Òh„{"H?ÜYñ¼Ÿªð½ô®Š þhT‰¢÷õG³ ÁÚo vÄ@øK!ŽÁ†^€íâÅ}èCïæË|€6°×ˆ@È”Øf›OÿË¿ü3‘1b$rv´ ²RÁ»|éò¤/^|±›Ò…èŒqXhí+œsÎþÚ\¸9hœ Ædà ²Ü€è1¼Ù:FÝUœ¨‡b €šÓ˜ÝPÛ¿À‡Ö  B=ž¿KÏ탆‰Ï«~ u<œçwgÍj|ÝÑ::S}JÇgOô¤µj†‘p dÅàÁeJš"ÓÙû)~•’ÞרÙJT+_É5p »„øÄdã~÷Ýg /P[•P˜Å;VP°þúúêW7äqˆw*#%$Ù ÷ÝwŽ ¶¤ä P‚µßºŽº€ DAÚñËøµ„Ó&q%mpáÂÓå;XõÑ h w€*Òù\'ÜÙ\ *¹)x2p-yt /ŒKhD1XÝÓyÈ¥é«öÙÐk +Þg²¬N°{25&Äiã~:ÐÂöAC¯l¡  aq4;pTOdÎÆlLÞ ¡×¨: 6èÒ"¬¬öñO?=3ðߟüäË¿Tˆø!Ï‚jì–ä?ýifÈŠœg‹Ÿžâ—þ›Ã=ˆÄ4ˆˆ±ÉþP:Á 6÷IlðþûÏuVhÐ1·ýò—ÆKTd:h\6rkŠŸED…7~/"VÃ[SÒ’êüÍo. Ï kÀpTI4×vZ›½$;Œ)f<‘©Õ?·–'@•A(CÌg‰ì`:핸'Ñ¡Ð+•÷ß“õ ,(Î:©¸è®F@›¶éä(N‘}.ð›âª jµpaqŽdÅ45±za‹A‘Õ9ºÕU+cž!=¥ò¥½"›7œ½\Ö‡8è)o0<[-sØäälÍ ì-D}ˆ! ‡Þãâãí5Ð òîÉ'i úm÷ °8¨Î”ï±q‘®&³£^œ]9¹)5-Ãó¯ý‘|ëý%òÒæ€%/Þ¾4ñÝŽ]ó]gÆÕŸÒ(hß}½ø÷¹µ\eüÏ­¦‡“ì•îžH'RͦÜÖ|ò½Ä4PÂýÝ•‰ÚÄß_‘4¢µß†?âÛã磿;êaZx8´à°1›JpÖ#W ¾“n„|˜Þ¯?²¤'êgšJúvEÙtn‡yõ àÆ¯?¦xWï0 &ðƒN¾)Üw‰ÍaeíÁ‘ã® „Ö!ZS€’˜p©eûl%ˆLØöÂ…Ó¦"oWs;yr4›nú‰-¶XßûÛßÞD„—EZ<Ú{l.4ì.¿.=D™å…ƒI…Wk¬4Ü%—â]sÕyÅ»í¼ó¨ý´»®U£!+¦b¬U¡›ÞÓuÔ^&§ˆ™ƒ¨/?9¿déì?=}Ãß_š[˜Ú¯¼4"È_œã¿O%¡‹£4›hKßsït§pXáþŸŸÿËÅ¿°dö]wÿ(ÈT×ÒÉüï¹êêqåŒïϸæðüÀiKfDá‰'¯¿}îI²Cî;î<÷ဩ?ùÙ¹¡K~é‹·ç §>ó aéѯ’`ñEóN#K¡ðÏî?Ï'cžõàÿ»Ô{¬ÃZ\=Pœ}R/¹'DAÚìö_­ôa„2(ð3Ò+–¥¨Ho}?ãË@Cn`àTU%‡[ó–Õ—_NÊá„b6‰:.`›eq¼j[mõ)jtfäq=ö9ý,hpðíÑ ¥jzìàóÏß#´™*¹æ„ídvÑÛÿïÿþŒe^–Ú”);ºhXÑ+!ßRO£–$¬Ëk5n½õx@A.œ\5%­W_}Eéæùü)ûتrå º z˜!á-G͘7O°[YÜ™ö+ÂЈóx—–XsWÑÉL†[ÇžLKuõÕ‡Ëj[´èÌŠrca”ZÖ¸Ži2h0–ÄQM2‘i7Už=á·~öÙ‡ûíÒàÁlÈ¡¬d$@œÇ¿‹Y°þú”ùä“”%óR!6Ø` ƒ³tX2ªâ% Ï=wë;lI—¾Šáºƒ1˜2#S(Ù8‡Œ~øÊH³Uõ3v26… /õ!Ø~%œM‚ˆˆg»¨¾éÕþç*¡âå€,ͯ|åS¦IÖP ó øÌÐðú׿†dE Lvå9â})–áB*t+K›~Bf[7hHDµJ»ü¥¹#´T«¶•3¨×@ƒÕ‹{b̯šZývâGRcQrç @¿­°Rx–Uûšk¦þú×d"ã>&o&ۣϘq¸U|Å—¹å–ãˆÒÛœ~úžìzÀÿ©§®ûÌg>b™¼÷Þ³6ØÓ‚†;è µ ßf›­ë,²ziN9å¶8êúØîÓºùæ7?¯eJ½aß?¡?s啇ќÁ^ÈAO!‚µù¢‹úõ¯q˜)Á°êªË«â£ÖÎZk½ÄawfÍúa à’`‹“›bÝ`x…-Ó z8qÕ ˜ Õ#—}dÐc7ÙdH‚]³W;ôЄ„°NØ“ùÄê‚̘;÷D8‰ÓÄZR΋´Òö•òÇfXcTׄf3Ä,ÚwÒà†h€Œq ¦ôlÁ+ ¤äÙ½0±:OêJ`•Â.9g.€­ Yùt ¯`6Œ-üJL~øŽÿøû …S¡hHJU¢QLISK–Üfm6­\Ú0”œ2"1àCW4)Œ«í·ÿœéi$«ánTo¹åú{ì±…ŠTË¢jCÚzœ¯…óÎ;€„oÄxà6»îº)¬ƒiÈ‚Ö fœÂ @y¬i ÃÊêZeÚÏ‘Ív×Ù)sØa;pÌ1! › ¦¹i¯ðH’–"Aáº#í÷¾÷½ëxqŠ9’Y,R|†W]uX5ÝØdÞk ï\ô~GêOÈuÃÉ<µkòû€†¶»J œghhÓ~îsÅ„ýDäóböÝwkSýÙgo6ÿY"›sOs;Ò® !q@è›g¢ó ; ¬ƒnxn>ôx1 XDÏ;ﻞ!ShÂ[šŒª¦LoÅ4X“È õACËÀ†­•¢áãÔ’ãd´7¸¡Ó,`Þ.'Äúe¥lòûï¿u dç¿ðÂl«8âáò˧|à+°‰jçX¡)Æ" i°G®Ù;[´nÀF Vøã¯añö–[ô¾5• b€ˆæ¢%ØŽ ‚›òÙ{ï­´ƒeÕxPÏ-ëXù¢ž¸'vÚéó¯¼Â›»àé§o´ç»é¦cךuO0š‰#ÆZ¾Þz \B¯`ÉGVÄ+TÇ4°€%oñŸ‹JƒÌßE,H‚°À¼±Ûw#Ü|Õ4 O–(n €h!h0éä{UìüúõcÚ‡Óbi/<úè•£„5©>Há è¬|¢vŒOÃøè£¿éH¥ð&Tƒý’Ó•ˆ ÁúgX $DXƒCÎ>[íÏ~v­Ö Btí¿U|p¢UÓûóŸoD†¡éR‹BRµÌa‘p:P­S¦`¡ýÒ—Ö~0‰²œ¯r¦¿5[4ÅÁ 9gBºŠM 1àDž¯Ú=¡á;¦-:!LÂ2ÜvåS¦2¦h°p À9=Ä4:h¨:: }rÐAÛ"c0(¡®§ÆN£}…:à>t§Šxq†V+]6Cô Ó J¿Æ4 9 †2Ô!q#Ú¼½\ny $G©ÝLE´”}3s†iWþÎvÓÀ]*TŠó•é´²Å7{Q67¶GxW»m–a Àä_c•5Âj³j0©öC<¯â¶¾­·Þ0‚¶ñk[f#™†|dÑÖ~;¹lg§c2…$Ð h`%U r$èC̶M@C©4¬oUúa©ƒß4¡R~õ«KÂŒoéÆsö4 Œi–k…гnÇ}ɹ°r(" ÙÀ@xT–NkhgfNÇ<%P áÜ£ Á¦huÜ9ÙÅõÍô¡˜+âük®9BíÇR2Å hÀfaª,ؘ#äˆ#¾aØ› fˆ` ¡%Ðþ–[;uÇ †¥ÖNGG-·Ü›Í^B1*?òÈ ÷¾û¦[tcìí/~qñlrA¨¤ùJ r—6üLS20 W­»îê!B¢^„ex(žWàÚ,Ó`æ:&²æÈ:ë¬f®9= ÜEˆ·˜Ã˜È¥Š—ÉÒÁŽ×xy´o¾ ¢>Ýø†€J¥o@„?‚'Š2yMœ%Kfsb:À=*,.8Ã3ô|°’˜’&£„gÌ’Ï?_ßLéŸ5¾'ÀËP·ú¨p´Ë¶Ž]{"É/°Ÿæ¿·¶{fV,¥6@(M®V !ò+E`¼l¸ya×[oun^Ul¤žus0¶@(M{ÆÅŽ ¹j±äJ`¡4ß<^‚eáÅäø`S¶Ûî3,W†hM¢¨|50­Ø›b€ ΑOzMŠyµ¿‡9lqx”ï»ïlÖ–-Ž×oÛ™¸!4Ž ÈØÓ<a†Îà§<²DûîË%ÜC†tÁ0Ëát#ÓÐÈ¢Aq'îT·‘Éð“p‡žcÌHÚ¾eXµ’ñÍ®¶]™C§——‹L"`lÓyë1d>¡»¼Q6ÐÆ  ) Ðô16„XtfÜ4$Ë­*SoyËëÃHà"„kqF”ñc|TðºrØüý1¦oôš;¶AhxCø|NÀÓ0ƒÅ€«0>³1 v󀋜ˆ”#ážXi¥åx ¾þõôîW~"âlŽ4Ìeãk¢on$*I˜'´Qî1Ì™¿1#©Ô>аCd¼Í—e–y#DåÆmÀt¸Áìá‹_°LÄ| VèMç õ Ýrˆ–h1E×kLƒ‰öØc“Sn¥­6d˜‹Ë4“BY·\8ÚÜ06hÈsFZbñ6îì`ðžMš'Ðbâª3aQgqX:ÿ¢ýãû™g’ü4q ößhq|b‡!vÒ‡>Û…–´Ÿ«xžó˜‘ò·@0ç!P@ôbE>ä\ç–o›æq‘è«øJk1½“uó_û$0÷¾Õ=q”iò§€)W_-ÚkènÊaùÀSl?Ö ûaó7ÚêÅ¿£QÓ¸ZÃ6”þpНž{cÑb{W}¹Öº'â<1)8I»Zš¶²`U?˜Ë#È—7Á‡Cýú|UQñ þŽùVÙ N3ÂKA?>¼ñúëŒ!·&…}<Äl{íó8¢ÄêÂÜ8€Qg”¢4xú s_E>{‡Ã0%7=Á:€&ê]…Là¡ì $f"SÚ;'ú×I]TcàÿóúÆáhZ¶çÌ9Á·º¤ÁŽy‹DUÅØÈú>y@LöŠ89Mí8°uò ?‚,pL)æ:÷Äסåô&¦héƒGáê¢FL¨~ dó—> ADRÿÕÔ' ”Anú¸BªûüÖ¿Æ 9¦CÑî(z+£ƒ4Œ &#~ž¾£ÙöàÁl…ìšj•t®¬ UënÖ;;dG¸P¶eáÜ167—T48zûé·Õ÷o¶Ì^÷(h0žcàŽúy]úµÊeØÖrÞzœý1"*,œƒÚ ¥Èžtø¥Â£Ùñ“UT‹²Ù ÜT¨ =,Fé¦ 9Åpvˆê¹ÓúÙÙQ­¬›*~Èl™o‡ÎåL”T5Îã¥+F~Ùt+°¨¸Óx_±‘Ô2dï´ÅÈ»™jð}ÐÐTS…€µ‘jˆA$…®h}¢ÚØ !™¨6îö¡ p…äQ3¦qÎŽ§<ݱW´ãè‡ò؈æ]«½-·ƒiˆ³/ª™u©ÈôØ !ùYñíQN7„ÌV¾e¿xÎ.?D ‹tÜ Ä ¿[Ð8iÅåÚ¸{4p–wŽ\qSïv5îñŠöjÔ 3ÄÜâ€óÚ@C°ÙýPƒÍÐp,BËìѰTA³o°õí·4˜ dR;°Äk-3·6ÐPÚܧ»ÿÖÿ¾-›/YÞ¢õ·ÙÊ+ö hË×Mp-S«áMc‰}mà‹W‹q^­ ¡Ž¹:^1Š; šð³?Õþ‘l#Ã~[ÇUÆìFçÐVÐ`ŽHoNrgVÝMÕ :ç‡î÷¤QO At/¢ÿjÆÀ¸.ZT¤ÝØ—À1UZÜÐÐ .A’ØâÅWUpHâ%«?Ä4W¬oIÂÄ”±p<Bíµ8S~ørA^²¶ø›ƒ˜nRCR™F¼¥{øÖ[¸FZØBÇDŒ‡*V2:ÕN»Aƒ™q¶ÒV]ôj$hH9­ r«’ ¾F»8ÊLbŘC7Rá cžUq|¼‹lFö¾Rd?áÁ=Z³NZO5Ù6ð.1!Ý©AƒhñâVÓÝ]dêîjÌwh Ü p¬eÁ å ÁT!90ÀYr2;ÃË‚¹¤_RkÔ­‡¾&N=u÷ D-Î` &*IBôÙ0É ²º ÈP +T‡eJ“r—ü໥Vz˱îBâ±ýWH¹Š\úFf?~+MVˆÞYi]AŽ·ÂŽËvìÁ̸'žh³0ÉxgýØÙ5þâ995b×iÓöŒyË!Zè6©¼Õö±[3YdOM‰Á 5¹… Ó0Ú2é0ó‹¾ÈÃ;/µAZGa 2ždF¤èß4—·DÙkš;ŒŒt ÙÚ{ï½å~ûme††“<]™ÒnŠ&„dlßúWðuI0>Gþ<ÑzøÇeÕ÷L‚ÅtÈŠÕ+á)AÚ²AÃK/%b®-ö‘wNvãñ<‚𧬠¸rCk~¸2Ð7{3Òƒ’™#sÄäپǜI;¹éb}½’0I™Z1ÅË/½Ûz/ßÒÎÀYvüñDúÄ‚$~’‡)–Ê'¡~æ\ééì¾A®yL$#N'qœy’ý(Ђd³ê¢ÛxÉlÙ2<Ô¬%©;˜(5ÛDîF›lœ’ZÖH)H=g=uÞ¿d°%‹¾!ïùè“4.²3@ƒù®8…¸ÈÆúõšgFÊu ò–¢¸c®ˆü+h͉Ú¥&ÃuÝuW#’†‡ó•¡›¬*<¦Ës  ¬Ð(ßþö7.\(òQÕÛ‰…dôRãå²L@ò‰Ü»í¶Y—ÇÖ±»´a²"yÄÓ³=v ]}“{!óJe!•Na"(IÜp$”Ö¯XÎËo³•ΊÙ}æ™ûH9!èb›AíŠ éßÚBÀôÔf­ßd$¨5„RXùÞŠYxhåh ‚wI·=“ SQ­!›}2é߃„õ ©¤\êMЀënÍÚÓ¼IÛi-?óLÂ4[j“Ñšìö!éÄ‚éïUÌ4: J.Y\M'V½OàÖ´´°½  ã+e)â.Az:›(ESD·2wÄm`:-Ò îÒº‡å vÇ)i, A#6DfB;9׊)ï°…k±Û´fJgLFb¿hQgDï ªRƒ‰U…dœ;z€9ÈãDÐ /Î'Á2Ü{o"úuœØ>ýÑÿ7L]åÈèÐ`F‹µ‹íj:ÿU¦¹à²Ë¦„:,*©fWÁd‡JÄš8áðŧh!$ÁNAUªX°*&d’4Í ¾0Àºõhûu À µ(om¢Ñu0PÁ˜@µÌ€¤§™V&½QSìé§o°r§ Æ¢oáur?ûR!Ó9 ²dõµH@3æ-ÀDi¹RAp¹`ÆÎ$Œõ«¨Q鹋šïààXœ+šô‚9KÆ*TôNžƒFœŽ þ¤„Ð,Ȱ1 µš|h`£qÞy(ÀEªÃ ”th`g|ët/<ç{³2(cr2Î&¤E*K³/`³z‘Æ @qË%èd‡mIÎÃÇÙд–ãí¦`ƒaR¢mz4X{ºbfv¾íˆ=„*4<”¡úö£*N ‚J«ÌC([]™X¹QæXºÛ`/V[mÚ2èŠ~l!Õ6–E¦ež óGÐf^õdãÌ|zŽÚä „pÿýç‚&6ÓiWAßF0=;ë½-‹ÆÁ+ŒšÿÒÜ%‡Q0{Ãú= Ód6á™3“ö(Ù¥œÍ_P¹IsA L!i^¦™B¥Aî~£PbË$É^—‚ùK|Œi¨ÈWËÚ uN\,¥È®pž‚†¼´jø–fò¬ôëç¬ë–mP˜ð¹yaÁ¶¾R #‘Nä+@#J¶ÚÅ੃)&ÑI³Tg«\ªaKíCMY&!iײÌûãÝï^2ŽK&Ç5F¼Ìm¹Åá•@Ãa†ê†Y©)‹½…Ðð¾¡öå—»Ij69‘4SªY€`ˆ7pŠ$¤ZæíõÃÜI<>±*›wæ©›rG\{¡ðlÂ(pBu!g‘R)²úªºg’å#ÓàîN;mOÔ£3=aRР‡%Q¸øâC¿ m@±C R&ƒÖäit#²µ²ª@C¯ãÉ(ë‡ÅùäE#C@Ó3¤o{³×^Qâ6!TнU°Êl63ùÝ[æ¯Ó~tÏi1”¡e )Q¹á…šû€J Áij±0˜f+ÃÁæIÅ•å¤ËÅ^@ôóyäjn¸áš”níK|"Ýû¼Š·ÌK¡VµÉoÓà0‹-ã°û ž{.‘€dõ˜fîôÓ÷Rkê§?M Uë€ 6m Óàà;î€c(¥“åüd–odJ ì,3dK§N7dp°òvƼ*½ a`Sb@;‚ÿì*40¶<ÜN'1 Ñ:™M_œ¢TåÒ‰ëR“ J>ûìýK±G»õ5Ê)béMƒJQ KµðL;*KH/!Öž°Ù…˜}~çg«±Ê%`óÇU€×À‡A €Ô@08nÕ4†M ]¬Ü&‘b˜ùü©@@PtÎùæ¶úþå/Ê_ý'ÔnÒŲRJÔn¶ÙzÔ¦9æ… {„4së­œ†óakh P ìop¦Ï‚ì’u®•*R PN_ðÀAœ’å2ASÀ8*2CC!ûüH áŠ+#Nã Ä=åaÑ Ú=‘@ 53ygüXžáÁoërìM*œê‰ß~ôÑï¾û~Ùàd S¦ç@ƒ™IÜ Þ5ä @`-.Až«ÙÊ ƒî‰è‹]°Ç["`†™ Ÿ€Z÷û˜?`iêÑ›º –õ>¼Ò-ðB""H·ÛÝ+AÖv¿Ê2¬Ì+X€eµö?õ;;àoÇ(h?Ál‰3@9 €ˆ`êŠi`OU÷)ï0çC‘\Ž!™é÷„ºØ/¾è“ùK–ÌÑŽrš.š k<ñi¦¡ %›U’õöcIÇ?Uã²ÝÞÃ: 4˜’<§‚®z¨!³³Y”ÜV)‹†@%ˆòÐܘtÏ 'œ°[¬r¹òÊï0YÐÙÊmŸ­r ‚HêdÀÅ³ÚøM·B›Vf#æfHa9‡Èù>@l%ÂÆa„j´IÕ(%.U…@ìY‰±!’7áÌTã4¡´J© ð}Àp°¢o>×OÿÒrFàñú…`¦ù&#´\ …ûî;Ǻ²§ A#ÀÐ@þhÈÄ4$ÈŒ™ç«¶ÅáR^·%‘Ì‚èžË3D|Ÿp98Δi`Òp‹úöÛÇ2ßÏJñŽòL«‘bÀ :=À…² ß@6¨=èjG  ÊƒUÁ£ƒG ÕÉc$Do‚†ßü& ;ê¿&þ¸$¨Ó4;”¡ºŸ”šZ–¢¼Ê¥}GCù’™·5AòÛ Ø…ó)؇ÐÅ+źÒ'ôÆNÁkNS^[/‚–ŒàïÀ²SØ? žµE9àT±»ö=¬¡ÙkŸä\¹!”a¾˜&8r³N î‰ë­ë %ËèŒ]–œÔ fçQ æ¯2 td)s`7æ¾ìÒì92¤QØÆ±ã¾å:e²C}¬Á(3>†¾ïž˜øŒ©©ÎS°¸©#¼¦~Œ|PY dÁb/'¨"èGtž)`1“›Ñbø¼°nJO€ä;lØ«L$+U¹xè¡Ë ¿7½éµ!#`§½vû~Sa.zÔà.4\rL§›¡îH >€†õàéW½ê_ƒ›¿à,@„«Þ8×%{h`.Ñஜ=Р· Õn•.Oûh€òÍ>4š‡o÷4Ý,Õ†$d,ëX¬<ér[n¹¾®¦î SžWÅ¥!V"ëŒPõ’­ä™†þõx½g…pQèh€{bLpöä“I©›…âJq Heæ{šðÈ#Wf¹u^(42F›±Žç°ÖZïõXüí Á³€±–šbyXªh@Õô2h0å8õ_|Q·¡ ­n¨Ôi¨VdÊ‹iˆt®»n’“0KrŒlÞ¨<´}†å9Ô²JV_†Õ:-Kj¶Ÿ½Böób!ÿÁ¾c'uÕUtýP EJEó(FŸ‘ „4ñqÑ¿ ûÆ7¾àí@V^ùâF=UÝfmJ%oòŠ^†¨£´ýFð •ŠÖ&LN¦Q\ûîEb惵ÑÓÜÞˆÁ³bÖÐóô˜ˆ°uéeÐ 9°ÆaÀ†;”ó•W¢ÅBÙŽ¨†²êjÆklq§Ä‰øÍoª^-12ÍÑJÆ4ŒYa;BÝÈAÏeæÛÄ eOtLé[$R*ñ¬¬>Dø;‡`ƒbŠvPtIJk–Ë!ªß‡ÕÚQp¢t|e¯†ýVË6[b¸²U“(¸¡óÜéx~þù„ÆkAØo3hlq§$’c¶·vÃê;ÏöÛ}ÿІ%_°+õ¯qðá'ìÅcZ#æÀ)Ò BŠr2ÈmÙƒËožEQYùè°±†âÜfJ,'«@¤š’%Õ2•»o±¨“tÒ"Њ$`¶±AÉœ¬­ë¡ ÕIƒqJ˜Ô±Ùë# é5b9ÆÄ©ç_X\*‡¬ªI‘€~kv…8›ç Þz"É9LÕ$q#Þ¬O|¥¡çI^wì†ÏÙ%듾y†©¯$ödzr:Hä x|+\ŸˆÆ@ &}8‹ã9Ä%ÜŸi¼\oº' †>h¨ÃRdOQ£eª #u•rƒ<ÏfdÏŽ ò¼ \ªÕ¬æ/¨9ôCHkE.£Fx*&¶5ÿÖZßÐÁ Á°öKb•Ôt§½Æ óP_°¦ý+?v=$V ®µU7QÈflfÇ[DÞeùœå­ .Ÿ>΍ÃaÛÌúþcÓà… ~;øÌ\+û6.Ø¥ãÓF†íg5U™ž[1æcƒJþ[ž‹*IÙʘ†L‡ÓKÄ«WßWúIú“ q=I±µ_èr"f(îò;!Ee,*  HÎÍmbH&¹à²–íûÝ&WhÍ"µ­_õ'xÅÎ )n˜`•ù†O@ƒ 7ج‹x@tgí`êr'Ãeüª …Úk½É4HØ{à†Ï£^ihý}[Bª±1yòÉ?ü@C'Û‘áú–Ý¥Mp¡§ÇÒñ ÁÈVcŸ¢£ŠSÔJñ‘dλb »_ï´ž·²“½ l‘QëýWO@(ƒÍ}CªûŒÂeRÜÐHÐÌg»lÙ›ŠÿÖˆ?´P¦Pù²nÚÕ·£(Ð5œ2,Ù›„¶ÕÞGº"‚׿¥(ŠH“ Þ65Êúûß“PÐÔý¡‡YGìH½­ïF†Ö%l‚ƒO½@ƒ1Ï*.²õiJ#™ˆZACº¸Ž²ª¾àv‡B…jÏTüH…1A¢\Rz6–¨”ì·ÞÉÜû ¡Ž…³—OÊ€¤i^‰úž-冯Jƒ=1¦e<€˜’0(qR"™Óàp¹æ²*ª´âGi3'I"µP¯qÒ¼I•qWWuB0vZh,S›ýD„']¿-ö1û£¶à†œè0™ÂËÅ– Ö!æŠnYÎHh„ÐõáË}+O•ó[ò} û’Q"n¬«ä Ê2­½Ègé~Éaévòþ«ïÌ:±¸è®úÆ]KÏ"ˆ7tˆ”í0 a”Qu•ËB=¶aŽyà ŒÌa¿‚íJ‘Œ•Žƒ!)‰7¬ýíx¡H4ÅTÊi4 o “%=’1›J¢¥Zº²6fÍ:>:„Ê,Õˆ9"±ÂW\-î2õë <ÌDuŒy¡N> 7AÀn»ÜŒº–Z‘–_Œcµ•¥&ÇuDôÜP®Ó éQ8ñpv-ÑmR÷1 ‡DÇ_ž–j%—Z˜ô[Þòz¹RÁ¹;`ÌH'%¹°L‘p©)ÌÚD)—JPªX¾š êNââr·Ñ¼}â-|’¡ *#¿K$V…I6CX‡°âåP¤.\7OO[º*»)²˜7ÝôƒP,8É(«0Öå<)s%É[.(#+ßý ùf©e´ßx£>ó%¿ŽãdgÈmûèGW`Â= ¶Ï¦¢ÅÀïòö dn¿ô¥µ«b;ü"³ƒ°îÐÐo¯›J~Yp;ïü<4Ø%LCœ ¯QïÃU<Þñ[½ÔE>`€¶¼ÁÒ`‹{ñÁä é|¡àjtaÄüàH&˜ª¿*Ðc ÊTÅH µ\êLÚfÙ÷›ß\±öÚ .eˆt]teëë®;*è)U‡úV° yc˜œ+°kjÈ#õ¯$©ÔÄKz”è1ø\’§¿…(ÆHC)Ðt™²• jÇ:vdo‚³ÁÞ-寵v6ïàÊÀ½Ú™/e)8)•S¦9ŸŠê¥—N! ™])­Ê,ÙÉÓgµO\ÎÉÑØ4“©'[KÌN¢9½Eê46%’Ñi²’”w˜%œ®¬3«ÿËnZ&c $„œu{,šµ”é|Ë+Agɦ_:u9hÈýâi "'=ätå¥Q‘‚QçFšµ«` äŒY§%¾ëgÎMêèH‘WU‹`ƒm=»iA•àR¶ ’»h?Ø6i–èo¨½™q+±fID°}Ø‚ °?´0ˬ2Ú5y­±ªO=e-Yøûß_-­\Lœ™òÜ혯jWœHÄï ª5±{VÔH”öKé$Œùlý­ÖÉ‚wqóÍÇ:WR»Ÿ&¦ç‘¶H @í¸)›ËPIäA ¬(ÝÂ4˜w>J‘¨¾¶ã†2Ðh-í¯ŠØj%7‰‚ô¶B{ûAý(†¥ô‰ÑÛõÝïnc¼žÑh¼v!_’@ˆÊH²}N*--@6ÀÀ΂éZ&‹RÊÌLr5)2Q?“@(·Ð…¨&“p08-ü>z³LXõ™HV §pÈÎqy!·9Cy¾5ee†ãMIS[ž§ÙmNÑ–6æ ¹†;ír0Jh!/5ñæQŒE¡u2ÇûÖ› Áô3÷øÂû¯ÚŸnAÚÉ/yíòÓü;ñW©öÜN¶Ìž{ni'ÕL=—ä€b5´W\q¶é·¿½’е™ "ÍßµFZ,}¢ÖD NA`ªV4È´¶æÅ‚–§ò XŒí€‚°ÿ~Ýë^m3$¯ÖŠ·4äPýäâíÑQ£0À bþÌM%†Ueµ­µÃóØ÷[¤1šb^éÕÞsi¶;ÊIùVßèÛØ?I(uã+Ñh‚¤‡ƒ`Ê!¡LxDRÝÛýf¦-)%Ý”á<„Ý„Š² !”ì‹È `™÷ÐbõsÎ9 |5d¬­14z3õ~"Ó y=g£=1Ï™.hHÉ_·[å$òTIxh„5A·RÞyW\0)¢¶×EX ¶Î~t~«ÿùŸM,™YcÃÃ81tè3€†@l0çþ £qàè'3à)/í²Ë&˜9ðQ›Ð3e\”ºn€ ÏýëçM°`W†”iŠ4Dƒ<å¼§žJÔ Àz85ÅA§®²Ê»¬å@¶qf ?(L,Y—œN5“¸˜‹•œ*À1QKSÀ`£éîÆR5hp…7C:Ô¿H˜ÄI¨tÕí‘ = T…éØMóÄ×¼†·‹ö¶×NÕrS”ð ÷û–@KdE·Î±q6¬óçgŒ‹\Þ†~Ÿ}ìŒ ™†°:â=ïyˆÀ*Y™L*«wíµG"lPìt-áæ³'!g/ÅëIâ-2´–ɵ×~¿Ý!}®%y\Ô%Ì¥=–2LC²F®¸â²tã«“LSËør¦ê¶…ß’¯ªÚØg³_¶VsçÚè 8øØcמÿÒKóvØlí.@ƒ¥=ÈÏ% 8`ýÙešqiõ 4™>¶ô¼LÿË}´‘Zæ¢~¦MÛ9¦,\xzpO 1 YÐuv[`”•ÉÈ@„ä—·_  $½W_}% ø¢)pÊ’.Øúž ÕïwÙeSêúCÕwºˆiˆSn 2Ý^_*X…u7*¬£«¬òNÎ/[ªQŒ‚n4S‹7"ÊøGóø-„æXŒ}‹ElôM|> €‡àÉ \TÁr®Apœˆ¡Ö,Ï Dvª4$:V­Ô²q9&óÈÄAƒ„•0)ŒmòÄÕ‚0B(I ëŒs,b6À‚ºTyàpÂä­´Òrö ‰‘,8_È=1hHª\R¶†“¢ÛÅMeý}æt¨áèYÐÀÞ¯YUË2ìÚ2vðÍ.,YcgÆ< ‡ôë_yÔ”b¢£U6²å·zž¡ nÚù/¼0›-³Î©qgGEÛÕ¢Å& A·u4°/¡›*–Á±¶j稥ÉÇÁ ÁN±ï%—|Ï‹ïÖ'Œ Ðð¿ÿË)  Tâ'i£Ø9—Â4[‰°öÜuu›iÐ0e§>o×…oˆ•®x.”Æí `P"hÀ4\uаð¶Û~èÄØrÐ0K—fÌPöbß°8‰ò8ŠhöÄ=(<ù$q=n¤Ê0Ä]T¹'’M˜[=Ø(ƨÇÁ¸—ÛëŠöÐ`ŸG‘³$M3 ~·‚Jxþ©PÁ0ˆÆ ã^ ¡°Ï°‘ e5Jš¸]Œ]Î8¾B¹]¯Ril\1AAW.€(\©#Ë)©Ž + Èú]ŒØÎïeCÌ„Ì!f+1¹Uó.‚ÏYHg¯ W,¹'ò®+Ô@D­ˆIkÍ'04¤"ÒÖ€UW€†³+Ä€–C´DŒºPŽn>̨LF꫌i€ÚÑ 9D—P„Âô©bÈÆ'jN&»*”!¬!ä&,ì2BðGíkvÛìYÐ`i郆Z,N exê©ZŽíˆc¢rÃÊ!óÑQZîŒ, W™®S5Ÿ¢ò¼-ÿ®S¶³óØMK8÷D`”­*ØK-¿üÛ—³bnèÀ[ílÁ­dvÞQUwµÕV°Ú1©HuÿêW—âªbf aKÞUz‚ž}›sô/W‚cóÐC¤[¡–6¶cBÀ×[ºýÚá|uL(É“gmíáô¡b¿åxw-Ò0^ º'Ü ‡•øøãwÙtÓO¤º³ì£ñˆ²¥.8˜uÏòïBžs NÅ@ÈÓÀ #c|k±±6¬¹æ*%oAŸ³Õµ/8ÃãÍø°“}›ÈPu¹(Ûx ˜O4"¬Ä“±À@³gÿÐñrs(I§)3ˆ¶é.‡É°³ö³ªÐW®@M±Ú1‚iF“8ÑN»$X±¼b[uCç}h9×sö4­¾Ã#c›n9÷oZ…+:kd²ÙÞAi̇ݘ~"Š™Ñ6Z óCvËXhË m=•æv ×ç™3*¹Brº|Éé†/©¾cŠÄe)© `-AkÁ¿!òt–_ÄÞNkÂë\Ëz8aÏg’‹Ï¡T0Duu#Óg„q~Ï=íôeIÀ£e»œoO>4Œ(®‡Py'ã“Ó J6 ƒ@Ü?묳šQ- Âx«òÀ”)Ûûe¡p Ü¢Ã. Ëùxnë}`,>™8ùäïlŽq¤êZvö®… p U ðL±ö•±mT™,ü$¼JÅ9m \ÈÆ4Háp !PØUR¦Á-üã«B¾5MV·VJJ*`¿@ùñ¦‰vž£gAšä@ÿ5úÀòž¶=T»ŽŸIy@Ê u7Œ ,“gÚ» X"ëþÀJ&mÒênwkñ™ð¬¬`Sþç?ÏLc¬|ËДþ›ÎJ†Å{p£ÆúÄÒ¿±Ò®ƒµŒ ï«¡è:±Ûœ…$‚ä\&Ïa,¸r—ùŸ·Ù¢éÛ}÷MgæÔÕ ¡Ý·#c5"?3žÈ´Yb5®©XzLJödÚVÏ¿z%ÝYþ­°&ÍîÒº¡AH‰ñBîÂé1XÁ­yP ê„9ûQ,4r%2æáè^ÖàjSz ×ÄLWñ¡ëÚÝÆÓ!‰àºÁá^æ:2³ÛëfÐ`^ˆæ†ZOšVê4TÐøq‡šRÂW­0PVŸ@ºbnÎñ@¾rÚÌñ¥‰œôáA"ŒpðbÄœ ?ôÐ¥:¯\|ûœ£Ê…ð…±•hEw@ozBäúsÛmIAKDÀJãA("P°ƒKE#wØ:j̦n‡.½ôWD¬«X·8ÆÄÍØ$˜×þð-‹fÐóG¼Mè$l-ª;;“Q©W= H,^\ÇbÔC§ÄÝÊPýÃ[A“Ô÷4äИ6¯¡TîÚ6.™¨½j:˜w^±Ë£ì nù·ƒV8|ž¾³fgxÚ´‘äûx{»"1¸<wÑŠ®VÜ˰72dR«vEÕ½­¾µÊKXBìXµÍª;϶Ÿ½ÙŠ#+HöÛ. †·£ òjuÌ‘Z!=ê(ÉøøK;Š1TÇ ÄX×lÞf<=þ|Õg¥²Œé)à 5¦'Æ>$ך:h^°Rßb«—ó‘>¯"dB(âç2½î ÙÞöAC¦Nie§ÞÕ4Ѓr¨ã56hHf‘½2çk©boõ’ÖivÁ>éFRzGx=(;?͹ךçÙý ÁðV>[|C+ ÈÕ :m \a¼„ª*C6«Ãbžäu?ÍQ¢]ÊP};ò>(ïŽ7¸¡VÐ0 3_ËW£L׺÷Ùƒl‰Ô­pÔb F÷)ŒômÝ®¥KÍ;f²€#ÊÇâ[ðš  ¡yƒjr·Ü³ ÁÔ¢Ž2Þµ¤³±í—ˆ¡ ­¤:›}ËjRˆo×k< £.ä°ªBb8„¶©¨’¨½aÙÑB˜­ŠYã–=rûÍuFПÄ>y}š™6f;Õ´ó9Ærâ¼b1ab¼¾õŸ Š«(1æ…:æ€IŒp™äDØZ¹\+hHêª [©¤ßeéjÄ£V*ŒR7VŽqeÁ@¥®F¸hèÀ®]]¢ÊÐ!…w›ñž|2¹»=¾å Án©T¢wÈî0Lìi3ÓP"}…oeoû„J %" 6Þg`ÿd;ŠY¿iÚÈ&g†¾w¼ã-Z¶ê³;N!µ¤zë [TƒöTËAÇéó¡èCžÌ¾œO6ÎçL3ëFš2Ý›Šô£Èãè’€N¼z(]1˜ùRvÎ$n- dwIäÊ_PŠB—XO7hãDÐ@¡òÏNT4.ñ|M 1Ü5›a}4mC'h0w r;¢¦§(S„\@†y¿ý¾ –-Æ4 H;VŠjfGj ƤaÝ¢¡.iùé§o€!€†Pªm8^3-²°O"¡väÉ/#™F¤!gn¢eå^ºTEÓ‚IJCZS>`}úôýh£‘s0†3Ëóß™M¸œÔ:Q kÉSàÎÅ‹/Œ P*q©Dí=÷@@Ã&ª{Yw]pÁœ¢ÄàŠæ‚þ-‡.Іž ˜èeÐ÷™†työ(8&ìÈ'ñËö«Få†RÁ*IY\”í3”;ÊÈ Ð ËB«ØF<¦T}ä#«0aŠáªÚ@×™ñbã¢Z"iBÛ}Ûw’…ì£ ¨øÌIí·}'qè>Ö°4$܃>`\Ѫö)åÇHQh¦¬§§ðèÉ9ß’r#j Ó‰9 w ʤ¨ {²(RYZ×mìå–{³b<:Ið߾ʅ€ƒ hX³d‚í*~„œ]¤° ¢»¬çd ¦°4 â Í™.Õž0’é4[Î-ÒÖWÁ.)F40 Z»sË­’’‘?@d.3¨h8ñÄ]Éœf(„hÀù£¸¨1Ò–65pr?ýé¹Î…!Z$Øy( BSV† ÈDRŒ¶„#ü‚v{Áð¡–xúŒ¡ÀfºR&ã"Éh©”·æ•sEÕæìLÓ ¾*Õ+ÉsÌ·h>F ÊrõÒ .Éz/ƒ3JÐ_ÿ·&Ø{{ñÉý÷Šl¨E¹¡Ÿ}öþn¸&57u¡ì?JKlžî,ÑYëåßþ6K¡<›)!³û±l3=Lª²:Šh¯¹æÊQO—¹´;gà ÑÞ]Ë–|2 R‰¶[‚9ÊACãï|+ZB ˜€¸§çûà^õ‰¡ì~à 0âóŸÿ8;ËÆÙÕ‘‹/P†n¹¼ÌPŒE°€É^ ¶]ŽiÍ–KƒÔp !¿xñÅŠ?´-× ÌtU…Žn°€Ý+#=úl…î¾»¦Ñ^Ǭ/U¹@€ëRAô½ï}—åÖžªv"à€»îú‘}ï½·4q ‡Á£J_:ÌñÆÜ × §m²É'Ó‡°CaˆùùË-ŸýìZäÈ` ˜ÛW¡Xëº@ƒÁ¬)0ƒ?Ðc¸´@ø È€Pµuá¼y'»"ί<¼±Ì=a†ªýfÂîµ×ú¬88~$Ð ÖÁf :M¼€Ôk žèüéPK{4HÆk¯c¶ñq vá½ðúZKpà{"êÁ-Øc-/½tJ '‡6åvùHêé§ …iàqP°Šåï§&ˉ``}Âa»Oì AQNfÅPýb ¹K9w9¬Ó¾bª„e)Åô»ßQaRˆSv~ 3–ÇaàOPã @Š¢°3‹Ë<È‚]@ÕÞ40»÷Þ+¨b!R—»¡ÜƒP¡ˆ¸' ’—_Nîú¯ÅÚrv¨-”uO¨ÝåùbÐõhOÉí@*`õ™†šFq´7£Ú\É=ÁoËÎ`#þö7ràƒ ýr 0šME·¸ä¯Pu¨¡z«FSÀ÷¹HIGªû$癑90Ýb‰W˜n>* ó± ½ ¨¤… EX×e.8 "^ÑóM(BB6'¢ 4@ü¡”T¢eÎíhZŸ®pO ñb—”SBUw$äœi™HT8ûz4LÊ ¿ñ-ÈIzw Ò´ÆÛ±&_KpCYLCÎÒx׬¡ÉÛ!É/`­°šL!29EÀ…6+Œ]ô­:‰±`³M¾eõbù S–yÂ4\vÙ÷ìº0Â0 *9Aoÿ{)‘‰h lüÁŒžrŠÂÖùCÝÎKã¸Öÿø·‰Xg°À¦íÖ[î‰kb°cKï‚M×~('8„„tCglž|…ÃP9PB äòˆ…­ξu#Èç`ô“H4í@*á)õÝM¸u5‹L%úDË®±¯²@È‚a¸Xm¼4À°¬8\EM¥'‡\r"pê9…(ë„ã¼êUÿ±‚ˆ„œãQY’tàì+®8,T±ºZ9ÓÕV[h0W_}¥SvµL“PÇ5!!"Ó`r l„cŒyä\VåÝìãYË,óIL|ÌÍðf1ô 4„@ÈÁ˜^ùàÃ혞¦ŒÍtãÜ"}¦aØñõuío ïƒj‰“>”¡z¨Ñõý§¯Ôi¨ ñ³ÈêIÄ!ÅZû£F¬õ•AWrjpØQù–1zòÉëµ(;l‡ˆže˜vÝuSË-¸ ^[ÉxiY8o®¿ÀÿâB…†Ù0©^­qTŽ ×ß 5į=<1{,q Úô•ÿªå §ÀË›õMÐÀ>ôÐím3Ëü`±J×Õaž!g$Ç ¯ˆ~€šA*¬?Ï´¥"H5$~ ‘eöšÙ‚U°%ªk¬î‰tØË †›^^žréw¯# …fš ¬ëôŸþtƒQª®©HsÇ8SGZòÍÞ=À’0ÂM"Î/¾9¨Tä/Ú@ˆ¥%ß„âŃ<¶Øâ“!ǡ暉!Sé&»XA[d‘1¢Î0Î̉š'¯¼ò;µs‰%-›b ‚0ÎkX‡«Â&ÁÜpfYêf1Ö†î6¬\ËÔé˜>ÓÐ~ðÒ¾ðñ³&ã0h_gye7.¼|”à†ZÅòâº_y%‰î.¹rºŒj6ƒîÒç‰#@졃³4©ƒc*šS‚’ÒàYlbæ=7zOPjy0Bkñ“Ø’žD^Ì„¥=”™HЉCüc%ô W¾ý7^·” —ögð°á¾MJq²æÜ1]h:'i dÅl1ÇÃl Èt âNÆ’^crµ³‡8ƒªG2bÉ“À Àt€¿É`ö!¯\ˆ±M¢ ùõøø&0"æ^>÷Ü-ж' ð|Â3bœ;,T|M&Ü +øÖÀŽ; ‡ÄŠC S,ˆj*¹·õ<Ìâ䊒«9S*²£».×z4 ìšš•ÔÈU®9mÅ|îÞ|EwïHé3W\TüM’I÷Ýø¾àâCnIVqYpÍîÿüû~zîI§í±d)`Ñìk5¼ýÅY'Ý5ù'„jù 5€†ŒzXÊY¸œÊ0”¹üÊÃ**LF=†êv† 0¶¥芬ÌCöq¥L¥Ò…3~’¾ÓÏS_[ö€¡%6‡Š¬Õ—¨{Iîü{4˜?¿û]ùò+ì¢Pމ^ÒæŸI«ÿ’ ŠRD\ßzBSÞ·XœuRÑ¿7þ xýÑÅŽMþð¾­A—»íÄo:î…ëŽ_ƒºqóñI¯Æu×·¸äúc—Þ óã<±â*îÝÕõA;lªöþ«×wžI–A/¼ì‘~þóZ¥JF µ‚†/9©Åò}Z³×GŠxqÁ}Ð0üp™t1 Bÿx 1S¨ë,Žö¢Eõ”ìº;½Ã"hîV¿ *ÏÇZÒ¤÷¬YÅË.+^qEñÊ+‹^P<{zqêÔä“{ïiÖǼ‘»î,žpBñä“‹…BBey|ÃpÑ›o.ydñškŠwÜѺ>Ø×Qµ'HÌ¢¶zîPÐY„V.áÃD;µ²´ï*½Ì4˜B<ú/¾ØÓB}Žr=ÊPýÔbY Ìm‹_D´ž}6qŽ„Šýß]wµyéâ­ÓPÉØxæ™?ÁË™–p«šòôÓíéÃ侪ñ%O<¼‘ !eþ‡]ì+œ5.Ò£–‹qeú$ –¥w…Û"*>LÈÞK‹qL³Ù>hèAÐÀdÔ(Œ8¹ h¼;Á ö¸ ÏI«åÑ{,øâÅI&½Zäí}ýùÏI ˆöà¹kjñ‚‘n„2>mý±€úÓ#@+z@ÙƒU×j"¯††œDeÉtBöoÙ‚'ž‘¬¤ÔÇHÂ%FBat.Áa<åR§Ùf•§ºTL¥IåZg£,·9וµ!`bæÌD%–€t)#Tb§vûÖ›¾dÚIµj¥ ug-·~öDõ aíi4‘ØÊs ù½ÊPý´å¤ÜмŸÆnÑõûß'WP¹»íÄù é$’Qñ.ÐŒ5²œ8=ÞèŠh4Œ—gâéRCÆ{­ž=Þó4àáº_å A¢AX}«—™tÃî¶ãƒu¤ÔŒ ;B_ᨣ¾A#¤Š8«h]—{©®Û£J»È» Ù%å¯JI:Cí”.|²dÉí²ƒNI,j €Ø“MI"V}¹²1ñ HE¼s5åP,Y€Þýîå¶Újé”[¥+Ë¿O¡Ò•äL(„B¥¡ËBƒUZf,ÄåŠd[CÝŠV:MšJ6ô2Ó`Â<úho•ŸÀŠ÷Žw\ÑÆËŽ¿5/hÕÀCoD/À’%Éξíë"MˆÇ„¿F-Þ`D[ µëÚ Gž|á†çma>Z3$Z¿,@fÖ÷* 3g£C•¸Ó,™AU)S%qx9TM‰#Q`$M¦\ Q2GR“'‰W?iï.û1˜xÃþ=¬ÐsÉ‘YŒƒ|S¢"EgÉæžêC\Eá4Z)TFBýØPfîÜíþµ„Ó©ŒH×DEXþéC[u­r‰‘$ È @ÑÀ&0E1ÅÕ‘±TР(—>ÿìgçRG º“ó\”®‰tHåÝ/yJ§“7ô8h°©šôR£Uj©¼PŸéê³"gÛ‚ààÀ&ÞVˆ/dÃoÛþ‡§K± Ð7úízÑC‘¾üí×ñîW˜kàÂ1' ÍVÇkHFz–ÞªOvÌâHûÁ´Fë´Å›BƒUÖDÆPꤨ#E=i½õV·§0½òÊï ÊDÕ”"$A=ɮܫ#‘_ùÊ'©RÛs`¥/†Ë6åiê ŸÔŒÐb ô °קäO$¡,ØS[.Ê4¡Î:k?]"A5Ò)>Yý…DÍ” (¨[¡ŒKFs½Àuߣk,=OÚ!R±jæ.* ‰(Á¢î´Tµ/’q‚+\ý‘Ë.û&V¼Uô¦—ªµ@ƒˆk•Ò(N‰Ž:•î« ¥Ï†…}ÐÐ#úN .Ž·Ê0º±¶~Û{5#66fIHS¬oc×ì5F÷Ò•qÍáÝÇĘ·é§h@‡¾·bÌgUã|dpÃx±r©46’_*U£ q…AqièFGÐ`k~Äß°¢;lÎEÛ*$ÀŒKÈ‚GÞÿË.ûfk<ýS1 4 0Øúã!ˆ@à bBAË·…õ^¥+«82xþùÛ¶ßþsÊ`*Œ²Â ËüÏÿl‚K@?@-ªW¨|ÊIôüË_^7Ôr›¿`Á4,B¹vdEʦa*ŽäÉ'¯UØ‚W‘ ÄŒ[–ià°P™…TvŒ¨PüqÒµÔBtèqÐÀ¥#Ñ&÷ËÚZاvkù•y †Un¨åÜ‘ŽÕ„:Šïk¬þÿDº4ú¹©¶fŽÒCÞ >þ”~nECƆcc%j Å4X\çϘ1õ{ßÛ>ÈE>xDýË/'Å*Uª,ç¡åüaA&Ÿ«ÂÚ¬W…ÿ ‚ ÷ÝÓ•‰zÛÛÞ Ö««‹V„ÅzüôÓIMv¡ vó ¬€ÊÃ*´†fº¸æš«üüççËq°ä»"Œ¢žE¨5•0 È7«ñ‹/>DðwFº®WUÓ(è†Îp‘¸:x¡|PTÅ4зžE‰ÙŒr×pƒ®†òÙãÊ𜠡y§÷8h€²ëª}‚µ÷H4ïD"žÚÛùÖ_Ýž›ïWߨ¬. Õ»ˆæÁW§¡ˆzm'z+gýÜŠ†àƒœ.ÚQñ6À"„FÎã×P.‚ªF€)NTá:ÓaLúõŒÃ¢ èa¥•–U0Vô†Üð%Bþ‡KKòœ¡àË·¿ý%WTÚމ òqˆºÀÑihÈPmw#v~Ü“;*ÛÖ¹¯Ê0®ÆtÖAØ{‰¸°ñzt×ËŒ ØÐùÔTÌ­è{+2ºD{’5Æ‹Tê4 «_”ÔGU·Ú"-…“Ï­ ÙA¹v{n+®@«²åYÅ)µ©î¸cš@„PX5ÿì³7+€ùÐC—Zûaˆ˜!ЪˆÕ¡0À‡ÀˆË/ŸòÇ?ÊYH ô –¦š–ÚQM^;R6|2}ú¾¢CEÖx’‚ íä`âõU&<ÊGkÀÆ‚½øÿìݘdUµ/pïóÝ{ß5ßkº*¢‚ˆ ‚€€dEEE b J–0 9 83É0 L&uu 9HTœ3 ÌÎû½«NŸ®êP»ªO}õõW]uâ>{¯õ_é¿@oX³Sßà^À°<æÄÕO0<,YÿI)?·ã9¡èú4Û1%¶þ{¬sKÁš¬ó€î'õ—VÕyüæÚ¬IAƒèTjñGÚ£© ˆ`‹×à@lK!ž¥D‚â=I^ýfêëf±Dz¢þÅ"ñËä/¼˜àHh"_ÿŽ-›4ð2Y þâ$—ÑY¼{˜\&¤ý›;$Q5Œ€5Ôd·\ô­èíb‰$Ó…>7®ë¢®'Ò»’ ÏK&Mh¶÷„ó“?ߨ75ñÂä¸#7ÒdOçÒ‹’9³G®â–ä¤ev‹‹6“ N’ÃÑ|¯Ø·¢ˆVÔùdY&Y ¿Î]š{3(ªˆPtÆl—Ü{Nr÷_›î}ö0ß‘hM8ªýž'·OŽÝ?YøÆð‹>I5)C ígÉ ÃeCu<±²›òk+ÔÊSiJrØ|jÆ Ãôhó´u3€fN£ôuÀiP÷¡Þ•œù‡&êº1½=¾9ká[š¨öᘭ Þ˜õì S^[puŸvïÃg—…³’q‡' Ö=EgCÎy%U ™2274•S7ãG›²È›8íÉV)úVô¸†DôM~.‡& Wõxãn`ÚXÍÚò°oc’Û hØ»Y:‚WuAí׿ú²ÿö·ßÛÿ­.¤økû«Ö¯%5kÝ7÷êó‘Ç4t6Þxµô¾_ýj“ŸþtýÅûäM7}8äm7ÚèkG½ãK,âÃãOäcøÏÿü÷í¶Ûèï?« Õ˜i¸@Æ@í­ë‘†ô(Ð0z2¡ÔŒ*£*ÖVÈû+x:U«Ò}¬uÈ£üUD(º˜h¨4´ì¼ófï}ïýá?þÝï¾ÿž÷ü¿±cwO’Öüã¼ÓOßs·Ý~°è¢[j©E_xaêå—þÁ¾wÖ¬ãýZ¸†40Ø—ÒIÃ:_LRöè( jF£jT•]¶»_ á†Q^vX°< ¡Wú;æ4l¼ñ꯿®€¢-˜ð1üÏÿ¼_väŸþ´è0oÞÉ‚[mõ/}és< m´*÷ÃóÏO¹ áØÂÍÐɘ±§»5–ì÷–­Er¹Ù4DËÝ+ ¥§õø`ê„\²YÖ·¢ˆVÔ>2ó_œb4;cbãºÞÊF™üý¸ÎÂÓÐ¥§a5–[l±Op!ì½÷V{ìñÃéÓ?~á‰ØFHâ[ßZéî»Ï¾üåÅ×]w…Ë.;t¹åž{. øÀ{gÎ,@Cgc;” ¥ü7¦p}Kôã·%Åë4DlØ3 _Eߊ®:,¥Èh”àæN˜%îÆÞ,ü4t^ýÊí·ßxùå?ß| dzӫ¯N‡VYeéuÖYþØcw–ùÎ;sÏ?ÿ5×üò&›¬¾í¶Ê‘|øá‹K¥×^{ùë¯?­ð4 ›§!vŸâuï‹5¶¥pœ¦‰àô*|Ódâö &¨NŸ)Úc´1¥fC!JKšâÕq ÐÐ9hᨊò…¦dïXF¡bâŧú \ˆч—_žO,\8ËOo¾9ë7fƽ ˆŒ–‡ÀÓÀ6’`%ô?NO\2¶x,šþ¥fd4À£nž#w4v£¢¶¢jˆdà ý_J¸‚L 9Ñ£óÞ»~^hè¾ïBöζ̺£x-  ·•Ÿƒ "Ñ‚ÈBßBw¯¿¾K^‡F…^3¤E7\ÄEߊªI¦Ggý!Ûc4Ø ½‘cïJNß3UooÍ.ÞŠż~å`1BÆK±?!‰Nš¼ÑÐѧñéG&(гè[‘ Üpß}Ä{£¢†y[ Âî½xåFà]ÉÏ7HÎØ+¿Gñ.F`ÐG@‘ê„“SøÞâŒ!)ƒ¥P›ž¹Eå6‹W­0 ”תqÇ–›hЫe´½È“¢SvuNÃi¿O^¿*yuú ¿ç_™¼vU"3ÀéÒ3ÎHßþM?LOæ‡Ïñ×W%6‚K*N1Ä#ðÊôäÅ)ÉØ1 ¸¢]8xÂ]\³ÓvÍ$CÅ&øiº"Øn¦;­ÿ^b´¢`‚2bÔ§Ìb¿Ñö‚kûÔŒ¶Aèài’†Uò "xà6ø³CùŤI‡Å~ ø‘î¿ÿ|$Hmm'tÐÏô>p›qãöx챉¹æ sÀÀæ4@ ¤B…!¨Œ¢?´Znî 4ÀkTõù¬Gô›c:€SE´‚÷ÅZkúgU³b(j(zÒL5ï˜ ˜ÿ©¶d‡ž o¿={ë­×ß|ó5Ñ"í·ßOW_}ÙsÏÝ÷É'/Ãm€ÿ IæAk­õå#ŽØÁ‡ïm¢Ô.¸¡ ³)4°þ¹Ó…$†Ì8ŽhMœ;†7ñ Öƒ:Ý&c‚åÑ  ù†µˆ£çåÑæºÓšÝMõDËK/M›;÷øÉ“ÇŒóË™3¹ùæqx/»ìl‰Ôù=÷œ«àð÷GHÕF0Aß_|ñAq³¨ö€†m¶ùÖ9çìŸ$×'É5öÚi§MµiÀŒ4cÆQ@Ãÿøó}÷ý©Ÿp/ªrüÞ÷ÖrÒ4 ¡KÑD½q–ò^H¢Ós‹ì6wi"6ªšƒ÷Jùñ6‰pòhšH.·QUS`ÕŠÊJɃ†Ì£Ý-l±(°DOCë³Ï^þ“Ÿ|S‹¦ßüf ìÈ4HQÜl³5ydå-É€ƒAá°Ã¶;å”ßáT^vÙÅd' UÞ}÷\tÑR;*ø H4|ëÌp.G–)ƒá–[ÆchÎÂûìó“€<ìØ*Z±Í6ß¶WšmúbÄ]Ý×°ÝÙšMÌÜ`„¥þ¯G@´Â„”X3Œ¶Ç‹¼ ¬y‘£D•Š„íÂs ¡„²d‚ArË?ÿyþ–[®Ã°`ÁUÐÐvè¡Û Rx þùÏólì±;MzD>ÿìxòÉK+‹rNÐðç?ÿV¢ƒyæ^ÚAU@Cš %è7ýÊ+Ó0·´œ°òÊK]|ñEï†fC ÀhŸAËF\`äv|øÍÊÜ@²¡‡8YdðtÛ 9F+ËðpB¾aàÒ½9Ê >¸Þ¼ˆPt–Óù’ÛÉ’å4È^|â‰Ky¶ÜríÐë¹í¨£vüË_v{饩úDËf~øÃuÿñórû–®½öÁˆ§Ÿž”ÏiÐÿIÛ'™ÞÚBêóäÈ«­ö%)–@ÃQGýj‰%‘ëà×uÖù OFÅáÑDÕ†MhèÛíô4Ä„š·ùó{»Òq{‚fmJã\Ðôt(^uŽ@Ö·bÖVÈ –Úô€‰²ÞG‰[¥Ûiß]ï †ûî;¡?Ä=÷œó"Zž¤åÑG/áEðáå—§ÿío§Ê„Ð º*0Ác!BžDþ{n‰yóNö¾é¦q¢àBc§Ûo?#ö±Ç.¹æšøëXY–ÎX”N4¡›¡ž©Öªìå˜t“šÒ5Ízæ-^½é±SZSN‰®†Ä4U8Þ†=hØ«‡Õ‡™.££¢³Qê±aUF·³ "=COCm§HáÐ)yC¬ÑÈvÉ6+ßè¦#Táxhðè•§A0‚ós$Ç× Y™Í'.YTEmzôJV[!Z1an]àΆý«žmxn$Ö˨õ\EõÍ…^ì5x#P'h |e Hbùe]ØâšÒ(/ìª>«‡X[›ª’ÔIjpsã†"B‘O„lNOøài¾âÈ}z@™Ë|§Œ"ÁJ4W¾ÅÈÉÐ쳪«ÚÅg{ñêóÄhÅèa‚ŠÌ‰\,Mü‚Šô½ݯÂÓP¸R†vz Œ3:¸±úâ4ertùžž‘,Á9̲¾£!ZdZ<÷Z³¾ÁQ¡,Ѐ¶Ažc÷ ÛH–T%!ã2Ôg–SBêeÊÐàK?Åwø¦6Obh^ŸÍëbÇlº $,3EÚc#æí”ÌÊ&KnxôÑÑØ yÀÏ™<ÁXkÓôÑ ™Ëâ2CÐ@nÀS=äV…iVb·zF BîÔ£2îmfb 1”ÞTXº.‚hy≉*67ÙdõÍ6ûº&UX !D¦P5(ïD5½ÑF_ó릛~ýç?ßàê« UPhäè 4¬”®J¿†Itºº(´HÍôb8‘£ÁD‚§™ Ì𦯭àBúԈпži [¥‰])uŒ@wžU‘”ô›oΞ?Æ;&â7LÊÛ—¯½våk¯ùœ¡Š–·Þšƒ×áæ›ÇÿèGëåÛOð+ðäZ[•°;èmM²µõ¤ñã÷ÀG©€ÎXi¥¥”Õ'´«†ÄìÙÇá‡XqÅ/ º.ü ›: Â缚ìÚ†¶Ã"sC“%7È,i²;ªC,Ö&1ZÁÇmÐÜPŒ§¡!²˜ûð¤ +y*£øÕ]—Ëgž¹\«É=÷üá/~±!¶%LŽB ºNìµ×ô¨|è¡‹´·æðž0á1¾€bÁ¯¿üåwvÝu‹-¶X aCO”ðK"ŠŽAí•@àqz䑉ðÁSOMâTÀTýê«ÓÑA^xa NôÓo4ô­ˆ$ÓÍÇ¢8º«‘Ë «?|{qJcÚ°jãW{à‹ýcÇî±ÕVßn/˜1ãè$¹n¯½¶\ÐNâ”S~»úêËò ø•R¿å–Ó“äÚRé¤Í7_3"+Tê0¨(<¾‡óVXa‰#Üᬳö3f»u×ý Xt´¹Z{íå ÐÐÀˆ!Ïr~Ú&K¤~¸ô‰•¦yyFÞÅkÀG 2A1ÇæšeV?¢˜]“ÝŠQÜ>¾ hw-+ô€¨Ð?§ âä‘K€ª€†çž»â§?]ÿo;M–¢þRßüæWµð>õÔß¡”În¿ýL cÖ¬ãÀ‹Ðϸìž° ²ë®›Ûeå•—Î…'¾’Ò…ð'Ç›oÎjl­9:£Ù]¿qu2öð䡇SÄЬîY"ÒÝ5M=77C3ùN\÷÷ó€‘ J™±HPS¾ NÅf² UE—šÉ?Ú›‰×Cxb…¾pÚi¿¿ë®¿Jk8úè%è  h1uÞyûitéówœù³Ÿ}[xB¶ÁŽ;nú‡?üX²‚8Åšk.—O@¶Q‘å4H„tþƒ›o+ÕàpŠ—^J= Y"$Ôrà ñÖ%ë«_]rút-1{¬õhä₦‡ qÿ´GrÓ-M¾äôÖâ˜m¯>†fEx½—ƒ¸-ã2kʾT,nî·†Ns®zöøj9QFå«»DH9 Â[l±¦ªH™ ¢r8$þþw¾„qØaÛm°Á*ª$”64Pº÷Þs¸+ø$öÝ÷'\!”PN{¼óγ$:<òÈ„,ËÁgÉk­õåo~sÅ6Zõàƒñì³W8g†ŒH¸d„ƒD@$Lh}¹ÕVë]qŘÈßP¼xxN=$™ÿZó/7žç¦qÌ*  ô‹× Ž@V[Ñ|}+ÜšÔ¥fb[òFk Ew A½Ã–[®#H.„ @‹TGJ½ hQ~)`Á+¤lQo)™QY¦-mŸ©7{ih™¯}€|(8¾ÒÍ·ßNóìÂÙC$ ;ßøÀøTø+ÔCî4¨yXÎíÜÉ ‘u´úc‡tîä£Ídš›BB0 ôæxA"£²†¢;Ððàƒ*žTõЭ¶îïS§ý0³¶™™ !¿Yí¯…§¡‘G Géæ)ñ.¢®}æ™f¸'¼U£Õ; /F+¤4“ZBdBÑÊúlŽ—>Åv?@ÃÞ¹“2Š'Ÿ¼¬H±_å¼y'?úè%ª.m£ÊÈçž›©pKãbºï¾ór)„-O=uÞ…ÝSʉ:ú‘G&%Éßž~ÎL 5kVµ! |å•è ÷Þ{«\yg#èÈa´éGæ©ë o¿“¼¾ ymAú· ÞndÁÂä‘G“oJ9*õŽ&O=“Üv{òÚë { M§8Í(é$F+0&5z´Bs –ºªŠÆ}ISõM¯ZFÈöÄ‚[nO©SÿHAΟ¥Þ8ßzk– ¯~µéÕW=cÆ‘ €ý'=t[|Phšvßý˜šøž~ hïiéûÇoïr‰É£>Xºe:òG($2Bf4Òù†U'²á†« ˜*Êxê óÚ’£öIÆžŒÓ<ï±c’£öLþrhßÑØÃ’ã÷mì[ètFp@2áÂF’ü1ZÑ}+07H°m\’iǰ<¦ª¢rï dJ7Ý4¶T:QbA…u±„¼!#@ k¥_9¦N=âW¿ÚD&аÛø‘Ãgì…½õמ¢ ÅöÛç7l0sîÜãƒï¡«ìÅ´aÕ¿øÙ-·\ÛAtÑüö·WÆ5ÙMÃ*Òk­µ<æ¨"BÑü aÚääÆÓ’wf'oÎlª÷[³úr;ˆPû<ýÙ·Ï'm¬ßš™|ù’TûV4h´‚Þ…\Ä l÷Æ*ÒAW5š"åð•O[s 0ô+ÜÏ¥o{üñ»ø^ÃW¾²ÇÃÕW%ØF#‰å—ÿ¼Ø0!*FðRì»ïOyôªÐîòÉ'/Õ³*A”ãx ¥5䨢RР©•CjvÑrB‡‹ÚÃÛn»¡Óysu¬±Ærºgn†ž$àÈÆh¨+פ¤…Û>ð^‘» þhõýë_œçiVrÈéÉӤƲäøÐÓï­ß°Mº±¶õÿÓO_^©^ž[ù)ϾZT278h h#”¼Âí²íú9ùq,Æ{”ê4ÊK"¤“Q¡Èç4DIÔ^!â vðƒ¬£ÅöÛo¬Bò¡‡.Rãà_ï•VZrîÜ ݤxø.¸`$ôÃkÐ MAD–Ó ôÀ™ñðÃg  TOÀ l°²—rˆ—_ž.qÉ%ò7n÷Åû¤ã‹\ðIxà6~-@C{ê úÐûþô§¬‹÷½ï¿:èçÖ£î¯âƒüãÏåEÿܹçî7~üÖš%óÊ+Óe é'yhÖ¬cõŽYg¯ü÷¿_„‡ø÷Øã‡ <¬[É0(ùUasÇ.ö#wÖ5€½º…Æ Q¿ÆÚ e±/¼Ðx:L:$¬‰™‘š4PI¸ ¼ÂA2:^Ý—\¶<þøDÅ„K&Shý9sŽ»ûî³Õ>Ä/}cÿ†fT-Ñ3b!×;µx ©2‰/œÁïj_9·Þzz<š,ËÛn;ãÁgRR7m\wÝ©ª15Á*CÃëËÂÓP—ÎK= @† ù¿þë?~ó›ïqÄöùÈáuéÃßúÖʺ5µîº+¼÷½ÿµúê_:ýô='M:t™e>Ë9gƒå–[lÆŒ£´žÿûß#bxÖY{ò“þú×—…Ñ?þñÿ ¼UsÚÿû߽첋‰ æÊšz¥n›cãf Qi5n´‚wACy$Ór<«EÅhª¡è‘§!ãdÌÓ3Ô’7Äo2iü·Êí™÷£f²&;T~ûN!óÍ!ªFå] ¡nÐÃB„ŸþôÇ€ò7^m‘E> üú×›ƒ--'ÂåÜr«­¶Ìc]¢S -ÍèÌ3÷^o½9'®»îê—ZjÑGÀøÅ/~F :Ÿ¿ô¥Ï9/Ðð¼çª«ŽßGw%ss†,ZÑ(}+Ä#@>9Þ.›§¡±úaަE aT*¶ºÄz12}4Ô5»Ê9 ç·ß 'ì²è¢»÷ÞsE†]vÙÌ[¸PE´xÄšk~Yü"¦, Råiô~Ï{þ/¬ ’YtOJÐË/Ïxíµ«}àr#yÔQ¿âu¸ÿþ FqÂiÅÎièDÈ®¼âþ²ä Œ|ÏùÂ7RÜ \yíµI[[#ÕaŠP Íhtê¶úÂ+hè“æ«KîGîl ÐP×äi 'ž¸ë‡?ü‹/>W —Ã¥—" Afƒ´bá á†d8ÎÙj«o,¾ø'K¥vÞù»ÿñÿ4pÀÖŸúÔGЫìµ×V’8-Т8Ú>ûüÎ8òÈ4ô4ðüO¹"™2©aÞS/OÎ?;9êðä¢óŸGò•O»"¹âÒäÂs“SOJöÙ3ùÍÎÉåGôçÓØžsfzñ#|;ƹ7q ÐP¨ö¡4Ô0®.¹ä§A|'K,±Èj«} é*ÖT_JÆÓ*ÁÓ°ùækàU‹¥S¦®¦IÃ&›¬¶øâŸºòÊ£d9|ö³ÿ+ZCÊç>÷‰Ï|æãЍQ»ÊiÀôêP²› OC½%—wÞ•œ²GrçY‰F9ò¾ã̆¹TCêjë‚etu?3™<&¹Xbu½¯Þ•™¹šÉ>*›®’¯ÝèãÁ>O°.ÒȃS€†ú±”aìgRåã2‘¼pá,ÿÞsÏÙT~L ÆÃöÈ#ËVŽÓÞʉý «ÑöÏ??ÙjU£¤\Óö¯½v•ŽéCÌ7²Í€Q¿jêÎiÀy|ÅÑ »ríuòYYÅçžGàÍ·ç¼ñöœJýpË·f¿]©îfTßIJo¼5û­:¶,M/G -¹÷¼äÒ^Сö 4¤Ì §¶[¨’è³Þ*)¬@·Ps´ì‚©”/¯èÇYú|yÅŽƒ<hèÅÚ©ÊŽL U)ÆUP;Û Û¬6­¸*©yŸx/îw¸®¤W áHNšB4õyTùªúûßÏÂ×§*XIÿÌ™ÇôäëJëøôšÌ&nϸ®ÙÒdç-%÷ž3€ QLJ숗)R8HÏ–~Eß³`âÛçH#;ä`WÅ0†*lQåð!Þ'¸!|ŸÚFPÂ[ož­ /U—=÷Ü}žŽÅŽ 0½ Z¥ëUÖ³íRlSŒ@ÿF 5y겺!SOCú¥5Õ÷¾ë]ïÂ&¢ÛEðÁ¾/4"H¡-õA×W{-ÞÛo«ÃŸóÏ^ðÉO~䤓v ˆ­D%Ù2¨›‚š¬_#ê ¨™ô«Tö½óΛé?éñÜqÇ™^x jH‰Tª¹°>ïºëxf&O>\ÄÔ<8çœ}óÞN°À”å9£`L5ã©§þ~̘íì…JR%ÊYâ Æ*Í/ú9Åî#rê WLJŽØ>¹èÀ䂊w1ƒ;çïŸ\~hrN4Òhè·ß(‚$—]v(z Ìð–ÊÌÅ8,áWêndýy챉¾”…CS )ÑIÀ–’uäüÚrܸ=²¦ `/õ{èçûªÈZe_¯±Æ²sæ¯î ]´¬«Ø°JQ8ú&¼Ñhen{À²´ô¥ôëúë¯$Ä÷ Á’°r ¡Õö‚aúË_þ< jÜMxëT‹Á(oï¾óôˆÔvÅU ÈÔ ¦^‘Ì82yì’…hñ.F`°Gள OÃÐÞ_þr#‰½˜Ñ1’ÑŠz01ÿû-ÕõÈ꥔s<à ¬€átƒ V5´)Ó£Dˆ¢ÁmНì|€¯XôK2÷4´*Ú÷½ÈЮ¸ J2ª°;Ãt¸iu¿¤ø+].EJHfPÄHÑ:î¸]ÐÏñ(`}VJVå]F©7)7SЂ`ŒÖwûä_Ì;ðÀŸ)Ó›GxBªWáqšÕ;ŽÀ® C¶t‡íD½ £¹ËeoW~±}F H„ìÏèõn_ )¿µï~÷ÿAd~öÙû¨úQЇ[ïºëœÿýßÿÁ›þÔS“>ÿùO‰Vгgh8ûì?üçþûvÛ}‡Þ,Dºû—˜ß»‹6±Ù/_B÷÷ØGÐ=U «J ˆç´è‰ÐyòÖ[ÇOŸž¶µlm=qÅ¿pì±;)ßRÐÅ9ë^”A*¬_yœò®°QYWmxBÿ*9´’"m ©ü¯_4„¢@ŽÍ85+· ƒ¸þ Ø·(@C߯­/{Ððe)ð¼×0Á¿ýÛ¿ OÈ©—¸ð‰O|øðÉÏôÃþ »Ô—ìØ~ôCª‰ƒ O úq0È„;ë¬?0\«¢áÍ,9Knô 4TÏÌö܈b8‘ÐIvd!ÈL -sVúæ7¿ŠmF?=~tx8`ýµ3ˆ Rʤ@F4”¤J8„±çž?Ôth=«YŸÏ’$dV¡E„¢igï@CQÛֹܴ“g°äf†nštÐÏ~ñ‹ 4&¤äÂKn™ò­HDtHÆ6¶òÊK ˆ‡’½’!jPc:¿´¡ì•U–öÆ|*®Q(‹þ­÷ .E~¶Ze¡*²Åƒ”ý@©ëB)àäíÃÂ…W‹9QùÙfù{£ h3ÿc:Ÿ\3ÀOª.#áŒ%ñËâÝœ#P'h˜69i;9yuF"Ç¥x#0¨#ðò´äÁ ŠDÈ¡8ÔÇK/MçbˆÒ þ©«;ü;Ù¯¹Òý^2,d‚æÏ??%ÔJ¤DH}ž}öò¢ïñ@<² ¹°E•Ïg$• º@|ÝÔÑvúSQwÛÔ€©NÐpùeÉ?KÔbqSïbuÆï‘L8°(¹ T§ìÊ ùÚfȵ* ~SµW¡)êíÏiH‰5Z’wæïb†b^•Œ;·çqé‡õŒž¿";¦ Þ|3yàdaG¯Ã‚Éã'ùs4PÕ2`jÊjÚ9dÛþp=I0'rì±;W¹j ”ù ù– >û5nÐzùåc$övܱÂ6|Ÿ’žÁØD ÙEÒ.F“&R™#Gy×•Ô 0B^tHHµìÅ8-yuzz"o9a™¤ÊÄ£ñWï^ ØA¿òÁ™âø:&Ïí§÷¡÷D>G½ŠV!uÿÊkUô‚MA£Þ@ýÔU0©åá‡/ž>ãȘŸ2s汊jÈú+&©¯ "M´¼ä’?†¾½ée´µ¬Çz²êZ^ýꉖ˜ÙÓ–i©~ÁÔŒ Mõ¢u˜˜ÕÉ®çþ¾ì8\q„7Ëix焾ÿþ2ÂõïË/§mÝï¸#yâ‰dÒÄh()ãþõ¯7Vë0÷Ú(òï}o->†]wÝ<Ò~˜B×_šB¬ÊÃÕmŠ ¬lß9M°|ÆŒ£fÌ8Ò1åUí¶Û–Øcš›´ª‹1‘ØLb¯9¬û¨ôácØb‹µ¤ýš™6v.”¦]³ §Þ€†¡è=Ñbrwü.ê?b{¥=Ä,I?ó̽âR"È—cÿ¥÷1ÇîtÝßN-R›e*½¸îw"$)Y£¤[õ†ØtÓÕ/¸`/ú)ßC(KQñå¼9sNPA«žâÿ8_S²‡ºÈ¿ÿýµIêÊ^ùA‰f\û7N­’å*À«®:ZQÍøñ{FòÜIãöys0E'ž¸«þi5 ¡Ãñ–mŠ«Ê11`æÈ+³;ÊßN¾O`¶Ajhîµ×VZ‹ìû,´Š{è'ÁÈ>c ßH}4¹ë®VxûíäÙgS¬C<õTÂýà•2B O´ò1¨¾>M hRpÎß‚†8»”qc¥E* Ú~ç;«*ÓÂ;'¦ú5ÖXîÛß^c)v%Ä€ÂÖ[Ë”ÚøÁÖEM†³–¿Á7èeTvvDS¶æšË©%Vâå×ýöÛ:›]Ž ‰«f×Èù¢"©Ë@ÐÐiµ…C•$,™º_üâgT¼Ç®ö^¿øÅÏ"* W—_þóªÓ•/Ó«¬’rëõd/“³NG _ !m2†Í©T:)Ð÷–'º&Uk¯½<¶Ný&®½öÏ t53=úèQF‚/ynO>ù·4w xp6ú ¾-·\[ß‘G¹„‰¯x¦2¹[î¼ó¯l>Žß 7 „» ÐðB r9Ú|öÒÿÀ”dð±CíÒÃO°Àxî¹ç\&cðR”¿êî‡Ü* h'Ô‘âÁ Â^älàÒ8ê¨_¿‡ëtL0îÜã‰ø ^pN Êv¿nØÇn@¨Ÿ^˜ŠW›§šŽáêp^6¨]¸+ ŠªÎ± ŽH{<¹å–dþü:ˆßw_òâ‹)zÈ^eÐÐæŽpÞ§>õQ‰Í=Ð4pxúæz¸ÐÐdñË.;Œ0ý~üc\rsÍ:xß¹™%˜Hª¼ÌÔE1òÛß~Ï÷€…¿f£m†øàáš„&0‡„žm¦G¥'rÉgTù…˜nón@@C*¦æÍ;Ù´!ýðÛòN™]W_}L0´ZLNÿz“{Ç­EjN¼ŒŠ—"7Üðkóç³—®»ÿþ ±!™œ¦"ЛÌ묳üƒr?ÌsdÌÊfoNôÚ±úG ¯ †#gï¾ûlí$€V“8 TakB¹Â³¤-@€r½õV€|éÑÕV[ÆÆ”.À»ÑF«²ÆV]õ‹Ñ] [-”´‰Î˜»é¦qˆ;Ä8rMÉR>i*<·TæFРæ’K~ÚÆ±ó© 8ŸÑzó«€I Kø™(þ(— ©l»íF8+3ÉniÑPyN¸—AØÁ¦dDºøƒqIU`Èf€ºx±8}†„àzçÒ(…~¢{tøÄ(b@n¼Ñ‡Í~ô£õ¨«¯}ÙSNù­óºåe–ù,3cÕ,ò´þÉWß–o\œp@2svrûí)\ÓðÚkäàT<  !ªPq1ƒªÞqÇõ%ž« Ì^†=÷üQ»è¢Çcºz ¦ù6iÒa?ÿù†Á[âænSåK_ú|≿þÝï¾äm¼ñª,D‹ö“±–ÉŽ$ým©vÜ÷޽Æ@^« šY ¦xÊM1} ™ï3‹Õ–Xï̺½öúÑÒK/JFá ^ûøpmòÚ"Ä mù27U‰£‹ËÅpSOƒC‘®$ ÐŒ ɯ„UØ-Dkôw>ýôd²9\™Ÿõ­ÄÂóTŒ@ôÙ÷ºz"´%%ˆ‰]úþ3Ÿù¸éHVÞxãØÊ´nÝa‡MÍÝoç{_*V„8I®‰ ›ym= ‹{ùhw™ ´»î‘ç˯€°~eÑÏK™*sܰƾð…Eø–Y{!G½•«cé¥?#š k]ûl?Q$,N¦$[ŸKyî\^èTšC!®=E£ÓÔ9f+ÛÔ– …ëÁmN%XØÓ§ÿÉBu4hF¯Ð:‹'ã⫯>úž{Ι0áȳyAËØø(> w éëÔåª8Nôbá(lÐ.u ÇÒ~™Œ—\w]š» á•WÒ÷ë¯w€eÐkëCMøÑ¾ñÒK€l>"Ðø{€09©|2ô"µƒ§a—]6 A®’éª)7/|l BZ@,èiòZyšr*Mªèiæ€ àQ3$ôÓ+ƒŸM›Â™Ôˆ!5’§.­·ä2ä4Xø&wBÎïUDê™}öÙÉ>\x¡é×¶ÓN›òÈ’6d ™ ’’`,®*ßq.•lù¹Ï}¡2;Haœ°”bx"zò Áls#¯b³<‘÷ Ùô4”LYªî‰'.åáçÒ,]|9{€¾×ŒŠD@fØ‚µ@„ý÷ß:ú‚c¿mêÔ?m³Í·©yqepÁ$~ðÁ WXá :”l²ÉêO<¡=U-•Gûè¼òÊ4+DìY«°ƒˆçư8­¶£õH3æ—Ô³EÅ)B+h`QiŒYÂN 7tÔ+U§+ç4ˆ”s!ê‰B7þ–±íVo¹e8yâÉ䥗’çŸOy$¹÷Þäž{R‘u ÜH3M¤šÎ44¬i¢"¢_i¥¥L]$ P°É DOîé œC4|ñ{0b±Å>&^tQÚ9…|ç{ð 9äK!_¡ûý;l§¹UW¦V “b.ršEEõ4´é±ÄõEsó)Ù’‚Ùˆ7hñ…5Í"ùO„§ Ãä CL$&‡9Y3tyIU"^À^6.!£DÄòá §6-úç?/€QÂéŠ$›Æ‘„#ÅÏÑWÐ#Þûï¿Í 7ð1t¨§¨ RÇC"z›y,; Â0§)Zš˜êå!0¹ÁÐ!æ4HRC!LX9y¹/ÁMÚAbÇðDˆ;ÌcÓ³™}÷Ýwž@5ëŸøù;é¤ß êé\òÔ$Rd¢|ï½·âÌ.ÞöÂùœ‰,’§A°ƒ.ñAˆ@ýó]SB”Sfb:²6pan‡Z"#ÆŽÝʪà¤pýãÆí"ë³E:l\é-^LßÎF NFÈŽ ¡2?«Xbu™I x:ž8§®Üš9sŽ ž­’ÈÙ¦Y‹çxÙe‡‚"ƒg2Ò÷×€SíxÒI»Ê’°±™àû@j®ÉÞq̾è@FgËòãš*dôh ­ì3 “1/Tf\ &A?D ?â¦ÕdaÝu¿Â)ë'^±¢Û®àÑLà}~ Ø!¼ óç_)pì³lÜ– &œZø ¸Ð(%÷“œxt&~á…z€C] ¡‚©3‹­Š2¯œúÀGÜFBïå|ø&tG›¯Š9ªš§!¢*üž¡ïŠ™ÖSÈ$⤰#â „}Û„Ä·ª¨^m/Òì\™yZ45ׯeS æªjÚ’hÍuœö4 åà¨INqdp#ˆa(G~°Ï50 ¡"¾2yX%Lj .²ûª•„ñ›H]š‰yò˜Ú_{ Šã7ß„ðÄÄ “—^Nn»-­~W ßí«NÐЇ‘jᬃ»C¼‚Å`xæ¥tó!÷põá‹]z?} ™’®ÕÖC©¿35Ðûo7.‡ì‚«NTåNïÏ-Ô¹oE²êÚqÁ‚«8½ó×i¬œ:—mW×øTvˆKÖµ£¼`§¯ô–å÷Æé-»_!¼î}ˆ¿ÕØ4`ëש͊šÜ¬ô²‘Źþgž¹"µìmÉeƒPlVŒÀˆšDHpᡇRèðØc QóTÐP—pñcZÜÅ€Ž@¯ACÚ‘DU Ç•Ô1c¶ËÉúV, RV)B\¡›T¼ÔNÑjþËìsI^‹4Þ\ ’N=éöhÿÏ9g_ªH±®tV³“åèt‘«Žæt§©=}htE!‡¦«k®½àôUvŒÅx]Ülk3VŠäýdñ;<(´s$"µ€P¾ÑÀ|3°üÜÈßl Ä ©Y²žL=uÔ°5Ô"Iª Ä@°IMv8¿Ê¡–*{åèòÔàÈÁÿÃJÕ3rR2ÜŸò Ôy©‚iv…§¡>,Xˆë¦.ª'pîÉr¸õÖ”ãÙîUhPGÐ'“h@•MGe@‘°¥˜ÞýxŠÕR²ž;¥6*'í³mÔ³ƒš€«ßªëÇ ø3ê逽 Ôª¤Te·Ýv†r·U½'TÆK³ÅIºãŽ›D¾^:ƒÂÆk:ΕÏ*ÓbÁ¤™#›5&´úÕ1¥ÓFK󣔨À ™îèAûž“Ã5Ä*(I|ï/Õ뤎@…Û€»‚ûÁ)òv< ⌹’<#Ó*ˆF—#ç‘x‹8ôdi±k’ˆm‡Ì.çÕk@B®ƒøÕYì®ÁpÚ#¦ß›$öÊp@Ì;v%öUšôÁ¾WÖ}¼5‚!m¹ågôÛщd1û2ýéøà6]OÃNgè¦ð/˜é<´³æöJ³¤]§ª"g‘ý7 õtÁ>`:1¶ > ¿ÒâˆÚ"…¶Lž:]§®Ü(…<èÛa#“PžxLÙ&"<#Î ÆHú»îº+(›2ÂHYÐ'´£ŠÁ õÈ“¯§Ó…mP]¹=-äd4Ét[r‰|O‡ o*¯ZÐP•‡8—8 ©  ÿ°ÇuÒÙzUœ%g8ˆ¼Y*; T4)}SÞ-8+ã£ÅFøªí$bº±xœŸ•󯲍¾eÑ“¡=e{´ƒ.†ªèÉüj´µÝ;ÐÐÊ4ĹDáQíÔ-.¯\¹Z¹aBô\+¬°Ddþ·½7C6Ò‹mŸ§™34™ªZªT•ËûÞ{Ïe|c¡ËeÉ0‚AÂáéàì:³8J%‰’Kš‰ŠÂ8ŽÍ­`9:@£ò ”}µɦ.íkƒP¥™ÍÏV|£ }©4³èSŸúκí¸ãvÆe²ùæk –¢¹%Û+ét Êwá„"Ê‚xËÉWäüÉO~Ľ¨ER ­T˜ë‰yB)º©ï?ÝC#Þ°bÖ6Æ=mM1S½NÁw›zy Ü~ì±KÀ,‡RŸŒûäW¿ÚÔxv:ë€ã&ÁÙ-«ÁιgJÔ?ø•Cð)hPb­<Ê—Qf 6ÝtÓ¯£Ï mnJ/¿<Ã1•\uÆi˜¶E¥Å¡dчŽ`ºU´> ²”rô4p'`Ý@êlôÜ«¶%Æ[½c¬t”*¾¯Ô{`"dûzY°È„¹Ô7•Ê™02U+=EQ’ë“6i4±9ˆ¥'žn†ØPPGâÀÛ[ ”5"y‘AMªf JªYÍÑ0M3°_"F#Ýž ÈFLˆ&Š_¦€#ß³Õ7Þ„i0Sì83g7”TÙ>ö›‡ñÍ¡…q@ª+Š‘Èw¸)Óú†°{6Jî\ùÆéŽœ¦Ác_ é²{±;i¾‹·ÍªbÀn–±‚;È=ëz#ÅòßçM´°cÊc9…„¤ Õ¡é–DóÅÇBVãSiš][º£½(†ÀBØ\ó»w ¡„óƒÖ¤Òt @¤T#«.·Æ6°< 4%Ÿž>,Oà‚]Mx ±=My£þ0€™á>¬¼òÒ‚F¤PUK¡êãJÓ T©a‡?ÀyäbPt áLð…Ÿ€ZÂîŒÊvð…X8&„‘UÛ›N ÏK4À=¶wFA r_ë,Ü$nÐî矿Ÿ]¾ò•%(Ë. ÑMÆ1-(Ü¡„uxV^y)p´ ãV;ëÒˆ^ÝXaªAþõ/½¢:l1MÎÜoápŽ–apkü%žTô4аºæJpI€Tg ¡Õ :ˆÊC£¤…X`ji/à2\bL¹YáÔ——âY€jçÈÅ‚ó œ´d<¼ ((Ð`¥Ï +=¥x‰’3xeæÄ:LS"–ÞDPë51˜~êèÖm1`¯(£3)ÈÏ´w1ÌE¸šKP4™Ü¹·ÓhˆÉ ?ž´´$Ï<“ ȤKΞ­,mž•&”K˜ÂéV;¢eB34GIK hBvï½,Ê˶ˆè\E@{ìNˆÓ  @<˜˜&*LêYÆÖpì€#2¬Û”k]ÿ'}Yˉ³”‰‰Ü‰#Mî¿ÿÖ!KâM”}”à (¼ñFŒ<œ„ïcò`AƹXëŠ!rÆ{<ÔªÖ™§”Ø%Ñ [¸Ù´W§+d®U•†FyŸœ„®ZÎHf‘b®Ó°ð GG"MC3COTš ('…ÚvÔô–•CèUÛEŠ=G‹n38v4ž1¡ÐlÍëzZé¶vúîoû}4\žþÌ™ÇæZ·W7¬ji9qÑE?fÊp3ª «Å«ñJLLóоg./µÔ¢¡aÕ®&Xl*ˆÄÌŸ5ë86ºÉà! I,\8kÁ‚™8y.¹äàL`—¡×Úü,I®EDÍh¶êRgì詪òÛ•AƒG¯Á&¾jÓ’él>èc|èÒÅ‚Z©iaàt4IâKX‡G„K#r‘™ô÷Í7+inÓ¯ÈÈÀ»NmǻEŒ½Ývßqͦh$‚N_ª ØüǹÎ@籺Íy®Ž{ï|¹Æd†Nr†{»*r‘‡öKg4P.>ô|É”w «ÂœÅPæcpLCm’w ¨OsÞséÔÓñ„f ©O‚”@$_u®ªwmIžþF£¥å$+Î÷\/Ö ³˜ 8Ù*´žR¾Q"ËIÍ@r‰¬8÷Üýˆè¼# Üˆ˜ˆm~ZõÉ–·ÐH˜/H×vTD˜Ãæ Ùh&Ø‘lñ¬á,¶Ø'±†4›]1rõkS²ú@C„S§&×__ &+Ïæ@¼È i‘Wd_ºv+Ÿç“¾D^fQš× ùÅÇÈ g=XË,ó98€&a¿8òÈh>R‰¯‚ùýï¿o{&šEñNÁ)§üŽZ¥¿ib;F/%‘í\<¥ŽÀÝꌄîœ.¬Â¶Ö&%¯ØqÈ>ðÅúa´YcL(ÙU:OŠ_:òر»‘ì{qq¢‡g•žpý0 ˆ£áE´–€J"ˆ)Än> ;‚@$ÄG¿b£X‘ÏeèbÔqÁï¶Ûté' ÀEB9Dn8ã&"Nà:F€" \Ôñܧd9b”ŒD?‘¤ôSg’´a§o/Aƒ,Eäâ߆ ͘Y;h0Íüëdcv0dæÑµ|ã´`Ö°ŠÞ2ñLžFç¼y)h ;[[Of…ëFkΞƒ4˜lbŸ©œ*ÐÛqâ\ L@ŠÜ|®4œâJäOüéO¿r_°‹qa®ÜrpœÛüË‹@eŠ•Ð@ƒéíRx íwÍ5’¯yæ™Éî…£&¶c.´7“ÇÅ@¦E ¿ÚŒë^,ÃäqL£AƒEz×]é×Nž|„@IÅûÕ!{ ®yî¹)÷äu[Lj†Õ4ѹë®Áèï|ôÑ;Eî 4h¢UM°OÚd3¬¶Ú—*-—œ4?Jtpèô4´z”s= uaãIœ‚}ü:n¸ déPBÆ?D'°ëuNC¼È<(lu.wGžvÚï]³Q…Õä D‰‡ʼnˆJ’Ç%1<£|´Åt%IBšm»+׬ O˜^:è&ÄaV/ÑçT€†bú1½ Ó¦e ¡•1mNï²ËæK.¹()„Îé’°Îù9Mq_†¥{í­·žZPNÒ»ˆ¶&™bdœiM¡>úèÄ… g“)dVÀ™s˜„­"æJô÷ñÇÿš(9ñÄ߈8X'K,±'„íÅYAu#ØL™ëÎ<ó–™+!û\À#\ZZ[*­œVëœ÷Ò5ÌŸŸv#„¼ù&Oà<'âÈ%/bH8‚ØÅEŽs½*RÐ@æ†4…¼ýÔ¡°3‚†Øÿ DÇï+ô0~üžDF4ƒáÃ5ÎÂICqÔC< ùHÿÒÈß²ûî?dJÚØßÀÌî½ÊRÏF3h0\Ä=3‹ï:4 JÃ^•’‚ÓÕ¿'@ƽ›®`„gÚòUÄk º¹Í$¦ÀÁà˜<ç>˜TFæ3oC³u «çŠ‘ú ¡5†'¸ X{G4̀Π4DîHø5žˆ‰ $üª• {46Af‘›·f”ªô{ß[;$ÿ·ùާr($žªè€ý³Á—\Ѐ5ÈÕ29RÐd85|¼È"ú©.S÷…&[ŒlšÉB°îLÝžž($ËÄJ©ýÚ1¶2×°¸Tà˜KÀhsZdiœ'#žðà@aÀ ¸;3_1ËÇac€bÌZÓ¯®L±`³aÉÒæ±Ë©Ò’m XÂîØ#*ra?åBûåð„Å`Ä<·ïø†ˆ½aÁ’i–A· ¡M@Ä(‘áQÁ—@×)¶Eó9 Ð:é\óð׈†0ˆGé¨J~ôsÍür$¹qbç‰ÌÓÃz”Êó"TŸ|RFgÉ3µ»¹êõ¹ú·j¦a-–‡ÑÉô4ÄtaøÙÄP•s,‚fPEjϳxÿ¨aÒœF'¡øf-u°Ä4µàA`—×¼°THI‚‰H ­"ær‡ÔåkhDnR¶IJxYl“'H*´Ûn)x§­L*Ö‚w"†`H´.ƒÒ*v#´~|`š[Q4tˆüÍûë_÷!=]mð4@B×€…XŠ=†*£G¶AÐ ¬HÓ¬±Æ²àŽF‹„Hèz—Š×Øbã< )Nr*€†µþmê-Ȉy‡Êÿüc—D«…dº2¹=éubÐÐæq³³Í(#×®½œ#ÆÊ7ª¦®t3O2“¢hø,Fè{[9À¤1ÆX2íqxú~õàXŽÏƒEoÙ Î0Ud9¢ß´Á©gÐF«ñv˜c( L!É`ðôÍO xu ž,›™›–LÇÕ”j8ÏÔôF \Ö[sªñˆPiÎî2ìåWljeHÐcg5V¾°‘íÖ¿²8ínmB¨š”hq;†øZ««Š½Ü(-HÔ^RÀ ~5+¢åç7ÆíëÊ!'n+Zú-Ä`@ÜAò6½DÇ7€v ¹vkØA8ò‰*8XÛ@ŒuçqHÌt ¹õg¬-fz”$1æVG%,˜^p@$í< 1 íy„CÇ4£V#,²‰í£ËЩݙ㹫= rÁ|Žã:Ža ^| q—^zÚ˜œO0A®ø6mÜÃ3dˆ,sꜵà\¼_n’0nbdlF”‚OÏš™a‚å°WyVwôÙ”AƒÉ/4હäQ á ¡€>ý~€†xns±£©†',Wâƒw=(Ây4[ &¢iÎWfyЗ4DË,†í-éè&µ½C.ñÐ$T_Cþ²ZÀv ^SþUŽÊÑ„2h ¯s‹¬$†œŽÁ}1<ÁÜÏ@AÀ d÷pêB6vŒB1ZÔT»• sHò:`È|̇'2OC9)_Õ™%BÚâž!†¤ÔLâä>øoÍa¬Ú,röA—ÙÒ¸À‹_èT–vïL@‘'ݨ 40žŒª°è( EgÑ1%¶¶¼Ö7•j´4¹,lPò!¤¡´gÔæ•™CùUzleûôCd[Š kù³ƒSiVAÌÎñeDØQ{ù7‹Ù¸ù5Ìž®J¤µ±SÄN%aû¨´Ò¤¹2\Î&îxœ‰u!?Neº}eGGð“{ j/¬ßpmé‡ÜUe—çCEG&åkG¶îLfYq¼­zà©*'Ú-‡S§I¾’=ˆv3´’ý‡´ìɯ$¦—7ˆ#Ùëì ‚¥¦X:D!s°º!<¾xöwöŒ¢ó# gñí‡ÌWNäì%hè¼a•µ¯ÄF’VLn…'ˆ>_2N„Bh}ÂH¥éI9@ ÁöU™×¢HÓ˜J1^ä ‚¬±8sUŽ­V53.ï$Ë ,ZŠœy$¥ƒg›»ÂÅ lƒ¬7È( cÕJZA П¶C±ºøHÀHBvˆÌQÉÝóûxeÂõ¤2Ô8ÀPìÚ·¹ØÅ^½Ìi(‚ÑNïbiê´#ÌÒᾌ}åñL;ÅÈ ºª¬’#E.4çíô˜)^É1P ÆyK\˜\,!Gï= U\´ñ"S‚ÆL ZD¬I›`!R\Éd¢ã}C^hV=AmŒlbF²¬MY{„TòÓˆ3‰ØŒÉÃ;%xD>ì³Ïe¶ëÒŒgTsÈF`àAC™u•²ä´ŒeÍp½õý½l *96a3ã+ß§Kèõׯæ` ¢è±½_£ Å_ňŒ%CU8T¨VJÃù6ŽV—Jä µ#ßàN;mêûXgÁúyâC ÓIkŸ.{NÌÇt„Ðú2½$Ìj–XKÙÆE0‘_ù (§ðM ¸º¼‹®â4¥ßÉèý”Uv–x%ö5’ÁÆÆÇŽŠKÊ Ss;–€-ˆ'Ÿ†Ù ¢¡NÐ0mr2ï¤äµÉ+SGÆ{Ú›/NyùñK’—GÈõ àeL{祩¯?sÙÛ/MI$—|ZòªÏÙ¿xºOÝãf}Þ`ÚkO_öú3“ÚïÎéî?79KViO¯;ïL®@¼Ñ)hH…ii³ƒg(G”ÅN² –)C òDÔi†Í"³VÕ¢–tRÕC& ƒt%KSÇI&%|O‚ æ½!Srʼnv4äL½üµæçzWŸ£uØé¯Z]m\uÞÔùÁAxgÛ “*k¯êtU'ª:E­ýZ{aÝ“ýZ{äêËî8’Ýï˜2 B ·Ü"«£ÚÛØÐ¡NÐpÓÉ ‡%§›Œ?f½GÚõ àà4ñ­ÕŽ’›­ºß?žLWíéÕh(»rB¯œfQ‘„y)Ñ•ç¬Z§²hx=p«ÀŠ£ ý hú;éþŒ©Cõ¶Â0»¬qX832ª"â ¢3¼NÐ@z¿ýNñ.F`èF 'Àþ4\ž§øD Ð ‹}8ã‰Å£©sZÒÖØ—*¹ªã•+¹lD[¶{˾Îñá›5£ Q?h¨c›#0¤#ð÷¿'{ü$Aê,͹x#Ð#pæ^É…$“T#Öñêh9ªºƒ£¯Õëúé4rÑ£Ú¶Amˆ¤·X¤Ç³¤éMâV)@Ck³Ød„ŽOǤɲwó~ijâmƒø·û-eŽÇ ^œúÎó“Ó]²oºß7þúÒ´Dòuü,µË¿õìÕé6ùCõxÙÕÝù)ÞK7’ž7 ‚éqÄ:½°pƾßx7ÛÄüÛNï§§¡Xò)¡>óIŽ0Õ¦j,UrÊ0R‘Ñ)ým޾öf¹ª(•9u«ªK8b'L8Hª¹hFØ>´Ü”•©Ò¤§[d\Çz¿:eh·/@ÃÕ‡ÅeÕ1Õ9 y›![G-*½±PàŠPðU©b­]eiñçÌYÇF}‰Û3®<ÊŽj°?M=+½…عdâÁâB€–9s¿àÂr”õ/í´©Âò'Rj“žm§»ûž³Žu&ŽZ”u<ÿÂõöS¦Ñiü^aÂ%¼--¡×föÊû EÓdÏÞ¼[bI}WÞ«#†ûÓ€%_7œË.4‰çM™¢½OJ£¾,ªy"Õªo⊊dŸóvy~ûìs4©óÿV}“þ¤fR ¨KÂ}@˜4¸bûàškëô€ÙÚH·Ç5úYdyÑ-‘­¯ãDÌ߬«jã˜J23BÜÀUœV”t{ñù_}n½òÊ£±;Tª'ºÀV­•T–KŸnø…Q€†:tS±É !­ =W«TK µ®*küuŠ'#iLðM낳ˆÓÐuâ5ê*, À4y!«ôwFIûšâêVo{|qcpÒ·k§’¤6O³è(S9ÿ·¿1“{÷|j' ´ßyEî|(/G櫤N‡—<ùUe :\mÈZk-¯BmuHÑ*m’öyGýX7 Z7â·04PBõtíONCÊcãá4ïíó '?†%DF°sÐpxñfa½Å„¯ ùùç1¤L5Laz76Íó/Î"UÈZIP§Ýq¶Ø&0ô `Ê#ZPûŽ-_%ö‚Øið%&D ;vâM²16$üê•ñ¢l¼ñªxT–Sö¡SeÚ •¿öÒ Ð–h‘bÁ·í5pv‰zÝÙCË`u¡Wa‚Ò÷Û…<­ ’ßWÖmZ%å"*·’SЙ¡U:¬²Êñ·ØìÒKƘkÇBp£ÁO€ŒWá ª®ÇÛØX0h Póþío§ÙrÇ~h)Æ*PWˆŸ*öæP¨‰·*p?Ôc‚Œà©_€†ª‹Ëªc:€†¶9sNØqÇïvT±-„þlœÜ¤ YD£‰ä %<-íPjÞ‚Dœ¨ÔT,2ÜÐv(^ˆP¶0‡ˆbF i™£ÊháÐm+gˆGа‚+F :©Ð¯<Å\¤$ éôðÃ)÷ ÌCÏx‹5 ©±æÓv}.I 1K†çÚÂ¥âš7¸ Õìeä-Á ¸Sݛ؃jpÍx²qt†{áæÉ“‹×JQ2Åòmظ ):õ;C›¯¼’Šâ(EIlAsW|Á4‘«B¢ƒýÓöqèòR´á-®Þ9`êÔ}õ4x®&ÀÕ¤¡¥2öS ÆÔ³š, šåKG©d²Z¸’æJ­•üêö±Ãº³<8?æ줌[sI¡£Uy•.|ž ߟ¦ #TŽÊËzçä­·’7ßL^{-™??yùåôï+¯¤o^|1ýæ©§’‡Nî¿?ùÇ?’––dRÚ™“y€ÏÑšÕöŒØd`d¼×l}+š£Q»Ôí·ÿ¹¤ nˆ ´¡Ý$dˆ>ÂÓºŽâ‹´d« «É ´/{l¥•– ®:(9º³#SjÉ,¿üçu¿$¢™s!|Ybfl±ÅZ„-ïBhŸ‹{·Å—L âŽZ­éá’úÈ"ì´i7•œp@žË<ð¦çS ÚÛh¹;WÎÌ 'qB³ëBoÞ6x…MX•¢Q%E+ ! Oð w*EXáÛz¤nÌ©’¢†Ñ#àk¡  ǰ \øllCä¤èÐHƒ¢ãüúáið`å\Ô˜¹X¡&-Av BháÓvÑE™à-ÈLËž&éØâö ÄÉ)Ï#ÝÆµ›  âRK-j@ßÜìlêÓ2Ó±Éò3§¡uK-Å¢.¹ä§éK›(Ìk³ÄXðÑ1À(ŽæЃ ôq4¸Þ~GÖRH§+ûZ0Aõ¶‰OÉ !5Z4R5›[Ø=h¨çh˜!~iº[Ÿÿü§têÒ} #5ç_”à¾$[”Òà<(Q‘*O  Š-ûØjî6À#‹$×¼ªÄ—ƒ¬š·³± h•Úy€oš²ÏÞMñ¿ñFòúëÉK/¥šþ™gÒ¿Ï=—<ÿ|ò é¿><òHòÀÉ]w¥\ À­·¦ÿþóŸÉc%>šÿÞ~{úm¼ÿõ¯+ØýÙgÓ£ùà}ç!mvJH’oú‘r3äi-s+šÇ^|´d!ª!W2ó˜cX#m¯¾zåO~²>ÐÀɯØîKÌ ´)ûÐÒ¯Ö2Î{€†I«®úE—Amë†JkÒ—¼˜g"T[²>Ú.¿| ’~ò‡Œ!’ŽÒ, OS¡Ùz*“+aó**êö6ZAˆ§m´Ä#„0ˆ¸àÐme:Â(!3,#œyÐ )­S) |ðjÄ€‘˜?“¢D"žMÏdÁ“·(0üÚ(ÍU1sRtÀ5n°¯ ¡BVØÂ™¢ærÒ ê̓<¹}¸ è?ªZ»þ (½­­+Á[LËí&Ù˜“ hð—ûˆb&<¿>S²xLñ8A7Û,‚†‹8Óô½ä†R•`2qÆžºâÚ˜â# °‚%!¾`Þ¹¼§h`(=¿hp¾ ;¸¿~fR`ða¨¼8T—3áûàiÏÓ¬ò4¸}%¹¼åðDð4̘øBÄ ]¼ÏÆÈÐüÐIåLX ‘‘ÚÆBn’C @dc‘»?…Žˆ-ÆÇJ€6,ÂÐì :?J< $K¤ßnàwX6àáèû·ßNß”}þ½`AªøYùôôÓO§jž¾§ÑŸ|25÷Ÿx"ý—¾§×ï¸#Õý÷Ü“Ü|súï½÷–u?MÿÐC©Ê§ï#p»sPüœ¯¾š¾ù|ã¯Óq3¸—TÏ«cNƒ€Âþû3f:(àŽ á;@µ ÑIoÁVýk(T?qLZ×ä9&µË2'è` ˜C—íÐÞ¯]ÝÚ%v¹Ì¼¡ä!1» |èÌÄgq‘Ed‹\+G …KÀBϬ‘ØQÇÌ7rHÎhnAU ØéÈWÝœ+^ÝA^i2@LÅ´DSÇùŠÓ:½ìZ)š 2ä:•¢ù +¸M—Q%E ‚ÖŒC@„Y%xDä†èpk¼ª¼Ã¸…ç Hþ~†ô¡š—UÍ ;ù& +šÿ vf©›€- 4˜ ²ýê™ñ‰9Q–ænlpEYЧžmù‚DM›¯rÇ2tø#îèQge¨tÿJœ¸!BžLšåú§á ’3È[Ur‡k,œ O´:&mì1m¥æ,7ò0¬SÐP‘W%  p7®t‚€ÍCv"EÝ £”svyxô7ØË’¶Á?þqnð·§0Œµ„ä>þø¥1-DðŽÅwÜq†c:Np0¤í©äòXKày$5r^îzÌk –¤øyTgèCှäö€I!ë˜úkE…r ý±¦:`<{œôpˆìN 'µ—“:`8Ky<^aüÆåÉN€ssn½´>ز±º ÷ääÞÅ/ÙøÄ>Ë`¸KŠåÚ®„ãÁP¸5!‰Ø‡æ¦›ÆF7nGž—Ç L5éÚž¨qô5à(@C”ÖÀî[ÑçÛ§’™àÞQëû†ÚŽ}Ö<#žÖ|ðÁ²O»û ò}¸ûîÔà_ÊžK€šç-ð¶ÏàK!GŽØ"z2—ÃÐhý~`]½'æDô1mm-³ˆ¬ û[È“P¢™Úý*¼,pš5D~KR§&lÃßÒ±KÏ EôéûÑG„L¯‹èÃ,ûhúû†A/¢ïKºß;Ú÷qwGóŽ‚ú÷ªˆ&>­?ª^õ†œ¨Œk<¿üËŽÌÊ6e¡*¹JÖdŒ´fÎÎŽÒ5zUʯ*] “ÒÓCZ.~ÄÛ3èù5ãy©ÒΨ™j%g{(¤³‹‰¶Ó»kSíÉëè;úGk¥hþ›…a»ðÏ=^F¼ž*Š‹Á£†÷Ô<ŒDÐ0baZZãtß}Š;ª¦æˆ½à¾\›ã_ÿJ½m ?é OCtìǤýÑ8ù½éczšâ§Î}ˆžy}öºi÷¨òé~Æ=÷~ŒèûÕö1wÊ÷ ˆ}‡rLš>Ĉ¾¼,¢ß&þ&õ‚†>¬ñ¦<Û}0Ö8û^î”ðGÇ ˆA&©Ë„Ôjxy5ðJºscPw ¡Ê1¨ã;Œo¼\ ¨16qc½ò}Ú7fðÅÐ>}ìŽXä9åóóì)uÿú ø×›W?&êLj¾·Ÿb8_P@\€b@Œè;WŒèû?ÞÑåP¼{4têÈ@SåÀ#wu¨¼bNWœ¢o#0JACß«Øk8F ˜×ô(»yX@C4÷©ÞhLj~ô½g}ŠŸcŸ:g¾Ç´|ºœŸî÷kÞçѧN¢{Ÿš‡¨yŽoêßÝŸ~ÑwÒËÏ¿ ­?Ø*¿ÿÇ\Ð0ë±ù-ébT»Á ’ø:’Š ücPüÓû>%=\FvÙ‘:Âñ{uÛçÉ×B¶#Öžï3°ª¥Ó½\•ä£~÷çÌG4{¾¶z®p·É@õ,oŽ>f.×ÿê4¢ïLðXLÏXqýX¦ïƒ¿1¢õ«"úÔ¿o@Û ùü#ú1„#úÀDüË—Àµc ÅkT@} a°&¡‘g^è›$k§2SS˜½:K®å#²õˆÁ.dcûÅÔsw]xê ukn§n9vVê³g·Ï;—ôÐP¥B¢B Œƒ¥ŸtiS²Ú!ÇÒ:\Ó®7ç}ãêäÔC“;ïJnº)ÕèU}Ú—ÌtˆX©ï3c¥îßX£#ú´»#ø—¹oº?jz*ß__²øc,€Ž§éÅx¼}àHð—âÙû…‰?ªn¶ç.—ií¢ºÊ|áU=j¯7Û´È—<ñÄ_÷©›eWKµ¤£¥ún¹þë©gË~ƒã•2¿x¾t  F˜7ïÏ1+UIaèù”êÝ_‚w¬ÈGô#3O¡øû£ ‹}ëŽ].ß…*è¼hNë±(#Ïf©tê(+ÊñËÊÂ-1£DškEwæÕÎæ4${/¾8Ýv¬\€þÚQ`¶» L{ÅæUQÂìœÐÁxcjR( ’µ²Õ¡.©Ö”¯1ÜK 4´ér×f*Å–¡^4Ý—LË®ªJ`væ(áqÚcѹëläXóŸ »[VÇY)ÝŽ]b­»S¢Þõ‘ˆþ't¬:NVVêû”2xõÕ— ìþá º¢ÀŸiÐòófEƒ¤:1Ü #´J8Åø” s?]4fÏ4·c=}¶©4´Ó§ÔïJÎÜ»2éMš‹³g[º£–§‡$PLÁç¸ÓN›²#8ö$¥€F n"à î!bRy<~üžŽ<'f·CS„EH$1CÇ’ÜüË Áañ€Ú ð´—î¾ûŠv0ƒ*üõÚâóÏßß´N[°O8¿ÓŸjÇDi³Ø§Õ5h¸…Ò`Ô2 r¤Sµ´È(Z¹à :Wq”£1c¶ëØŒ* Š èrL)~¦Ü«þ­ñÖœÝôuI ¥ËóS¥Qg«sØa(,³ÛlAø1ÛW8ЀÁMG.‘‹,òQÔ+j‹ÇÛݽËÀÆV_p£×T¦Œ[ 3š«Bfb4¨}úÓó«…4^¶ÚkÈÚ8„?ï2o¢­Oxpæ+OÃI&·ß‘¦Hñ£þó|ÀuÊîb³b†eÚAƒ…<磌Ž%;Vž·à À8ÇfxòÉI¨Þ4»!ܬVkWlXž­hpÉIÒCÓýkL¤·>{I‡¶L¿¶/þºˆ_Rg;.ö&s*3O·Þ·Ýv#,pN¦¤>\ÊŒV#Fø'H*Û£¼#C'^*Tñä<©@ƒKƒ`bŸès#Ï>;™Œ"Þ+$Q© ŒœK[¢¼@C¡éáóÂ…©–¥)œŽõÅ€¥†èÆ„[‰#v­¬‘N©²ÇiÍ=L ›¹¨GÊ—sä‘;ª2º‚r¸%[`’6ª„§½GÅTÆ›Æ×(¸ŠÛ ÝJÈ¡–4 -tI†Bë ]Š¡ƒ¢Pd†ká€qÙFÌÃå“ „ÉXÏÓ+Î0~á ŸÖã›xÇlGº#vÁc„Uˆ$·e‘î!±îê 5Ï.on’”AƒgéAD¤„zQb˘4"hÀÕE£_Üû4Ö†®b^RKˆ·ÌHC£ç‚ÑäžbÖS«ø¹lɬG#ª&Øàjñà±Ù¥#?RÙÊ÷¤):¯!Æ)©ºg"Dí¤©|gô´ E“v3â|_hS>J5¢Ö3šÊ3ªTKMõç>÷ *0Dè=‘Îu¨Ï#Bëç›\«c6•;®V‹êtU:rYÿa¶¥Øñ]•yƒã”Í lk£c ¨ ŠB¯ü‡eÕKLyQ ÔA)u ëÙ¡Âê'.äP¦šõ`¨-]âÆ‘Ý5Qb{ßXŸqG[V—Íœ³$¢IŒ1–.6#iÀ{Ì –Wõ^d9 bòÅŠä€aQÅI{;eÐІ†°Ò^’4 6ÙTÅ–‚r’gž^!,g†–•HÈd F"æÊ¦Jñûk½ó‡Ó÷䤀£c.¹ä¢ÄZ¨Z¬Z¼©óŸfbf}ë­·â%—L]‘$›¾ME£ƒ&Ä Û†R$F¨Oºe$¥ÈÒ# µo ÆÈ.Ò.ù©†s1¨P.Úk“¡‰µízÈs=±-u^ >‚Gߘ¼ðBÚ:Ë º*ç"úØß±# •»Κuœ›­Uv$´—^ú3¦HŒ½f'bÑ2®-3kÙZä!aè°Z]Pí°6L®)¨o¢Õäa7s;«„  DjAök ­‘Ë¥ˆV-“øƒYªP<¡÷7)ØÎ£¤éè- ,£Ôf.+¨Á÷ôiOÔцËMQp¾Ìà!A݈è:@ÿ®ˆ0ïoxEÐrsÂhÀžQ4œš-÷Zšß4èq¢í$ÙÖ!^óØc—G³°±càL”I“Óë.3aÖ1uv0îÜ5t¿¦)v¯U`Ј¢¯yê©Ë º‘¹¾¹òÊ£‘X#/£Vºà)6³h!;‚ü>8Žt=±'…o£af5Y9Í<àH™Â(_b‰E`O™5läUÔO­¢zúÐû¬g7ùB,fük±¹5€=Þ¨äR¶ 4Ï©[ H7\Á½ÑŽòÑ Ÿ£§A&žJ‹ äÇj}:¸™G¦˜‚`‡oxƒ `:˜¾6° …ÅàÚ8H¬íܹfþü«< `iA]Æ»“ÎiZUr¡Yl^¯bFøT@mÁ€¶6éb“n«^SРaÅ`mÞvÛ™à}÷¿Új_"x= %ì6Ýôë¦mX/?Ý)¢Á a¼âQFh4èTùÆå@pÇËÓpšƒ“ÃΦ'®é-2á•WX)×h+HF1„xÌ@Ã/tþ»öÎ;ÏÆcM¢¶>šÇÙy)ÜÇcŒž`’-ºèÇ÷ÛoëάŽj®<˜àÃþ ¥H;`å'`Qïo``XºMÒ›¼rR8€´$3;JŨÛJD™&Úòì³SŒXM:BÞ?:ÞÆT2Ððç?ë–y=E% ÎÃ.†•Ï%ÐñF2"©xÞÔÓàâ©Ñ `Í„ãcP:‚ÂÀä Û åÇ?þßÜ>bH>ØVåbï²û=<‹Aå…Æ1ÆÑ)h°6è]N [ºî5PÚ)D¶œ:8\›Xƒ/A«‚wŽÉ@ï]lÇa÷ƒ"&à•A@1yÄÑB ßHß3`ø€)o& ÉAƒ/Ù]ì þÌ!¾Hˆ09è ŸS½nšÿ˜Ÿï¯ë³k}ªÚ=©|º¼/Î+À5ãt*_1.NR¸÷ƒZ¡Š/|a*Üq|ÃmÓ1û>&BFÐ0•†1𲬖ÓFöæJÆ ´rx" „9õ 4ÐâÐRL‰sy!ìÒ®Úd‘ÜÜãk FŒwÙ|?T>ém4\p4¤­Ž†!ä°+äi€Šèæ®uûöÆâ„ ’Ü)mØ` A­7°ðqÇW5h¨Á>-œÆÚs¥‰ÍlZo:†dQµˆgi³f…ˆr>î s ÆÍ2%øPÉ@Cl‰Öhà à´€@é阢( Õßzi†Vé…&Ãæž%#Bf 8l|Q¦‰"²pÙ›f6e 3ÆVÀ TaûØ»ÅU…IßB»Ã%!g³=MÆ„£Å£Ia^=ül1± ÞwH¬Úä¡àÓ‹»·‚Õ&}> (2Zµ¶§F:Tlãi|,r„aÝ ` «:øåvµØÅN¨ÂAÜKLÃäî3æÖ3c…×Ǫvמ bGð˜T52ðlÅ©SÐH:¸qG gž†«’-g¢R$zˆéÉ£Âê„,ý-XÑFº‡Îf$ø—€e5Ù’îfíÊÓÀiAý`Ä 4g$ CG¦ŸH†`Ÿ”ž|òRª »¨ª”‹Ôpƈ-ˆMRÔ.d,Âï¡€ ¡äܢyåmE˜´]¤k”KñR"Š!ƒŸ5ìBˆ‘¢ÒÚŒ©UID°ûÄVX‚o#FÆ]^®o:J†%$@¤~n^™JX*CˆD:äÊ.æÅ§p0p¼Îáû6'"~c«0žWEÔòóL€CQ¹jµ‡ÚÌ)XÎð–ìx€ÀÁ ,ùì8¾.Æ%9¸Cq«:¦2¹Ílãvq¿ò¹šàcKbGЯ*×)´7©`uðõUÎç< yÊÕï7Þ˜rÕÔ¼z i•£yK_ ½éžÎê| S7ÆóÌÝP£’5,˜iK?U ŠÊq4Ûúìj„昪¤ì)mˆÏÕ)þˆe ñ°>ó=¸ À<¹ØšÖe˜:¡¬VÉÍ,¶»-mã²ýõ\cH†{ ”½“•áÚÝ.Uí èÝØò5 ÓØ&¦"çn-­ð‘ÿA[C£ÑŸá^¸é€•¼Âv/¸´OÁ FI„p¨ëQcÜØaÜÊ)¢•ÁOÿuk&qÜËXÛÜŽW«ÊŽitÉŤÝ44®ª,®¼2=ƒ†Ô1IŪ?¢€¬ð7˜dŽE+}¨¬ÐTlQý[éþâÂÑòÒÒgkŸÌ$‰8¢ «è³#™‹„ EY`wëÍŽ`ÌÎv.2¤ã¥µc®Ä‘+Íåd|ƒ$¿mÒ‰Ò¹¼ø²»ÂAvµ 4¢8·Mê$`k¹åùÆ]çs̳wöèq b3B.CêJØf”zUJ¸í+…šåƒ;,sÜÑ‚«8•ºDk¸—Ô€ÌܯA'N4Â4Wð4—wô8üë§ ¡Ê7ž]U_ü`àŒ.»(™ÿZÊŽ_¸mn¸¡+¢ÞžACT™¹›¬ŠÍç­Êò­Ú2E  0ðKÅš4Þ>Ðèüêlì8šµçЧëêûlLËk&¢{I  _MÉoíÕÖ>’®n<»€’œgKᮊY²B gT­¨º¯9»Áª{Éÿ›¿»ªñ¯ºàÁ˜dý;fái(TpãŽ@ ¡£ÀìTXµ»»w©ÀŒNîŠL`r$g~4ñ»¹eƒ§'á“I³x´’d^Õèdͬ¦=Wuêì\Jªö(†¨ÿZk-/d°Ýó  æŒµ'êê›N¥îŸ9œ!+¼ÊíÚ½ÉÿZ«Ëòb°j”òãY–éŸDí¯"‚†‹SÄ#xoÕJð4tñª4 Ø]™\Fª$„÷xÿv6cìŒ*Â%ƒÃÒ•†6xÆò´!–ôȪrìï¬Èg‘ ¡qUfqåõ‚†[2LXÉLyIÚTùDÜlu|spÊSŽNÙŽ6Õà†PIþýë‚+nÀžÑ€ÿÐ0€† ç§,½È°ï_{m7ˆ jБlÍ{ž_§¸x@Λu8Hä\4ª·qG`ÈACLhv9bmðäÿ\ü ž¢”Üwnrú©Éý¤XAC×>†¸pë ƒ1â}8f=~ªZ/Ù ŽxÅA÷ãE"d¸ººÚ>¼¯7^€†ÆU™Å•× cAõá˜u ÌN½ôõ/ðÞ^Xo·¯ÿJú¿e&“»¹ÈL&÷átuÞ{?ŸH¯.,€†ãŽL»ù ÚSc©ú]F$—ÚþÎ^=ƒ†xrÅ€8Ä"D4Êeˆ„ÔzoϾrCcy—˜X›ë'ñ$—ÀXïñë¿’¸¥Ÿ^µ€«:¾Ä(£*`)É£RÄQ}©RiäéÄœ ï¬_†R&»¿`®E •!%ª‡pvõK1ÝIð¯O>Ií?$vÕ7Ôh(ToãŽ@ Á ’©7 9ÈÙz´¸ÂrîÐ:¡û¥M:)5ÌïÂ?O`ÖFicšd‚¢« ȨúãË´‰LÃNo¤Ÿµ"¾R9Ö×wšz/ú£&â•WÊÙ‹U‡2z®ß_Ò;¶ùˆ¢˜üìQr% ¡õÈI§tn$ŠÖ^ßTms¥®Ç$€†ßì˜\uUÚë'¶Ô7Qh¨¹> WâjŒEñ6jò"«ÌåÚ Òo +&ÎPuÙ*`¦ŽE½@GÇ~w9#!º…@ø/#m&b#¹B†KŠ|p3t¼Îü‘ëqµw‘ÍÅô§K/=$°`uOxòÿÛ»ûWËÎêà@°¥0ÖØN‚C™¾HS!©¨}Q£Å´%%­U¬I‰bªU1¦iÑ ­hi‘ÐtBLÉ51 ›™{Ή ¾!‘h~H ‚ÁD´Ÿg­³÷yöÞçÎܹ/sæ0.Ã}÷˳Ÿ½žµ¾k=k}W}I-ÊkJƒ”ô(Å#´¡Ê‡ÎFë•QŸ*@•—ä¥P½ú1{ê2‚‘¾¼‹7G°jf›®¸Ô³1E«h3jOW±}Ê]ÏÉ’ç;ŸÆc]m.€Ù0Fаº&sù4 &‡R7¹ûÔúgñzᡚ¬)ïBéu—!UqG«Tÿ-z[iâ½÷"Š)LE€Ž€•¶,7‘“lNƤêV§Š†2ŸêÀ£)C r±êchÕJTµ÷í»¯áeª¹s¹tr9ÎWTB©TätæiC¬¦C¹;–…Ë/¹ÑhìÜ*䙲U«ÒU *›ˆ…VšS±eÕºháÈ~Sÿíoi²úßQ5l¯ª\}Dã]·*·c‰ª÷m¿Ôzl$Εò_8¤¼O€†ü@Ù˜ùÜs…jï”ýÿú‘bšíÛofFX…¬fox½IëdÃÔ8ˆXTüoi€fVé`/ ¾©|Œ I8®:6X JQJBrk/‹‚Úçúo71QÁ¬”¹ú5«H½¬ó­®§Ÿ>Âeâ–Ò3âæ›ÿÆ£Ò©`4÷É~eÆolª’t=pZXÆðCg=$i0 $M‚ÕèGJ³ é*ØPÊAU6{ý^Â$˜Úóp9†—Ë—4·ò¢åðO~òˆf•ާc¨õùMƒ¼õ‰Û[c„Ì&Rœ±Šà‹-e?gÉIª™Í…ÒTD¡pû¾ëI.»ì€‘øF²šaˆœR¯ï[G;ÍÒGÔÝ*jrÂh&| q¸:æ|PÎpÛÛ&šæå…¥ZµVß×·z®ÛÅbØ—6Å>&íQ ™vOö¨!¥h™LŒlÖý¦2wfÖý² .d#ó‰ŸÙ€ò´‘ÏŽ.—¥[3õ…›áÖ[¯£R Bˆ•X/o4Ȱ‘(¡ô½q™yä!L JQsmZ™v¤OœiÆiß¾ ü"eSƒG°ÄŸ…»HÀó™…c Á·Êà­ùrf0h%×I üÊ& DÀÈKÄØìzX]¨£Ýßçñ1ˆ)âH…‘Æã¼jõK€ÅF› @ƒ‘)Š· v.ÔT&Êœ¦ îïæOÀ¤d(ѨsœlöL…EXÅ ­,Ú†º-[´ï*ôSì}ê¨Ö\¡l‚pßüæ?5K< 7È z8´˜–¢øöB iyˆ7\yå+½<ñÔS÷“lÖdÚü//è>Ñü³…€SS|&íîF îlÍ#ìÚ»wÏ»ÞõƃÎM|Jn•ï5“<Ê<>œW1á‚•ÅZE4{ÑE¿a0> Ž«I: 䘪r! 3[#hX]“9޼¦ÑŽH“§Ž»Ïà—³d8¾V•Babv§ô-+¬‹¸’lVÓÚ·¾(C?Ì $uÖ ñéaKe¡h‹Æ:÷+7£"‚ `"6 ”X»všýÞÿ?W‹]£à{ðÃ3R¡€ÌFJâ£Æ@3`¨C~œØÂ0PÍ&t°ÞýB½0™h$ãáì- ’ÓQÉkÄÿ¶ÌÃp¶.JiqÉzÕ­)>º7BÖäõ´(Ö;꣢;p¢†~Rm¶ÆÉY«)Úšü’nȈà­Æ~Ñ‹~ý–[ÞÚlaOXoÊüÓTÝfSÿ¥ZëÈ7ŠGdúÆæJWaÚÌsý뛲² æ=“é¡H¸Ù LÛL•戺(МtcjNt…¾¯3Ss‚#æÊñ)kúK°Ì'v«:*4øš.w•È^ÑĦeŒi[±ú\Gœþõí(^ßÔƒ_:Y› 1lc¸ ŒUD‰¯3Avé{!½Ë9¶§ ‘Þ4 åú?_Ñ k—âŽØOƒ_³˜CN¶#'N|Ò†‚ep|Fcˆ‡ßûÞ«IŒ#l°á…ÔâÌ+=ùä½&ÚGñôÿÐáÚbVqG²aøÆ­¢ø`…æ`׳y²À‘•zAÂ4ʬ7k€‰" žya'çs”ÙWÄ®@ƒÀqažHëÐùV‚F(Ó¬¥ ا^äu¯ûBœM§0Z¶Õž‹„Õ–ÏoN`‘fxS›H!±_ŸGñ!|ÊZM©«Öÿ4@' +œ’À™ˆ(YÔÛBWzãµ9„ÛrÂM£_œ¦; å-ÜÙ¿Ö¿éÒÈÎÔ‘"K¢hL=t £é]ÝhVQ:'©£6!ïºaíGËYÂŒå=P÷h[­wбk­w„.”»~Íìíϲ¯T(C¡¹ÿŠõh2ù„¿&Ýßø‡˜yJèŸs³2UÅe­u›Ý¯—Õ§=pBì …IkYØu©)·¥(€ŠBŸFíl\Âd¢‚ð_8†õâPÚb®ïïu:Ÿ­rg#Gí:¹Ý9…Ÿ˜ó*ƒ¡lš˜=)¨q^µ` š¡X «èB9Lí:‘@KÎaððR›.¿°µ ‡Â%f•ÁfkØËæéãÇÕè*ž¬#fÒŠèÎ<,ñµ¯ýq饘'(ŠÆ¦ u7dѼ;l'ÂS€ÿépH‚¾ÿð Gþ+œg¢hKËù €Xèéi˨D2S·SVŒ$ˆ®[°U" @ƒ‹Ô}|VWiÏÆ1µT+]í†WN !¹¿ ‰¢¢µF#<>¥ïñ×°)|išBe’³ß$‹Ý*hðí½¡Ödp“Á!øŒà4dklõÜq3yR ¯«®úc4§Ì†H»5àÅtZÒÓŒõâ÷1›W\)¹´¹":Z'ÄñMozMø¥idÖx«.ïvžî¥M¼B@‰uH›ß’#N°4€€ˆ h°I³¿ú*þdUh£eqÙ<«7\‡ûýSsý¾÷ YëxISX]Q©õ¨eèÇF`.Ú5oêqîdš•$ÅtÞ%Ý>O€¼8¦U¤ñ:i¹LöÕ¯Þm†yÈêͰiIÐ@ˆßóØK/.ÀÖù À€W3Û䨒#4D䡇P¥žˆQM­+ªç ußz|hâ(St¥¹xEVÝ«^õ»¾‹¥(ý"»õ'ÜÄR^ð¯åAÇ}ÿû¥#‰¶~`A‘ù¾Ï>K:›ØÕV×dŽ#o@•mq!q¥vE×D.g­±-mPþî»1%?Áº0Ôeb]ðüBùÿØÇ®×U˜:¢”,=ëš}Ã.ýæ7K›+WÚ.j:M׃ÜJààRnülñÅv°YàC…ɺ ¼°¿`‘áTÒ#x V·HƒgÁ d<¼ói?‘¢wn4óI½PéìBâ6ÊpPŇ‹X®‰ŽÄê5‰àà’3*cîs¬yŸ$ º3·Ž,‚Ìá#ü Üîâ°|â9UêPŠK/w£±ïZF”èv[—$Ü ~H®á»³aiL½VÑ~‡Åü×r56´¸\ë=¥t”aøcûç™°¤‚,<Âíª–$NÖÅrŒ¤ Wüã´Yo™ k/ngÁ "Tk{ ùúh¨Ï 4Ø*H¡•퉰/ K¾‚wñP72W =»õàΰì˜Pa–«n°ßú–ÅyÒ¿TˆÏšc:Q–4Œ¦wug`¾=a|ì±;Þö6ë‹h-Ð4ðAéFJC”¦×Ç’ááq±Ø¿ˆ@À4•øˆ‚”éŸpîÝÐrã9X†÷Ý÷OoûŸ%hp&×KÄ[FÔÀçY 0é%ª•ý {icÝš50Î.“Fí´ /÷Žwh1úóôÜ|Œ™ÇHÁ&ësO-sÖ™1ÐÄk6íkÐÐËÖœƒª5â ¥ï†W69ܰ°/4jçE¸Xÿàƒå.cY`Î4-ýƒ|´b\30Ó¦ K xÛÏh'f¨—7ÐÔ<ÔÉŒE';ö “¯Gæú‹UsâkÐðËÓnJc¶Íƒ=Éh?öѪ/› —3R]ÐÐÏRlA Û±‚Ê9¨7µ'@£²æMzg@³;ULñ ŽÜyçZ= 0«—zöY­VŸ-¶«Å‚ÔÛX[Ûž˜c.fOB\ ›í‰C€L ÁÒ2ÕpyÆ—øzÛùŒ¨‡»íå½ïdƒ ÒqfZPHœ?Êœ ¹À°MBÇ„ìÙó«îP5) ÁÇŽ)½ÌÒ²™ÇÃÖô=Î/v‘ˆÛ¾‚öí{1CîWFèØü þ_xá m[8Ó‚eìR˜=] U¼¤¶èVˆÓxp¼ ý˜r) ˆÒ›ϵT¼2»˜kÛb&ÊÒ•!Áº1öÄ€8³wq„X\}õ‘¡BúŠ–7 "œ(1³,_»?` P&3 w¶M Èš@[<’lArX$Ú}Íâ 1÷¥–SJTSÝ&Bª úµ0Lš¨)4À5΄TmhX·xEO<ñIó©¨Ï¡û¨ÈÏ*"eü\>`üÖŒåÚáÇAÃêšÌq䜆µgžùLt¹ëä4XÈì [{ãWÑ&å`åú—‘¦U¨*‚*wŽ…&x)VÇï¢ñ­îl–^=)ÄHeÑW‘¶ViTÝpçz;vx?ÌV h¹BÎglØBO¤0-L1N‹Z¤Á9@ ËdŸ›ªÙ»÷…™œWf˜7bO–êÎxÚ)^y¦Ê²Ù^04ŸÐ‹‹§Ûtú-pAܨšA·ÛÇ04ü·¸5Eê>¬ _Žþ å<W°)T‹ œ˜%¶“z÷\j™z§Ö¼šY•—;³Å ?-dŸš-çó4}‘J½æ)uÞ(í'¬ë)àgr@+@ŠŠ£Z[+c`æŸ×'`ÀKûhoàÀ¿`}îƒÒÌ È儯|å?ݪñšä×?DuW­¿J›ŒpÌ|ÍÎöD$B®»ƒí~#ÀõõQÌ%Ÿi"Ó™óîˆóì_íLùoBÐ `Á'7¢%¹ÅaËD"XÞ Ï¶" MF×N“]ØDP“i0P|ô ‚?Ѷl)s>‚†Ñô®î ôK.cK¸ RéÙxe–BM• sšÚò¡¬.²5AÕØvÄ*fo((K‰6h6›ó(èRª’à4§Â¤ h­‹í`·‹ãLŸK¿EØÆ>ùHÖu–cð(<‹r£ñØT*—·í ¥ÍÄU@£Ò6¦Eg3¤jg5`¾5Å RM^“óš¡~±Í®­ ¸Øf~ŸŠ-ÈBMõÊ|qÁTPt WäúÍ“ñÁê £\³ô^vÄh©ÖÚƒL&;E/5y—¶Œ?ËѺöÚ+\Õ¤èÍæf’ŽÐâÍÛ,ºP®÷en)gÿ5W0–ébÔhuïË£fû<³hEÂdn|Y¿@r~wÀ® ɶK¥KS–¾ÞW’¸FýÖ0ÂSšnÌ“4Ž€‚4X*dÃN”ÓnÞŽL²ñ¦1_×ç3/ŒÅA“`Š&[`±G£5Ò¿… ›ç«n7Ò0ÌCqÄË‹ö0m]ãª*ó+ö2‚ù9ˆ‰?ÅöÞLªºñƒR„éLòÊ–·õ yÿޣ㠓ûÇs<Õñòh·27±xªq–Š—<Þ4_™Ä‘2Î-ÓùD}ºÈx`»_è1¤RÙ˜ëg0Âò¸îU³Áç¨Ûe®šó'é |ýë‡cx³ùÌwiÞÈg/Ò¼‘;$ÇÔlW¬šÉÒ7Ë&e[jnÕ‹èÄ‘¶˜"_ªÌL¾ï¢ïXOx)3ZÓ’_Ö/õxk «k0Ç‘ÿò—› wje~¡Â´:jõ Â\¸ÐfJÆ mȆ©.ú¤Yû[V˜©¢ËSƒmBa¶ØhÍÆnv nµÙ¢×œ-rO‡ÇùZh@ä£É¨kß± •|í‘wÕû|ºš'ÍS;°…\Lå)1æü¥:†¹…φ¨Ñ“sãrZÍéÅYô†cþù†Ö¡²,©Zg:¶kjk;X†‘rÕ\;7i{6¥®ï hXh¹·q°oAOÍ&ÙX°›ŸÙÝ@û*luf×îè ËœRƒÄŽ-‰xer€‡¶xÃ{Ù1Ò0ßÕ͆[,3—iMÙm·]Óã\Ùé§lJ3Ù¥àë¦NÞÂiWpº,v·pÃm\2ŸÆØrO6[ðÕÝ‚ê¦>ß­ÉÜÜ»ïÀöÄÙyB²Cc›>W¾Ÿu"v·S«Þ´)°ÊÒÍ Ö6'¹º| «k2Ç‘/4”å#dX±¿ìÜzyŸ<Õ.Eèâk-™¥UO‘­¬™}ýóu#hMïêÎÀé«'vk]Ëe»ë®(«“Ù /rÀì´ø¹ª ?úÑk“Ìñg?+}•ëwù—6?à‰ Ôy›ÜBõhê½[<9{–\¥•Ê•_E+œse?â¼ÔðÛ j?jªð6 U–~tõÕçå¤M‚}AŽ ›ìVéš…¼:_ÙBîÍ.w­*¹¸§ %ªA¦M5Á\Ž:u‰j`ãAaèCùeÔ—:‚z„õ…9 åÎ vw5‹ç¼”¹Í+8s «k2Ç‘wyPëDéøp ”b¢lÜÍöÏ*ª’ÙN=69íE;ù½{~©ó e5«ó’Ô¬<VPÜúXÚTù¦‚v Q/Ǭ@ª¨Ä£µEc÷FØS˜‹ÁT ú»ß}ÕÏ^*憖¾w mï¹MÞ_‰pÄUfB)¸²5ç+ÈDipÎ%ko) ³²Ê[ AµÉ$V‘*<Äsp  Uy¨ºO„(ôÏw¬ÊU“È“ 5¬vˆµªPu«’E“‘Zaծʑ#$5Q=Œê§Ð$Í÷ãÁÀ¸PC·J¬Jª€†›ÕÑbûÂj 25jm'Š0+¡Â¤Ì1&”è†RFUx*]M"„îÝÎÐà_´;“3‚†Ñô®î t!O=z¡Y—² 1T\ÂTf%=_ËF8ê‹.RšO=bóÍ8¸}ø3(^,eKId IÁ¢¤>KÓq½D³›uþ…ùÌ3¢‡BèReã— ,*ÚÓ…Y@*6Ý+´öI¼L%–l 7Ýô×Ù™"QC1”lЕֽs8ÅFÀÞêSÿ€ !AÕó¯P `ªc¶ƒñL(‘£‡QæÐÕ8'Âa‘:pàB|tµšF§ÛœswÔΨº7œ­‚Ÿß·Äªç t /dáð‚pñ£d•ƒ©ê¸8ÕK_ºW‰ Âcf_)Gg ¡„@ÜÅ ük‘ Ö ¯hÅz`o f‰“IlÚžÀO€ô ·6a­ˆˆ\´ênÌ”p¿â¿­‡F O?qªLá¶m#D÷½:刂óXMt$!#h„_Czó•‰+¸ÀFаº&syÓ{‚Ö¢$?ò‘¿Å™H­ÑuMžÁšÀ'}(ŒŠHLÔvKÌpîßÿ[êÌí¥r¨ã‚ PÕŠÞ‡ñF@”½ Ý WRZŒg‹–Fu†Á¨ . åÆ.“¬ù5É ›Í—«ì} xc‘éíÙókAB1©` Ýq¡#ô§ì— ‘à¢É#E^´_\@ƒfTºÞZ+tRP]]Ѹ•y˜¾¥10»AeÚ5wŽÓØ„™‡m€VP¹­$4Ù"hÐåò È¼ØøýûS+HŸ¹a0$ ÷w‘JÎ4Ñ‚‰/9x 1Ie­!B©çÁŠ&L¿cµJ¸ @±,º`CŒÂà?JSl ï%/¹"±ë6ˆz)¯Œúz}Á ~%¾IÔŒ;o†¢&D&ê¡"âr8ú{'Gèl ÿã( Ë[#hMïêÎ@p«‹ fD©M×ÆÛ™@ûö]Àã§ŽÄ_©)~æG‹ž^H<‰‡)F^¶øä‡¦ÇômÁB[P§®Ú‰ÚŠh6Æ}2ZÞ$^´Ë ³Æ¶9O­0 ß.Šwç£Ö£'î°âз‚²F(øÁ§2BÀPi{ƒÉž|Ã;1ºÒÐÒ?þqéÔå1t~Ý„©€¾D2¾¨s¡Ës?:˜§× Ð'øyû}ªFGîlÍÀAC1¢+¹'ÇÁî™ÌƒeëKè h ¸BLÐro_÷0òíÀ•S™‰$[B"ÎcbG°  ¦¼ÙÄ…ÙÖ %¯ Tpç[o½&Ú"Р’­ Ð!èHûäŒ=ЀœÕZEó©¼ìeû,3é°Œb„Roü{ÇÄ<*} B9…¦²Œy7#hX]Ã5Ž|y3ÐlOdj‚ðª‘ÚÕò^ö‹/~1 ëê£%+  r˜gªÒ>i¶%“è@Ñ~ ÿ„ ˜m–Þ/-ÇŽÝÁ‡9‚'±€…Tøë g|'Á¼ÞYiÐé2ZC;)~hN›F¨}ŒðÆ{î¹IL×[!·JG+Á†Ðý¤ø©AÚe¶{bÌ‚"ÐP«î4ü»Ð¯à„-oA_ A¬Ø¦f¦×ãûlYÊå)Às.± Ð_‹Åí béߨq–ÀòD €§®¿g ¼^·x§‰D‰4'<Ñ›m+)Ñ7ö’K.–€cmˆPEG‰uÂJîÉ¥Œžè‘øiQ2èUkÇVèŒëÂêí è€bžã.±U!ÝÆ®ŠEh„ž{Ë-oŒË)T¡¿”õÙ4LZÓÈ*‚#ª]Þš# Ë3yã“·;DÈÉ7¾q˜ÂéÚ9]šŽÝ‹ÕSG´\tŠ*9 L2…¦d@°–ã$Ê«?H*X¬¨P1ÑÌcàniàVœû ¶ÿµ(”õEw ïóÔ¿÷½ì€ØPÈFV96Xo¹ª°"!K¿{*—bJ {µzSi%•­.tšÐ3ÕoÚ¦³=…ðëqé–ó­áH„, Á úæT|ÒZ1}Nô¥¢BšoO´ wÇ…“´Á(Ø­6®’Ì-Æèï²tò¶AÃÂL`] à\ ˜µt9Õ5„ù–Ò•ÄqâKÜIƒU$üeŽdgè‰$Á‚0xe Ÿ9AËzpÐná>ÑL6H#ìGàÙ¨¨œ÷€°çgÿ/é¸øÃ‡ Æ`jPf„â‘ìS’–5½ˆ®íår‚î‰Ý»-ë;ÇÏAÃv ×xýòf`s< â¬Ð_Hº_6s¢6!ƒ¬ `¶yYvcm P˜ §Üi´“~_úÒÄ ßR˜Å(  X°ýz ‰U@ö,Ü'Ã3…I—R¡Ñër¦0Å\¥yeº¥éáô$4ÀÑ— i„l¶€òÜKJŽB¶xí¨u/ø 4ÿ?ø–¦k mV^*®úÙ®y4E­Ï­¶™T¹Í¨ˆÎ‡T8¢Æ©Ó7Ñ4<µâ2ûŠý†^ÁÞ6ÏE‹#m ù`õÙxÑ`[REÓÌ[*0…¦ÚªKY×2p„9wrÖð8ÿG?’l<“9½–A/·Ö«s‹ÿÕïË™D=B7$£Â ðo.ÅQĺêÜ:Êè2f` Ë3yã“·;› *-Aê¨iÓ\ ,ÅP“\Nñ9•EÒ¢A¶ÐO…í¤@ñqÚÒ9MqãQ} –@P˜Y¹ÀHGÁùl!;èΞÕ¡¢ëö:NážjÜ£ëz/ç,{!ºƒØ­I°‹A½çæ5]]wíñ'ººfÊs(|v!u¸G9]íÃpÉí·_é™ã~ñ2TñLlv 4ÜÐü´¯Wéö¯m]òÂóëK&¶ÔI†ˆÏË;´Pž†nG˜Ã›ˆ7ˆË‰¼åê"»:„BÊclÉ»†#hØ®á¯_Þ l4d ô”ª¬Ö“©²N­0×XzyèM º3R˜=ºðÚRß!ûÁ~J"æßnÈóÏ™—êË{¦n¨«óHO]ÏT´]!ñ’4,U'ï"hØy($-ÈÖÆî4™,8ZP®í˜î)ÒƒGÖÒ¥JgˆÐ–gòÆ'owÎ4ì¸Â\“a{»»á—Û›xÐ>…‘êþ»Ê\TtVÂ/_)-sw`ÇåäLoxÆ áÿÌü¿ñµ%tEXtdate:create2013-09-23T18:00:55+02:00k4%tEXtdate:modify2013-09-23T18:00:55+02:00f6(ˆIEND®B`‚stxxl-1.4.1/doc/images/overlapping_merging.png000644 001411 000144 00000072054 12350112610 021252 0ustar00tbusers000000 000000 ‰PNG  IHDRl3Y¨ÅgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿ‡Ì¿sGIDATxÚìÝi`ÕÞð§¥) ;H e)‹¬BMeQ(¨¸°PñŠ 7PñEŒ\D\p)"êo/ A¶`AdkK›ã½¨hP¹j”%mÿï‡É6i–IrfÒ´çÿ¦i’ff~™3gž9$J”(M bhW¥ùæ?ÅZØD©]ß¾>¦!pR¬M”Êuú¶è!° l¢4©m›À&J›ú^Ul«Ä ØDy뤚ØVö+X`¥¶#µ6M”Ø~m M`«Þå¶ïµ—†Æ&{ˆè´hÿ/ÒâÙ/>?øB®‚O¿+÷/þôý¼ÿ'Ïâ}ˆí—‚bÕ lÕ«þ{WCMgÿY›ü¡“˯ΜGåK³ô;Îý’ÛÈ4¹¼/´ïêL–—Ññ—§m Ÿ®K..øAl­zÔ> ãWßRùߨB<ÛÓ@oÿËþ]•¿ßÀåb#lÕ£þ—¼â[z1/[ˆ‡"`Û ÈÇèú‚(Øæ‘ë:_‰­ °Uê 4þÙóÿÏÚŸÀâ¡Ø~ÕƒË<¿ýÒ˜ÐÍÈ7ƒô“_Qé0à±¶êQ¯è%Åò·Õ{ˆÈ Àú¡Y>lO£¥ÿ½,a£ñ&#"*FwŒˆh ÛY/6+ÐLº) Ç Ò³ÿü$6‚ÀV=ê¬@ÃG>ÜùÖ5˜ZNDTâ½]ñ¡; ÒÓ.•þ7HDôÕy.x~ëÇ‹{àßDDÔx™ˆ¤Ê"¢B»¨|á^ê¡ÿˆˆŠÜ¶êS_¶‚§n.%"*Ÿ `qȇÎt:Ÿ#"r_ è¾!"r_ÔÿˆèÃLÏ/×Îô¾ÉÆ–=`!Qy7 á„¾W—Q\å$"gN½/Å&تM¸-zl!"¢Û/\ú@…‡~º%ÚÝúCé-í ÞØOéî¶ÐzÒQ":4pˆèÏq  ëƒ´ï À‰hoc Æ]gˆz nÛtüÜ©† k[µªß·½³ã§òX Qß[ßÝÿGÄßøuóºãDD'ˆþ*\·éûr±ö6Q¢6Q¢D l¢D l¢D‰ØD‰ØD‰ØD‰%°‰%°‰%J`%J`%J`%J”À&J”À&J”(M”(M”(-°¢D‰Š§bÇQ¢DÅS›(Q•›hc‹û‰™À&J”À&J”À&°‰%°‰%°‰%° l¢D l¢D lb½‰%°‰%°‰%° l¢DIU °‰¥M™6Q¢´)“S`%JlÖ*‹Íir+<ºÛÄ~ J lƪŠÍm@®ì‡£Àb±X‡+ðçVÀ!vQ`ƒ»Šb3Aïm"; ,&ƒü¦V“Ùêð|r·z—ØDŰ[Çwã´ ¶ª‰Í H=­y>g&©|˹V‘[£[ìB¢ÔÇ–[%±Ù 9rõ`̵È^kžIzÄâ&rBÕµ ªÊac *6}UÄæÌä¶Ž_ËsÌ3;½4E‰R ª6·ÆÓ=€Ü‚ˆ D§ÅÀäÈD—¤(õ±åU=lFè?Ðú<6ÓÝþîQ¢TÃf¨rØÌ@g@o“jámžGœ¹:ÁàNú6U¹Æ[ä Î*†Í°¸‰ˆÜ6Cø+‰yz‹tèst6MmlÖª…Í &‘+O l[Ò ×ADDV=s²±‰ZJ¬ö0ØŠÖEÅfTñ=ØÜz@_@DT‹Èçmn«€Áê&"w.’ÝI"°¥2¶c ZŽŠÍ õB$IÀæ6—»|lQ>›Òì""+’œÛØRÛ±§[ѱ9ôê}¡'›x‘ÈeÖ0+±#55M6"gãävI l©ŠíØÂNí ›Y½ø„öجÀ[Ro¾·óCIŠ€Áâ:}^Rs[[ªbûpE!ë§[z!ͱ9€X Œ±­f0[ë&3·%°¥ò9Û튰¹Õ ‘hÍ¥—úrµƒÎõ,ž|ržÀ&°Åí.EØ(WµLcln#,q6 L@2»$¶j€ÍªZˆDclfOOGüGÆ<=’×I"°Ul.ÕB$Úb³BoNôƒ¸mFèÝ›À¦62ª"Q[¨Ôtu:ár˜Ïô¸‹Å’ë¹[5Ïb±9œ±­­dÆ‘6±å©"ÑÇ?;WwXÌÆÐû¼Þd±¹¶jŽÍ©VˆDElsŸ<±ÅùêyFÿ ¹_åùÇbЇ½—5ãV[eÂFj…Hª6›4ƒÞ”gs„úîr:,¹’Ec輦ÀV-°©"©>Øœf½4C”S3·g,cˆÐ¦ÀV-°©"©.Ø&z³Âl€Ëj ¯p9P`«ØÔ ‘Tl“-–Ó^gž¾"7­Z`S+DR°¹Íþ[Pc(·Å ¶ê‡M¥I5ÀV LŽxÞÅmѹn­ºaS)DRõ±Ù¼·…ÇÅ-÷ôlÕ›J!’*͆Ä=_`«&ØÔ ‘TulfÀœXÀ¡÷Ÿ•a³ošsÛ5C®¿ã©][ŠbS'DRűY9 ÈåÔû¬T‚mÿýY¾V‡…ǶJƒíåØÔ ‘Tml.=ÁïœzoOptlöùçÉB­V l•ÛU@?¥ØT ‘Tml¹|Q¶z;§¢b+1®1G`«ضÕìUˆM•I•Ææà•0¤›}" vÏ¡7ÞªðÃÕça‰À–rØÔ‘Tel~oh‘NÚ¢`k‡÷ClÒ%)°iŽ-Þ9µþf‘U`K 6«ôR‘±m‚)$¥u‹¶”æBˆ¤*csG˜‹*Æ2+Á¶[BRz!ÌÏ6Õ°Å[ØT‘Tñs6^Ý·F%ØnšÒNü[`K9l*„Hª86N“'º Û=9¡)"O`K=lüC$U§éÜLа=Ö(4¥=X)°¥6þ!’ªŽËIn ìœm{HJ/c³À–zØø‡Hª:6éúXbåÖ#W ¶-˜2›Ü·v¡À–zØø‡Hª86 ¿Zâ2BïV‚åÔÝBÒB ×ÙR÷IÇæ6'ÜIâÔCï EØîB'[…®iˆE[*bã"©âؤ[µµæ$eØö×>¨/䨼Lt.ØR÷I•ÇFfÀÿ”Å3œ"lìa}Ùè Û·>ÒÀ["õŸšØx‡Hª>6²"îqH €1àæš¨÷³4íwõä{'ß0¨àáJp?Ûwýºò#eO-ßûÔ´ÙïŸö6Þ!’ê€ÈfÛœ§né9.Š +™QCF­î‹IƒäôäQiaûiò@¥d¾lpu9ýؘMDÓq€háª&Ø8‡Hª6r[ V¥{IÌŽ —R2ºÖö±:µFÓ?­£kMc#¢^ ±] ¨•±†h:QùXô)«&Ø8‡Hª 6"²™ÀçˆÖžtÚr@/›¢M96ÆŽ¼qǨþ=~ð½"Æ*¶ÿ«€mŒ2l_;‰ˆœ?Ñ­BDTV¢±µäaã"©>؈\ƒg¦C«#ô˺ ,Ò|‰È-õR©:"òÜ ØÆ+Ãöïëd©‰Õ«7’wˆ¤:a#"—Õ¤÷NŸk±Xž²Y,y&ïÀÆÓ¸USlc°CõÃÆ7DRͰ9­fS¸[w &K;ügV„Í~hûúGª¶ÁØv¤6wjK_Î\C$Õ›´6‹Åd2™LzM&“Él±8ÑÖSTlÇ,÷ôȔƎìßj» ØDtrÿ)ÏÒÉÏü";ÉøæS»ìË¢ì»}ÎØ~Ùox Û9Ûª lAï-ûÃ\…޲ª€oˆ¤ºb‹o=EÁvì™Ù¶ìþb1_l¿-*}°Qù»}¤ Úã…µqhÖ•k6¼ïwïïŸ{¦M­ÎµÛo ƒmSÍæ%¢ŸonŸ““³…ˆföÊÉÉÉqQÇœœt@Ÿ““3Œ–ää4jåäääX©â{Ëÿ°Ý»ÐìÆ‘,‰Ø]ËS\C$?loçTøêì¼#¶mc2”O€Ç‰þ¸&ëµÃÕ^•¬ÝŽ«]D_è1ðœô„=ÓÍÒ¹ ô!±=åù#/û“ˆh/€µDD¥ |IDë×®m <±víÚmÄÖ®½è¼víÚµßSð{ýaÏáÙr¢ýzL}l\C$/lŹi@›±O½þ®u÷Á=-¯<5¹·ºYvnØvl ﺽ“€è܈zljˆþ¤= l%"zøˆˆ~ï‚gˆˆh BaVï1[ÉÊŽÆQK6úŃˆZ{Ÿòp©÷ˆ)oùfÏhSND´{S׉ÀÆ ÛçýÑçÙàl<Õ7•ðkF®pu)ÑÎ÷Êè9OGy'`”çüê}"¢€Ûˆˆh ôg¥ç …­îa"¢?úÀª«Û¹¨Ø*¼wà¶]¤/Ü^°ñ ‘l|°íïÖé½PÛ³ø©:£qÃv À!é¿5õþï@ÆßRÏ¡ˆh0’ˆ¨xÂ󼙡°M‘þW˜ ½ØÜѰU|ï€?ŒŽHû©µ `ã"ظ`;Ôùºpi‘m-§sÃö%O/ßFà®iÓ¦M›6íRûˆèǹëȃí"¢ižôG´®ÿA@­31a«øÞ-ÎÏ'"úí×*€gˆD`ã‚mL„Æâ6½…'6ÏïCÍ)þòŸWüñJ 7Q;àk%Ø–A:)ÇVñ½þ0:ÙH›z’_'^R±ñ ‘l<°½28RÿÆ®øc»ºW²ì¹]ç­‘°9œQ‚mç¤M9¶ŠïˆÖeh´¤´j`ã"Øx`ûä`ĺù l—„È6Ñ`‰›öHØ œR‚Í`wLØ*¾· mÓ@Ÿ’*cˆD`ãÕõÏ[f£.Q¡ÙÁ=ëe§uü^:Rõ&¢}ðvTDÁv‹ [Å÷–c£Sw¦¨½³*`ã"Ø8bëføñŽ*`»xÐÿÀ_D4éÇÈÍ`½lÇÆ¥1a«øÞ_gŸ> áÿª6~!#6]~ÈmúÎ*`{hyÆw$p–ަ£;`; Ü©Û^σ*¾ÎVá½eØ^ûžˆ¨|>€UlüB$Glðö¶=6yíùxy5°m0ÇóÿÒ-†ÑÇžÞÈËZ¿ø°M‹m ¤\ÕEÀ¿‰ˆèD4lÞ[†mÆÒ¿ó€Ç«6~!'¶0Å ÛþËYî@úR""ú{L›3D3€¬3DDóöDD«Ì~ùZàêpØÊ:cÝLõ´›€£ÒƒM€þæú„yïÀ?ŒfŒ–þý x¯J`ã"ØR Û!?yþ¿ †,ß¹áÉf ¥î-¥ïh düUVH¥Ì>KTö\PoSÉ9¶{¥ÿ-Ciü‡…@wËé¿S.kü_‘»ð–÷)Omü·ì›}DD´õ~ªظ…H¶ÂVús¹gi¶oX¡|"¢“çhÒ*íþ“i@ÛÖ/ýÐ@Ö¸ Ûß Ö¸À»ÏV™OÿBåßþ3Ãè½­}6.ûýB­7Ñ WxŒþy!€m¡ß[ö‡ÍÀ‡‰èÜh^¶dcã"©ÊØ\Úcë~Y…º´/l³ZºMü­?ä/vx|C¤·ÈÂE/^²÷Pk›.lpë†3D· ¸á9¢ ×7 ¼õ‹Šï-ÿÃfôîVgäì:vø˜ª6n!1=OlB“p ·fdP•®]StÖ·ø÷ÞUʉ¨lÛo*ñ¯ïlø/Qñ‰ÏvÝlùH¶o•–¬Ùwšˆ>;ó{ÔÏeäþjËL¥É¿eý­>6^!’dcV-œ°ÆÅ ß)H”`{<ä65©…­º”lµÏ-–=öÏÿ¨Wˆ$ÙØÐ-èK·ì}C­yn.؈×,¿D&iòhØ ¹MÇl<±ÝxÈ,½õfõ±ñ ‘$.ø6¸òLz¿\°™¸môÒFˆ‚-ýöÛôª‹6ŽØÐ´g?ouÉDƒ³êcã"I>¶-+VßDïÓ<°åÁÀçÚ¿Õ"ˆ‚­ÑèÛ´Ç`'¶ úH}lœB$ÉÇÖ~qÖ¡àc[_,äÍ¥ç1¥6‘[ï鎊‚­íe!çÚ8o´À¦"¶¨Sˆ¤`£åƒ?ÉýÈ.ã€,|Îls¡w+ÁÖ·Q¨[HßÃGlmž[¾Â[Ï7\µ§Hl|B$•­ªÔXàË %†Œ0&~ü·ù.jFÁvBÝ“}3^Ø8bÛ,;㸪\ƒN!’JÖÕyUöÓIÞÑØÅæÐÃè±-¾¯µ(Ø^±âýD‡|ßNºð‡À‡Êú½¡6>!’ʶԞ~. ?·¢ô#*Åæ>2fR†m¿®âA¬ oô‰À÷NJó¥,UÆÆ'DRI°Ñ®ºÝöø~ø€c|°‘S}_JîÜkQÇú:›ƒ†²ÌØ8î¤_Ë»ú? Ø<*bã"©,Øèóæ˜_NDTþzm u9'lä4¹ñÎKkÓ#ð+-¶÷€&¯É¦!4<"°ñyÈí¹£}Ð%×î™ÀÆ›¼ÚÒI²±5> häO»âäOüÙ±ÞÀc3ЛmJÖ˜Ób³+â'½=\%Ûn,`›ŠØjÝñ3i‚Gˆ$ÙØNÿ{`“Oâù³c¿[ÎaÖ€)¯ 8§5×F«;Ê'·=ß”áݽÇ}æQ-ó–§^ÎóÔ£Ù_FØ8„H*ÁÍ£Å|© 6"·Mò½Ébuõ.¹³g{£Åý“‡ßž_š>~ÄØ©/îgL`ãŽmiàC´9©6!’*=IÈã›5Wïo…˜y›ÀÆm'=)?—ÞÇ8¾S$l‰‡H’­fèy¼lj-±µð'í²4¥G¢^jØbhFÊïy 7h„-ñIÒ¯³ Ûùõ'äõãW[{¦6¶¡Þʰ’æê÷‹f$Glsúª=Æj„-ñIò/j‡®ÃÆæÕ~:ôáëèäó­âœ'6ô¾öF©nÖÀóZaK8D"°ñÁÆæâÒÕ!¨Ío•µYtðÅ&¯æ?k…-á‰ÀÆ [¨C×™« ÆùpÎU Ðg·èT[)ß‘VØ‘$ÛÅ#*ÔpC bcv kÙý²Q'Ý`ì×¹>Ü}LtýsÆÖàîù ¤zqÕÎ?ø¾Sdl‰†H’ŽížPÏ-¿"±±Â܆AÇçA«Åu6îØÖ«øN‘±%"I:¶WB>ùîTÄÆØ‘Ç»§ù¤5õ¾¸¨­¶’†-ÑIÒ±… rJjbcŒ}¾ôþÛF×ÿ}P"$j¬öÓÒ ‡e®rͱ%"I6¶…|ò¸þ©ŠMĵT^íDtÆrC‹td´¸ñ5—¶Ø ‘$[Ó[C>ù’Q›¨0;ÜÎ6¾¦zÖ2M±%"I6¶N#B=·¬î-›¨Ð;ÜG:@Ë‹‡£#ðp¹†Ø ‘$ÛàóC3žY›¨«ýTS-|Ct'Oέ¹\Kl‰…H’mö„xî½X'°‰ ¹Ú˜ø;Ñqœ$ZÓäO ±%"I6¶u¸¾â¿­­i,°U[lÃk¤[ÚŽã$u{SCl‰…H’í×ÌŠ±Óƒ£H¶ê‹­ Ò<ãŽHØÆMÖ[B!’¤‹p êÛå?),Q›ÛQ`±˜L&ï F“É\qX­bë‹.ˆmèˆÏà§6%"I:¶| ÙÆÀ8†MþR›Ûa 1$‚oh³Í)°Ufl·bh 6G´Ä–Pˆ$ùþLpËnoŸä—æ:^"•°9óŒ¾c™ÅbuøªÀb1{súÜp£K lÉÇöŠïã8N–nèŒÇ•퉎®å©DB$ÉÇö{[h~Ó óþïö^Ò€Èåª`sJÙés-aÇÏrX=!½ lÉÇvº•î/¶KëéÅšbK$DR †²û®kÐÁýâ?ˆ?6·Õ&kÔ€»Àl³C!¶…Ü”—l*`£U˜æÅ÷‘¦Ø ‘T†q#O“Y»ý4qÇæ¶è˜lJ/’8óôLEضö ¹)WôØÔÀFo¤/õaK»»T[l‰„H*Ç ­ûFê<ÒÒ®ÊWögDŽ͢ô–ØR«&&§l˜þÎÚuAµöí˱S`S­k=ó,GÛ›)Þ?xaK DRYFDþ}õSÇÜ1ûý_•þÙ1¼ºÃè­±_ùwäÈs+À¦f lª`£Ó/¾JDç~eÿà…-I5~<Ð[â[?`pÆ­“À¦¶xö^Ø‘T}læŠÓ¬ÅPV=ôNØ2t:NÔÐy*5tº­wl¯~#{lŽæØâ‘Tuln3BÎp®¸œzèQ°¥¿|Xò§Ö‚bß S»ï$*`›û†ì±þ…Zc‹?DRű¹Ht»À—ƒmôﱎSGøÔ½P`S[›Í%_zëÓ%µgi-þIÇ–‡Äç‹t¡wEÄ6Aú× Ù‘+p¯À¦¶ ãìR±Å"©ÚØHtxv""·¦HØŽl‘þ})K>á/š lªcÃV­±Å"©ÚØŒ^%‰•ÃÛpˆ×z½‘l¸ÿÝÀM}l·i-îI•ÆfKx’_ÃÁ Ûv¼¸8xO`ã­K¾¯sù·—GüWóÞÈøC$U[âsEúωѱ±^Ù³¾• ìØøc[øÐÞæ´Çoˆ¤Jc3$< ²ÿ»Ì¦Û»¨{Çv©+òÝkÒDl_ÊÖ࿚c‹7DRÙ°}Í›à5ˆ§Y:FF»Åæa­ún8°R`ãí„l?·ëBÆþÕÅoˆ$>l.µ°ýÐ#6šñÚÜVŒW‚Í«å?qOŸÏ÷¢6ªeEÙI‡W©Œm·léÜŠ;D6ƒ¢>¾8°ý2§NWlÜŽ–[=äöÜ66Ó³—\ò&°©Ž­30WelãdKe})îI<Ø”]½Š[Ù'¦L€'6âÕI”«cߘqóØ)ó·q¿So O•ŠŠíë¯S[}ÙI¢=í˸C$ñ`£<_¤‚¶ ÚàŽÍÆ ›A96Õ†E¨öØV_Ðq‘;îý#nlÈè|q?Oõm<w?w\ØÜJ’1`+Ýt} aSÎØL|¬9 Û²¥•+6žØve0¹éìa) åÚºbíOZ` ªK(ÞI\Ø5$•A2· þŸ–ÿÑ•/6N‡6ƒ2lÍ‹—–Øxbë d]ª»·üZ´é]-³G«Œm¬l©¼-Qœ!’ø±EkHÆtQ»|Ó@4Rá¢6‘ÓñPsçž«—а]#[z“6~Ø6b:Ñ9 ò&×{šuêakt"ð¬g:p˜(¾IØ¢4$cÂö­9P ›dÆóªqYô~¥Š°í’-]‡ù7lÿËKDt 0ѳ]•m»9=dûŠk×ÃGHuE÷L ¾4ŸY!’°EiH*Çvú½áiP÷ú×Ìj`#rå0Xc9w+0ÐÛä/K\ëm«6~µÇµ-'¢ãÀõDôwÛ½Gö¨ Á/j¿ì=™7h‰-rCR)¶#¹W¬t•öT‘Ó ¹6eÞy†€y8bǶ¨s@`ã‡-ŸQ1ð0Ñ'5=ûþPm$ÝÞ÷~Ç"I[Ƥ"ÿ«7 ý“ßJ?Ø¢6"—ÅFKAäKN«4[nAÅ—R†­°1ÆŠ_¾g½8gãˆþa*'Z Hã"컀ndæªcë:û…co íÇuIcM§úg;:N¶WBle®râ_¡°]/_´ó]ÌŽ9D’0¶° ÉXÇ ùkE -'…±­ÒW16ör »jÍÈб3í6yÎ)ïLGÝÀööutD:VêHW*>¾ÿ|@7áˆ÷wòÛ%O[hlg,7´HGF‹_si€­Q`;òìSÀ1ï®kˆ$ala’qŒ®U8U—ÊØ^F³l›°C½s¶L…ØìW9û|‹sÝ+þæh· ½ÓZe:ß ÞD·xwÖ^©°ílãkf-S¸áF©®¹¬1ÐÀ×½kˆ$qlþ†¤Ë¤O ÑW=RÛëÀ Ø,|Çú—æº ±Í”í£€:Þ‘¿ìƒ3|8m°16 @ßaOby%Âö‘Ðòâáè<\®:¶ ú§ï‘XC$°ù’nY÷L|#"ŸKalÛü»õ˜‰Œ±‰ø0éØ6§WÈ~ —§YhÆDßÏÛʰ±\½-ÉcÍï©4ØN5ÐrÁ7DÇqòäܚ˵ÅÖóM¿îXC$°ù’²îeØäóFîØF)Œ­¤ ^òc³MBæá$`ËJ¿-`i4€WdðoéË¡6¶…ÃVÔÀCÞ¥‡py¥Á¶ÀÄ߉ˆŽã$Ñš&ªmМR-üׯbÙC1†Hx`ó5$ /ó)ÃÖZ6oä–1©ŒÍõ)Æ´h `K¶&¸Õ¿`KjÉg~€‘1ÆØMèÁÂacohäý²°*ÏT«Žm8p´ÓÇI"êö¦ÊØz•EØñšcó5$ۑʰáÁÃ_x'H.þðêÌßR[Q§LO—Ȩû‰úØ ËÎÎn9`\l ô–¿×µ1Æò31=<¶’f–xOîbPeÁÖi_P¶q“UÆöð¿cˆ„ 6_C2PºBlAµ4•±±ü Ûï÷aË~ÅŒÍjÈsÆvd³·@Õal×˜ÔØ­å¹÷çàõðØØ$ÀJgT>‹ªÚØú¢ b:BelEøýØB$|°y’΀ nâÃ6 ¥±±ƒW·YÃccjõšUG6Ò œ·Øìwf˜KÂ7#»x4èÍZxŠ1v NÙ*b[ —wá`N%Áv«ï¦šã8Iä¨sƒÊؼ÷BÝ7[ˆ$ñ‹ÚîÀ†¤ÁÆ6eócUZlŒ­u„1–_Û>åïÞÂy …íØ¸oG:gkàé _è`c…™ÀÁØVÈñ.,†Vl¯àB?¶Ò ñ¸úØì3/®4è53xšÓØB$‰bs¥‘â< É€+ ±=ñ£·‹¤ìó‘KʉR[|û”oÛùª¢·ØŽ\‘³%bÉy‚³ýÜÉØj ÝÛ¼ +þŸdl§[é~ðb»´.^¬6¶¿îño–kä7¨Ä"I[w\F©!påA¶n¼JGO.MelA;þþµ17#ݶܰÞ*bË7ôÞ¹7²)€gƒ~¡+€û[*÷SÛûÚyÖ8VI.j¯Â4/6!'åŠíÌ0@.CÛ4«ÖvÙƒ1…HnFÚ£Óß4úÞ\¶‰²¥½x0•±uõ/ÎŽ§72¬· ØædúG²õ÷Tšyþ7‘±N z³¦æ1¶h Ûbý¼ Ÿø´²ÄµÞH_êÖvw©ÚØæÀ˜ g‰¦L+ÝÕ§‹,Sˆ$ñ§z§¯!ió5b•a;.[š„Œ“)Œ­m la½UÀÖ¸Ê×Ü^ñcÃÜ-¯â4ÿbìQ k$lsÜì]ø€µÒ‘×µžy–ˆŽ£íM‡ˆTÆv®1Ðèc""š2èlϧ)D¡7Ò«-zW@#6ޏÖNûSn´lÜ꯹YŸ†ê'9óT·’· Øž:˜å;²õõT²<ÿ316¾B¶ @Æ!ÆžÚDÂv%€e¾k1Œ¥®Á-6§_|•ˆÎýJ¼+¶OŸ’­iSoˆ„G×¿ÓØ¼ É\˜ãƶ ¨Š6§æØ*TZcßEÿ5EÞ*ž³-ÒÞŒxζ@]ùñöÿ\Ì{hÛ‘Z@ýÂÀ×9Ty°©V!°½xF˜2ˆŠñ]¼!> #`ó4$mÞ)ÑâÀVºfÎQŽçlîÛ"flP76À¢7r<ÐhG$l¬?€÷e_éæ„U€.Boä<6ƒ¹:a³ïbÛƒüxC$|.j»€ÕÓô^TW†m·léÄÿxvý»â™Ö'ÜE{s<Øt:.­Bç{|¶P]ÿG;Ý "a[@6*úççíKcŸ§GÃbÛYÈö¿òk@wV=±}ì Ä6=¨³?† §‰Û ˜¥†¤÷¢º2l7ÊÏ|ŸåzM·`U‚í®í‡ŽùëålÆ+Þ53«0Öld¨.’Pµ7ׯØ#`³_Ô<ÛºH—Bÿ½d ùýlûç­“ÅIØÊšaµÛ‰ûPËoˆ„6"3`&`uxŽ'ʰéeɳyÏ€Ëy`3Å>{˜ïO²% ¶ööн‘ã_‰ [˜Îùg®#ŵžðDlì`; ›ÿ¶²†/¿5x.àΜ@l›Úµþð*Óe9Êj…–x¯ëÑ”6€àQ@b‘pÃFfÀä΃Þå‰l)Ltyô™<õÌÔVÒøåw§uþŽ6kô™¿•¶"õJÎÙF†¹ÎvëtåØÂ_Ö–fðcŒÒ_€å÷zǰ|³Òõ|%|œŽñ¾ß:ªà¹Yô£[km |‘AhVT]±3ÔòìŒS Á·oˆ„6²ÆÓ˜<ý3ñe#g139`sxL©MäôÞÛk¡±½Uw‚Bl‘$òϼÀýŒ1v¤ PãÑ¢ðØØÑ):ÔyøSÆJ>“†Ö¾î|vƒ?!Rx œóèc.o  ž,/RX³êŠœ½»þêÖ“ü°ò GldŒ_ïHÑÿø°µ)'¢“5}s…$„ â˜D+D½Ý»±e#×ßÃclN Ü«›-B42ð3?ØÐÿ¦¢mcu cÛnj dµ®ô˜ÀòãtHG¼ÝëùÞ¸^Ëî7- ¾¤¶ V_lôû„Žˆˆ¦œwé³§C´Ÿ IÀFzè@9ò”c˸xø¨Àú/Ѧk;ËåJ×Û+³/%Ëľ=ÀJ¥G¶¡ÿ„†/~÷éû§?ñJÐ|q¹P8lÖ@¼Èª16¢£¦¿ˆèdÈh˜ò WläÔC߃aPŒí¼¯ã¼à¥ ›Kº˜PçˆÑ?V_<k­Av…Ø(7üíl*Œõ_|IE¬wäWª¶¨Ãä(‘ðÅFN=š€S)¶Q¤*6ïõöĬÙ^Ô=±Æ¶e…L)¶ÃÏ;cù̉–­É¿u¤m×Ï+!6·KlQ‡ÉQ"áŒ\Fµ`VŠÍª26r¼ø¯m; XUŸX#ưó£ª1‹Íú†ó£þN¡ñ‚|V ±EZW<±E&Gqˆ„76r@縑_~ǹsCœ7—ÐRl‰O¬!Í×zReʨÿ4|&ÊoÐËÆ*#¶HëŠ+¶¨Ãä( ‘pÇFnćmmë¼±Yõ€ÉÇ'±è“‹”bK|b §oޏû:ó³}ÜóÞˆg”úÜ{ŒUFlוšØ„èAËM6"3³RlåE›ßZá«{Ñ÷7îØÈe`´ÅzTÓW8$ª=±†Ãèߦö!•&C<ödÄËÕ›Þe¬Rb‹¸®ÔÄVñW”†HÔÀFfneövþ(íŽpÇFäÌ 7+¾ à¶,‘Ö–*k¸¬a÷!1óhðס•»·Ø¢“£4D¢ 6²6E~m\1á^{%lž fkÅe5!ô¡P“‰5ÂíC›úÞBaS0LŽÂ‰:ØÈ“"›dúùªCf¿~ýú­R‘ÛfòÌ™]öçvX¥9·õa®+GÂÆobûÀ¦¾·PØ “£0D¢6²)ã° w~q¬ÍáÞ)Û0¾ì¡)×buøgöu9–<ïLÛ†<‡‚µïı‹ íCÕ[|ëŠ'6Ãä( ‘¨…œŠ8¯1AÞ©.6""§5×a[™ò ÜÊÖVÜkÄu£¶Ñ*°©ïÍíÈ0ÃäãÄ›6…½‘SûjMêʲYÌ&“ì^h“Ébq¸bøjŠ{b¸v ½™;6ûGsÖÆñ´ý ÿ¥ðõß}`âÔ=·{ïŸçÄ™›µÁ¦7;ÔÂf˜œÔÀvfäBY'ªYlG:‡ò| Ÿ‰5âÙ{ x7#?{y\6|Í^Å>·Ì¾83ýîÆ.ƒ§¶Z~Â;–ÛìW Ÿ™6½¹€o3RV¡‡ÉqJ*¹Ø¨,®pGl±½mdl O¬áÿÒÑWØ{øb›~S'ÄŽmï ½•’ÙZwˆíiLeŒMÄÆf5ÇŠ-jIÈu¥¶p=úD÷,M°ypgª¶'Öˆ¸÷ðnFÌŒc¬«BlñclMf—{9Œ1û•èVÂOiÁØþwc³«•M$࿱Òc ;EHJb‹¯@ÉÞÃýœ­u\ØF(Ãö1ð6cŒåÊ‹c¬dS ㉳´ l¥#ÔÏ'rx&Ó8¶ú­OCv®Oøem°E˜"$Õ±mÝ+6g¤½‡;¶ âÂ6R¶»à¿¤P‡Ëøg#¹K ¶Hëß²áWû2ÒrˈœÃ g{(ô‰™¨MI¤)BRÛâæ¶±DÚ{R Û•جP[wiAØF£Æv*¡ww·”¹ºzÞnAÅgå%>Ô&Ø"N’rØìßX¸ÀW7£ûg16#õöžÔÂÖ7ÛJ5°E\W<°ÕÇ"¢­@ç}Óg¨w¡Ñ™^ñlÇk*`‹´)Äfæ-MŽíÀf"¢À?¨|0~O9ýpå%óp¥¶(S„¤¶ý *^õ©¹0&l%o¹•~fûâ‹Ó³²Pg¤äyQ×ìììì¶w0ÆØ‚vÙÙm¤¡[ߢÐö)©gâ3sO°âÉé¨û¡ [Ékýì­«7I’¤èkk?À> Îäá°½~€&3 cŸ^Û*;;{clJ×ìììì팱6ÙÙiÀùÙÙÙØÜìì@fvvvöëŒ1f_Ô úxb¤ÛîmÕ—å:a¬èѬÞWuh9æR%Øœü7p6œ "ZsAw +fŒ±#ÝàId>éŸPÍ^¯—1Æf` cŒíÎÌŒ­\6Àã»×yØråŒ16x•±Ã+è€gn9lé `xlêÜõΦ…m\Éc¬™wî}lŒ±,`…÷)ó€^ÞDÛeuv1ÆØb m#cy¯·ú\y×Ê‹–ÏžÂõŒ1Æ&fTlçÒk”yzV-÷AN§j–?ÇË|ˆÉÄmŠ”Â¶+ýjyodÌØü؇ð&ûÌ·›}]}Ò®^ØÂËŽ½'áYƒþÒò8 ñaÆØCŠ{û…’@lí ù/xöö>@ÖvÆ+êàÝ`lÒ”»Áª½[QTl{zWìmÁž¢ËeìØ Æ®‚‰1Ƙ-­8ùب~!"š l&:p…ç§ŒH K ñøˆFØ¢M¢¶Ÿ¿}6ñ’ñÝÄæÊ ¸!!Ø[àgÞ­CŸ€~¿ö%Œ16¨'M¡1¶1ÆãéWžó°°ú[šØúï0ƘÅ;MéåRÈŠI³ ^ŒmŒ'ü˜ ¼ØŽEÃv¤‘÷ýÇ5Ž2ÆùÂ)z餱ß•°m}¾P1¶™ÒÄ3ÑéÆOŸ#¢Ï_€¾AÏI<>¢U×”)B4ÂvB t'Œ­pÐC‹Þs3’ÈÖ[àg~ð 8þ€ÿ0ÆXQS`!cŒ®ó0cŒ}žŽË'Nœ8qâÄ«<ÈÛ  ¤"¶=Ó¥iv,@ÆcC<¿t ®ë¿Y¶×€›¤?©—g–Æy@ï€pJ÷=Œ1öÉ1u°=dX•b;†·ˆˆ¦G‰è5è‡iàé — R[”)B4Â6¶$~­ä„ãZá½~æÁ@®÷ÿ{Ï´Ó=;õB]¾t‰ac|5߃-ìu¶ƒ·. Æ6²9eØæX¶IÐùÿ¢1ïHõõþÞ›ê<\cYy« ¿RltåÕDDãæ"¢²+½[dO…‹ëîÁmŠm°=Çæ9À&†-Œ· '·û„g“¤Æe `#c¬ï(ïá/l›oi?ãåØ^°; 6‹§uªÛåÈŠGbcÿ€v+ÔÂvF ·bl_6, ¢ шˆN?Ö@›‹ñ‡ü)y\fJÒ(ˆyŠm°}×èw†¶ü/®4ìýàî°²â¹6·ì37ü“ÁµÆù2R·2¶Õ3‘Çž#,¶µ—Õ{ŒYB`ûÀ0ضCuE9¶@~löGtpí•z#g5>PŒV^ð=íKÃo·#;x¼ü…à¿…C|„4¼Å&Ò!uüðÒÊ3”86ûœZ¾«Ù÷ã{ûqn“^¾p·ô¿·úGÙ]9vïqâ~EØJ¦¡ÍnéHUÛÇÁS×`+ðQLØ Á}›rlŒmî ­w«ƒ­ÜúüQRŽþÓ®»®æK²œE06ñÒö~¶Øûà+c\Ë>IrQ£Åyæ‰-W6„Ò  ©=àVÏ|½ö €gгfIKzí0Æ;ZÛT¤YYlo=ÃuìÇ„m 09 V³¿Ø@¿JÐõODtî³}Ëcˤà+ÅFØ’€íÈyzK³ï}©SP—y"Ørmî ½‘XåÝ=3OýݽßÈð´Ô–B:…“~Ëøa8lëÓБ…öPJ›„Äö.¤¹¤.P|m2ÐÌwF{ g¡›4[Üa#€+¶À¾ÐM#ŸyÛ¶±Ù[és¼­Ç’9x3Þ’ý‘A×Ù<€ÛŒðõ(ÖºyÏæög}½ýÛŒ|ÀclEl×#}W8ls=×:ÏJW££a[ àÏO‹‡Ï :²u’ž[<À3_x¥Áþþf.ñ-vlëÌ Xžti\ØÂ]i“}æ[žCÄÐmðý|"à7>ÀuEŒ1fŸ™aeŒm‘]gkïé@¹h\Èc÷­¼Øs~OàþBÆ>é‘nüù%H±¼ªó¼XúãŒ1V<€o†¨e®,aŒå7ÐPÛò´l>›±¡@f‡×ŠÙþ[Ð~OÐßú {p³r_þž.úÃzçg¡÷Á 4ƒ1¶À%žCï¡ àoSNõ~¾úï1ÆØÁÞSŽw |ÆX~v­•[Äû›¹ÄG¶Ø±íd3 Ú}ë$rÈÁŸùØã-Ñø²^5‡Ze?^T;𖘵KCofŒ±;¤{ :\³‹1öH@Ûkó³4³‚±±•š‘3¦§e¶Ìh0õh…?vÝÈzHËjŒÎÏùã›»È|à» î襅Œ]ßpþ°‡[jl$ÝŽ3z cŒÙ_ÊÝäýŒ±#WÔô‘šª]û¶l4rúøú¶²Êƒ-âýÍ|â#[\$jxT~5XI3Òþ›ŠŸÙn}õù·‚ߢ(h¸âݯ/Zµ5{ôÝEk쌱’K÷û:HòW.z'tæØú7žû×öÀŸozùýÆØjáû†Å/o ù{6ÆŽlxåõÝvV‰°E¼¿™O|D`‹£ëÿuý÷ÛŒoäémˆ[^ø{GµœXchpƒ8YU °E¼¿™O|D`‹íËGfø¿˜±¹Ý1}fÍW?<¢¶H÷7sŠ$ۃΔÄVlj乊¼@#q‘¶X±n_®¶H÷7sŠ$Ûè·S³/éâsäΜ‰Ÿ³TÅvE…Te%ÂV¶æÕŠõ\_©ƒ-ÒýÍœâ#šas„Xs‹Ì“šØ+\•ðýlÉÇv pG¥Å6+L¦m¾J]ÿáïoæÑ ÛOõB¯¹ºg4ÅÆgX„ÇCï1ó¤¶}õ€ÎÅ•ÛO5Ã`»XÁ:ŒeX_ߨû›yÅG´Âv_¸ìí&-±q¡ƒ=ä3òՔ¶qt#hqýc•ÛrÔ¹b¨§.«Y¨¯ŒG]…1 ‹àï w3¯øˆFØÊ[£›omµÃEþU÷º–Ø8 ‹€Ñ‹W¬ ®óëŒI)l‡¶zjW¥Ävs­’€Iïñ1L6ã°¾ w3¯øˆFؾÂd'Rùú­Ééúç4,B¸£t£âÔjFVž m lö’ô;”oæ‡E¨÷7s‹h„m;¾\œX·8)Ø8 ‹ö~4‹ÀÆ [ÇŲµÒ_pNYÅ8,B”âÑÛ;çË·`\r.jóÐ5ÊòÔùhœå«™7l=—ñˆájv¬Ã"D.nñ°­o-[d¨‘œAZùtýãE{Ñ~ÀƒÊ÷)M1¶ë䇓‹qÇ  6~ñ°Á¯‹›€#)Œ­¿ì‚Û%½Š6þØÞèx¢dCÒ°ñ‹h„­¬¥%pixjc[![Úæ¹»S`ãŠí·F3Ê| ôÀœ$aãÑê:ÛÍü]"çîA²Æúçƒ-¨r lü$‹Ñíí¿ˆˆþÞxuñer°qŒh…ÍÕºæ]ö2"*ûæ‰n£Æt¸9¶lü±•OÕÛÐDêé5Sr°qŒh… 5;ôk+ÝÀßì§TÆÖI– y X-°ñÇF¥ÿ—濪2«1{ÑŽÓ±ìõ)€-k·À¦6}¹3)ØxÆG(I7þ«Š`Óµì;çØÔÇöÚŠ¤`ãI¶sSÛ“ûŠâÛ§¶°•ÿ¼sµ·Þýgã¤`ãÑÛÙ¢uÞ5÷þ²Q9©Œ­OÜû”À¦ÛWÊ[ëIÁÆ5>¢¶ÒÅ ek.¥±}$°i ÉF%ÀÆ5>¢¶g‚Ö\’°ñ lêc› ¡‡¿Öǰã!Tñh„í·L í‚€5wUR°qA`ÓÛ`Ôx3Þ Ùñ ‹¢øÆG4¶|<®öWlœ†EØ4Àvî‹w3Ç;,BÅâÑÛ:èâ:WÎa6 °MÁ;ñnæø‡E.¾ñ°GGªØ8 ‹ 6¶êXVûæ ƒŠó{åÛ™×°œã#a+ï—)ï˜8œœ>Ã"l`£‰=dãîü;†ÞH^Ã"pŽhÕY”)ÿ¦1&Ÿ®u±Uß’oÚßûˆü¹; ]ÿœã#Za£w;¦h/°‰Š¸Ú·®^Öðº™yjÖ iÚcãÑÛ÷«WßÔöîÙž57ûÞ69›¨ˆ«}’~Q›w|D#lß r\ÔVÛä|ßjߘ|l¼ã#Z5#ûTlCŸØø­ö3 ÐüÄ€Ú¥56îñ­°-Äù£'¬¹)mûkÎä#6Ž$·NUïaãÑ Û×5ŽÇ·ú+#¶½uBw_ŸW(°ñöá$cãÑ ]GUÛ¤p‹^ØøaS³aã!1}ÌØìÍÑ¡¿·Z¢³ïÿýç lÜV»ãœüÁ£N±ñh…í ù⯇RÛǸÁ?–}ÞùËE׿ «}.“?Øc…ÆØøÇG´Âf’/®ÍIaloÊe½æ¼M› ØÞI26þñ­°u*«:Øžo$[\†«6°µZy}é¯ÎÏ»4ŦB|D+l¸}wQÀš[’ýsiÊb[Ò\>HÒ lü±UìªÓnâ¶r­°©Ñ […JÏê÷ÔoZcã2,Âzì\|X/°i ®9¥ä•8 ‹ B|$yØ ÁÚbã3,BI³ç—.Ø4Á–Q³fÍL ï¹è/ÄaX5â#IÃV³fÍšéÀZM±qaz—HÑ S4#UÀÖùçY·¿ +ˆˆ\».y1êëðAøˆfØsü°æÖä•&û¬–Ø8 ‹p¸¹î¦Í%Œ±’mÓ³\/:HTÀö~èÞÈ{F}Ã"¨Ñ [×òн‘S×i‰×°Öt9†–€&{6°•„Æö‹.úy‡aÔˆh…m<…Æ6c®¦$¼†EØÐÖß ÎÞ$.j«€m‡+$¶¿Ç+8—J|XUâ#ZaÛÛ' îÑ[<[=ä-6‡g5•¨Õ¿ó ¸yTÕ\Oåï‹¥‰¿STlªÄG(9ÙÈ#Òlä‹3¢´Œ+ñýl%ÿybúÔGVˆ;µµÁ&Õ-À`Ò›*ñJfùk;R›AÝÕ:ˆ¼º]`S'>BÉ "ÿÝW•WlÿØTè D.Ýô5i€Mø%5ˆüÍ–3”úØŠz l*`KfYø‰ r<Ø 7¾ò’§^œ?8[`S[ƒÈ*ÅGH‘cÇV<§ž, #°©-‰Ad•â#T½‚È|°Í ú(›6Ø”‘ĦR|„ªU™¶ÏtZwò× Ml1‘ŦR|„ªU™¶•@ß]¢ë_mlñ‘ŦV|„ªU™¶W‘/®³©Ž-þ r¢ØÔŠPµ "óÁ¶ mÄEmõ±%DN›Zñª^Ad.Øì|4ÖuÿÕžH91lªÅGH‘cÆÆ6êòdËý6þ«=¸´ "«!DŽ[”õßÝJ`S [™Kv¢EYµøˆÆØÎÈâY)DÞýÒK#[š¦Þ%ÕÔ›[ˆëlê`;c¹¡E:2ZÜøš¯Y§AY½øˆ†Øì3/®4è5ó€÷'Ú‘©Ü<°mƒ¸¨­¶m|k8k™çg‘Õ‹h†í¯{R?x~¨vÙU`É3™ä2 ¦\‹Õ‘P3²›À¦¶t€–GGàáòÐÏPT1aS/>¢¶3ät[—¡mšÕGk»âÕ/6wÅiýÌVg¼ØB£¡W_ã¯[6þØN5ÐrÁ7DÇqòäܚ˵¦^|D+ló`̆³DS¦•îêÓÅ¥*6§Õè=ŒåYŽUn‡ÃfÉõ0Ô› Üñ`Û–¾K\gSÛ"'":Ž“Dkšü©lÿP´Ú#`S1>¢¶sFÑ”iDg{>­6—Õú\k„S4gAžäÑ\Go¤Q\ÔVÛpà©“ÿ8NQ·7µÁ¦b|D#lŸ5>%6ZÓ¦\%lŽ\0Zt(¹mf=ƒÅ+61,‚úغ Ís{¿„mÜdm°©ÑÛ{À-€­ß©‚Ía`°(äÐs‹‚m‹|qÿZ?¶¾èB؆ŽÐ›šñ°Y÷±íA¾ Ø\&&Gl¿Û¢ôÖX°’/.½‘*`»C±9êÜ  65ã#aû؈mºÂŸ˜°Y˜â¸é¶ê£S9¶¶%›êØ^Á…~l¥:ãqM°©Ñ[Y3¬öc;qj¹xcs£#¾Ïà¶°)Ɔ-·úknÖ§Åol§[é~ðb»´.^¬6Uã#a£%˜æÅÖ¦ïÞH·°Äÿ)œÆ@mѰU¨´Æ†>Øxb£UÞ]æ8à>Ò›ªñ­°3Ôòt‰L€ßrÆæ4BoKäc¸MXcÆuó6žØèô¥>liw—j‚MÕøˆVØÈÙ»ë¯>l9ùÄ›K}¢‡3|ÍõX±ét:]°X`ã‰Öµžy–ˆŽ£íM‡ï‰aS5>¢6ú}BÇDDSλôÙÓÄ›)qkDfx7A4lwm?tÌ_/g3ÆXñ®™Y…OltúÅW‰èܯ±ì aS7>BÞbsÔô,iõ+zu¢Ï‘¬ L0(ÂÖÞº7rü+Wlñì aS7>BÉœXƒ6·žOKÛå=m‹‚md˜ël·Nظa{õÙcs4Á¦n|D+l³dK;¶qÅf…žÏÁßâ鋊‚íµÐØÞª;A`ã†mî²Çúj€MåøˆVØZË[ÆpÅfJ¤Ó?èý °Õú{cŒÍ©{6~ØÚl.ñ Wÿé’Ú³4À¦r|D+lxððÞ5WüáÕ™¿ñĦçrÆ&m«l!³‘Û¬Øøa ¾C·T}l*ÇG(I#"/åˆÍů )O:ù‹'y´>Ù6Õ°a«êØÔŽ$ ÛŽØÈâµ.¬«[èlä¶e…â:›ŠØnS›Úñ‘daSØðS„ÍɯûÓ"½”ÈFVl]ò}¿½<â¿ôFªÑ Û?zÛÜeŸ\R®xõ+yupkÉŠø°‰l$l'd›Ô® ûçŠMõøˆVØŽË–&!ã$Wl|.´¹õ ±‰l¤úØä5¸JmlªÇG(q­öóÅÆ¥Ï6Êš‘"©9¶ÎÀ\•±©I ¶U@ýS\±aL¼!i aÙHͱ}½àµs*cS?>’l¥kæåÜ©‡!Ñc[Kа‰l¤Ø\§N:uJú =wZùþ76õã#a+=uêÔ©Sž1mÿ(aõ+ÃF΄ï5fR†Md#5Àö€ŒÁßѾA½ÖªMƒøˆFØœ ´x†ˆþz¢)šÝöoläÔCŸÀUw.`¦ø°ùç¶ÙH~Ø>A£¼ÿmÌ<§26 â#Z5#áªCåDtúr@ί¼±‘ÓäÆ{†kÓ{ÆDˆ[¡¬óQd#ùb[Òä{"":V Èúç‡÷œ÷­ºØ4ˆh…-ëQ©íx+€ëV-owläÎ`‰§ŸÄa„÷°¨Ûæ)Ýku»NYã?´‰l$Ol¹+ˆˆ¨¬?Ðð(Ý÷œºØ4ˆh„í· ÎÑfSʉ¾×—sÇFT`ôy±Ýl&f7Å€íÈÿíËw‹±þÕÀ6Rº4»ÀDDûÔë_‹øˆFØ>›BDD·Ú&"ºò;°YõrcÓΕg`rÉ_* ¶Â€Úíú·hRÍ7 l*`ë^JDô¿ó>eDDGǪŠM‹øˆFØÞ‚ˆ¤YÚ>$"¢ë©‚Èf 7Û”|M9-F0»‚_* ¶û`ÄÒBÆÆL,þw·v‡6þØ./!"ºÀ>éîU±iÑÛ®ñDD_×…gí39ÿS ‘ìS^A$pNk®ŒVwÅ—ŠŒ­¨PcŒ±1+ìò ÀÆÛ×%Z@j>5üPMlšÄG4Âv*óS¢_;™_¹ïîEªaóM¼èM«#èê‰ÛQ`1{&&­8›l«€ôU̇½ÜÂ.°qÇöíyn  ù¯DT2S‡jbÓ$>¢Uoätݨ[šxšˆþúà"LU‘Ú«÷÷b˜ƒÓœÚšÄG´Âö¿‹×–}=ä2ãÕ¹jc#"r9,æ@_>y›#âöŽŒíuà…@l¼'°ñÇF6c½šCDylÚÄG´ÂF§guJË~â\Ì«?l¾£œÃápX-‡ÃáR²½#cÛX±Mć› ؈JËTz§`lÚÄGHà ²;žÕÏ[ÌÛ;2¶’&xÉÍ6 ™‡6U°©VÁØ´‰PU~\cll.&z±µh `”èLqlÚÄG¶8°uÊôt‰Œ€ºŸl©M£øˆÀ6–aûý>lÙï‰IŠcÓ(>"°Åƒ¼ºÍÆS«×¬‘LulÅG¶¸°1¶~ÔÆX~1‹¥¶J‰M«øˆÀ¶;dK+Wl©M«øˆÀ¶æ²ãÙ²[jcÓ*>"°Å “×mñô¿éÕ!ºÏ¶TƦY|D`‹[P™¶TƦY|D`〭§À–ÊØ4‹l°a»À–ÂØ4‹lñ`›¾ÇÛERòÁ ¹vÑA’ÊØ´‹lq`ëÈ«xè Å[ cÓ.>"°ÅíÙÒ»˜,°¥06íâ#[ØvÉ–®C|-e±i© ØɈkùêm«¶ä`ãp§¶†ñ‘*MÝ­Û" Î-e±i© ØÜüæÀÿ•a+~ùžõâœ-iØ]KËøH•8g3p»Pb„-Vl"õŸÒØ´ŒT lÜ".Ï×\dlÅ8pàçÎ6»À–ÊØ´ŒT l6€Ïy› çlùõdÍdŒ™ÞM®ÿL`K]lZÆGª62ñéQ*ð¢ÒŒìƒAk팱‚~€ìý[%Áf{zYlØ4T lNÀ’ø›¹õÞ,Alï”ÚŽ£=Õj‚ÀV °½s÷…ÀŒØ°i©ØÈ‚Äc.#ôn%Ø>k-Ízø€1vÆvŸ/Æú¯Ø–˜ú flšÆGª6·4ý|"åÔCï %ØV‘æõm´,`Œ±b¬ÿJÑŒ´·Œ›¶ñ‘*‚(QmN=ôNR„íÅé¾YÚ^eŒ1öÿíÝy@ÕâðïYTÑTÒõ¹äî–š˜¢å–™[¤–¥©EaÛË÷Ô_vŸ•úÜ*ÊVµWÞ4·2EÍ%-IsÅp5±TTÜí Â…ùþþ˜™»pwœ çû×]æ¸gÎçž™3gfb“¶R±Ïö ·ØŠwúH™ÁF=[ôÓXYsƒmqŸôôôôÍ• Ü±þ`­½[©ÀÖÃ[lÅ;}¤ì`c’Õ­±½†ŒbôÛž€¥éé»ê?¦§§§Žk&HîLlÅ<}¤ acV,aðž[F fº^!f@8€×ÒÓÓ$Þ‡aÛ‰­˜§”%lÊ µõ^ýX™äÏé¶½÷ºIOßÜ¡íÝF lw&¶bž>R¶°Ñ” *ÉÓ!¦T=è3\­u“RÇÔG‰¹‘¥Û’GÔªU«õcÝa+æé#e Éä8ˆJÌp·=™•œ‰F×kÝñŒ Ãb"r)îÙ¢æÜ4·=[qO){ØH£!J¹kvR†“%R Ê·RÝ®u1ëÿŽÃö±ÿ)lF÷ô‘²ˆ¤1)N¹}}D\‚Á`ÈP’l0$ÆÅª·°×§z²Ö¶; Ûô€Iižì³÷ô °yrf¬–…{ZzV’>ÎÙ©»Qq†T“‡kF€ò=¶´—+}êÑI±Oñ ¶„ Mþ3SÒm·XcF²Áظ¸¸8½Á‘áÕZØî(l‡‡U_íÙhd±O¹}lŽ¢GTÒmïzfè­'u”òµ.RJ°íŽO=ú/öé#¾ÁÆX¯wÙujQ@±Ì lw<¶p rGØŠúˆ°™" 6¹ˆÝ[–>@ñï¾ lw<¶ñM€nG=ÁVüÓG|„Yò` £ƒXn¡&+Ã…z l›·ûl*/y‚-¡$˜oV2ðœ>@\²WŸ3ÊJ¬‚XS ®u‘‰ØÒçøÌl%±—â£_ñD`­ÜIE<îÞ’ãäîÐÔÀ|Ö´À&°y…-}(ºÉ-¶˜>â;lL€ßZ2Cïñ±cb„²£gŠ,±H‘;kÃÓ¶ßÝÎ )é#>ÄfЦš,“ƒMžtjú,’!@²hI"Þbë.Ï Y_è“æ[T‰Œ¿ùl0 +B=y:5®ÈÓ£äÑK<»XVB’f4,J¤ä°uâÓÓÓÓgãz"rR Lñ%6f€|6§Ñ_/Êr¢KF¬‡‘YbµA¢aQ"%‡m{8Ð9Méâ0p+l %0}ħؘÉêvbœÓ êV¦1€g‘‰Êtb hX”HIaû¤]4´.}dM¨û³sl(é#¾ÅF=PˆÍðpq“@„‡‘Qæ1¬ÛF¢aQ"%¸éÅåÇKbúˆ±1“-{cn6æô¢†{:iLŠ…FH4,JäÁÁ2‡ÍØÌOæ’$Lj2$z3)¼ lEŦ/{ؘ$(GТmNå+Ä%3ÙÛ«ö o[‘°¥–Al “Ü# ©¶‰¦Œ¤Dù-}™U”_áM`ó›©,b£ºahLJPZ±|§Á§ MD%¦’¤1¢ð@dQæü8A¢aQ"w<¶–IlŒ3y˜R qQ…tœ>I};Ön ²ˆsìâLÚ`sR”ˆïcô [Qo`ŸTF±™bmG22 ƒ!!Ñ`0$gX×i’ý@dQxDèSµêÙ"ô©¢Ù—âfYtlšm±x7{Ú÷çneyz ~CÑfD¦ê#´â¡aQ"¥·ˆÓnúHiÃæÓi›÷Ø6!M¤x°e lBšH±4K Ç"˶(íxD iå [²Àæ]ôÚñÐ iå 6/7ý´ã‘*¤ l›Ë?íxD i›Àæ" ɦÒW”ˆÀV67#¡  ‹ØÊ 6½zËœÛG¢IQ—¿ÚPø%)=q¿çüýÃg˜ÀV*cu2Ìízs]TïæjZuüÜûü-éØ{Ýü1Íæµ++ÆFË=û¤Ãs{¢‡0&°•Îd%FiåÍeQÒ¹éP}Ô #ûÖ2â@áÏŸæA ¶©ã£á1¶ô±CtØ6áøž$yéóÖzeØ}¾Malä`±‘|F`Ø„7òI;•ÇŸáë x°=66ôÛ M`+á¯âmœz»¢ÆZa#·„º•…Šæ[so°Íض; ›So·SÔ8lÜ äˆÀVêÖ|©¿õÊ`•'˜´-ª6Æè-° l囓 ÉÛ)ª0¶¡v9ÂviWšÉ ¶‡=žkû?]Øu8Û5¶K©ç<±q\ž†H cãp¯Øc[÷€šÓrí±I«ºù×®»†Yä}P7WIÖØ.Œn=tɼyµ» mQt/ÑJ¶ò2ií{Íí°½«|¸Ëß…±]{ýOIÜ׺ÉòK'ÛÝ5ßÈ[ƒ%›žíKTüO6IJýƒ¶“Ò"]¨h¥[ùæÛŸ[[ÏÐÉÉG¿j `H!l9íñx>IÞl¼H’<[7h/I°ÂÛ‰Zþ’?¾£ä=ÄÑJ¶ò!Ͷ\8[[H IÞh`“-¶W K“; à;’RgL·/›c­°í­1<-?j,#INªÞRa’L±“$¥ùa™$9WÆ–Ýh,ˆDÏ“?òE¡Cz"EÉõC¥`Î>ë¿8¼u°^~˜5§PëG:Àö’üh¢•ûZÖ$³[o‘$/UÇÇ$)  ë÷þ×3Úâ[’ätÈÓLÒ*]³I2:â:Ir&: +e#eì|6§ÞŠ^”ô4øæóƒÚ耦\·/~98ã¥SoWˆU†ê0Džã±tïÜ"OÝïÿ‰òCÛYù+óe±@µkJ‡öÇHF£wɬÈÐ㢙 l¥›SoE.êÑZæ§!u[Å%î—þ«)êÀ¯v ´2ÈïÏí h2"‹$M5@‡;W`žP™£€îGIòxßp¨=|5?j~Ý7±MLýêÃ¦Ž ï)¬ l¥b“L>-ÊIL¿¯7lÈpö®tø‡o~²9<»}é6Wǫϑ7~¿îŒTnÛ¦é8IfìøK®é¯]6õeLÝñ§Uå\ºN÷žVëû_-§ÁËï«w­Ï?FÒtè€ÍL¸¼C¿/ؼåaôuQ"Å‘œ5ÏTíAþÜ @Ì_dÞ܆î?¦¾ÿ[¿@MÊBr¿<`+¿ ‡ß"’” ‘¡ý‚{~@9ëðæÒþödÞ¦ñ5Û’_Ö‚ÿe3¹59잺¸{ä²å›1&ÅjÅCâDn7¯VÐ#û]½‚¨ü“­Ã»t¬4’75¤©Aÿ—|xi}`„‰L‹¯ `ë@fÊ}ÿ—Ïô:òª\ÄŸG†ØCr^mmsã+4 ¯ü±Œ¦Á?HÒêyù MH+W¹UðаÙKçÉ#UiõÞ»EþÙXK’œ†Õ$y&ø„<¸á_fGoy7/‘œ‚h‰äA`ø¼y‡¹eã+ ¶\éià]žgÁ;@€|R|AW$ä'¦Î›w]`ÒÊYæ¶‘$_”$³”9Ý{ÔK‘jI^P7‹L[p<â¯\Ž©”KKð¬‚\èÞ. )5>%I~™î­ªÀ—b3Ò>òÒJeÖ‘ò£å@5ùÑF IöÅ\ù€Í$YêKäÛÀÛ$Ée@Ce¼¤’Šm3PS~m ð/’d, ßÓþ«Ó|6Kbµã+¤•Ƭ7cûÙŒm/P—ä5ô `IÖ¶¨Ÿ¥Î0øP/M¦bÛjÆ6I’¬¬T{½þ›ƒîH;IBZéÆ¶ÓŒí P‘ä÷Àã£ÍY `Ûª~òy`I2ϵœc›ªÎo ÌSß{R`s°›¥£va "9GÝî³ÄÛ‡@˜‘$óu®± z’$QÕ l¶I6–Æ¢DŠÛ¿Ô)ÞŽ±]¯ |B’©ÊlÛ%ÉPå¢À&"°Ya›¼ãw„ Ò/dnèé6&3És‘Á(°‰lVØfm- fÆÆŒÞèñB³f–—\bã/M6äÙýþ À&"°Yc[ຜ40ÕÛ•.¯nøÒpÔjî•kl¿7žÿÃç?œñõWØDî8l—€UÒûƒí6#/E¯*\šKl»Ï…'6‘Ò™ufl;€0ùQªŒÃõËÄ1—)° l"žvmÿÀé€Àx®Þ³ª²|ŒÀÝ”ÅKÙ l›Ïrã“ûƒTŠýo–GËÿùVc?Õÿ/§¸[€À&°Ýñ‘.¤_ô¦“2ýuâšT-@`ØD6MD`Ø6M`Ø6M`Ø6M`Ø6MD`ØD6M`+—ØPìqÐòÊzÊáW±^ó›øÊ"Å‹MDD¤Œî³‰ˆl"""›ˆˆÀ&"""°‰ˆl""›ˆˆˆÀ&""°‰ˆˆ¶í/§õÿuæ³_I’7§O¹àx‰s_þXÔÂϾÃó…ó¶Ítúž”ž¸ßgU Ršb\7¯T`Ë6kø½ ö¼Ùøœ$9èâ@ò”¶À´¢.¥Íê¬S wŸK‹Ÿ¨‚ÚŽß»²bl$°Ü'U RšR2³{ š— l?˜«áwKy¦TlwÙ/±úù®("¶‹£,…»>.ΰM ›ÖU Rš²ÌÈ@”lÙ­Qû´¦ß.ÿ^ÕÃb?Ç·‚”YP×slägN±‘7‚UlÚWH©Ê”Ò‚¹ojüݺ˜=œ=éx‰GŠŒÍªp²Ù66T±ù  DJS—lÚ'Æ­‡EÇã ¶­®°57c)ÛY.° l"›À&"°ií8ɼc;ÏZ½të¼úèü’v·ùÄùßR®xºÿ—¾ço[–{¶fxˆíRê9›çvÎ.„íâž?lîtqïžK6ŸÉÚ!yŽM»*ñA¤?K¹è~¡©F«V}–4:$Ùa»²o_–kÚ`ûkftsõ5tøY~)ÿ§1a³H’§?ˆÑ­áùÇt@óßÍÙ}`“Šº3¶ßp[zÖ aá+t;jÆvþý••÷Ö¶l9¬SÍA}Ò­±ý:â¾ÈÈÈNO·.%o^í.C[ÔÝK}þEt@Ýt^%Y°íí Ì,ZZÒ€®ëvs!«î÷kX-lŽ2@òjtdd¤žä×]"###—¦aˆø ç&Ô lŒè¹9$yëñÈÈÈÈÈ®)$ÿŽŒŒì‘E’Æé‘‚Ÿ<%·ˆÏ‡Oåžû€gm±eN‰hݯ>Úoq B#l«c]j/_ÇD·ˆäñ×k˜Eò\Œ€5»jß]@¥SÊgÞõë}W–?ò´Ëâ“«‡/Íç×+V‘±}õ° `›Y¹+ûl{¶Q¸w‰í}ì¤þAÛIi‘.T~~²Ý]ó¼5xQR±Ã[H”¹Ñ¿Æ§)ûæV>–_0=…Á§)mo¡ôl—î&$ö7w» 6-«@ÄYY9äÛgø£áy…xT]9oZŸv_›•G¶ÇU~#¯Nk`êþ0~yÖØö…l"ó߀ß:Ç 4ÃvhS? Bû>›¯žø§©ëúÉ--?ÿq5–ðDc`¼ü‘MÌ$y£!ÐqÞ<—S¬ö‡øí$INVŽ;/ߨSÅ–V¡ž$o<ÿjƒí‡ ã…ŠYˆQ$Éø’äÙºA{Iò€ ¶¡! LÌ ÔÎ%ɼ‡CO“ä*ÀïIJ#Я€$3«©›‘#TllQ›†U â‹.ß^’ä2 Êq’Ì­ < ¼û6‘äÙ:òH2ˆÌá…¾ ^nõ¿eµÑÒºg»RHòV8Ú8¡áfäO¦H$¹ÙèI’l$·4’u€fÇ”¯uÜEÔEk¥gB=×wRÍi†AÊÆrÚž *¶ùh*w[Í·Xc[øž]¡Cñ¼¼íà—ORê¬ ‘šclÕwäÅ `›ü¿M3/Ò—$¿RåÂÞR±½`ÆÖ©06íª@Ä9Šáæ¦Ö$ù9à¯ìÕ?ÜI"É~w!Î*)l o#/-Éöj{Þ¥b{8@’Ô'Y°Io‡¬µ/§ êÈ»FÝ$¹™òëk*­Q°}bfóɛաÌ'~¨MæÝ‹nJaI*¶x3¶Î°iT">È‹ÀO–±e|K’y €éò>—î’ܰ|óïf’bÙҴ¶UÕØí „VØR€ åá/) –y Oniùþ É/–æŸü}.‹nÿ|ÛC2EÅö!p÷N’¼rÙŒÍ4îžÊ ´Ï$ÉS&’½”.ÑòQËPç à]’kñññññññüF.Þ²¬O°iT"Ú'¿Šå'NjôWÛSƒ’œV-‡$_GM¹ Ä×*ç“üŸ=6.×_P±mpÂ{lOö°K®MÙR à '-Ž“ä·@˜yð˜ËqN b»Xг Þþý(Ö:*h €Ð9yòc R»–X° ¦’|A£-I&ŸÜ6¶"Tˆ²ð71 „H$y=ØD² Ák$ÉV¨gÕ.ZGalJ¡Ok a“Aœ³wó¢[l‘ö—Qα)›í1îZÚ!@Ò莪ù®*e™eŸÕ6~ |~¾ÛÝ@£«ŽJšMåS^ϸÃÖ6K4~Ô›çU â‹áæ1´i®©ûàCHnEI2TÙÔ·Ä9¶[‹;õßð=6D†½›Žn± ‹±‹mÏÆÇ•ö窥±'ðIæÕÄ—•’D»ÀÆÍÐuOކ2fX(ÒÜ@q•äFà!wØî²l–¨|§6Ï«@ÄùP@É›ôwáÈ‘1²uÜÃ=¶œé5z#{8Àö80çìÝ<ûûl¼ã¶¥]h*Ÿê5ÜrYò  Š+l¼:V â6ó>[z wXVZ 4$õÖ™±Õ"I¾¬Œ‘M¼Åæqˆø"/~Ê¡V>ež­@êv8¢\EòAµ¥åš[ÚK…œg<Ôq c0›$¹ ”± :¡•|TžA’VJ·n…mžyÏø<™ b'IJóÃ2•ži>ÕŸ©É$Mµ?yK#{p½[äé @{¹¯› „JÊøM³|’LñÞ7Z.£–U âƒübþ%¼RÑ<™„̬<¨>9 ªfum«61õ«›:.¼§2<‘£€îGIò\ÿÊPw O€=Iiy#zí²ºëöAMˆÚ³Ñÿ•˾6F^f|@ɹ]MFdiY"¾HÚ @]û>÷Ö›—g3d}ß$«g·Þ® 5çç‘L‰ €J}?"yrð=Øç 2w€€q3­2uÚ­HL‡—'òðÇÛnܳâýw&M=´™.(ÛÍÒç×~¼Nî²»VÕ…šNü¸2݃çÈ›¿_wÆÒ‡än_ºíªëI¿[qÈúHKþîe+R%^8dyéôÚMWIöö”kïª@Ĺ¾uÉêÔÂ[ɶk2oÇ·I'ÝÉXµö"Iþµ<%ß;·5ôïIv„½oy²³Â®r¸žE”åxÂרބֱþ9i±·ü­ Q[ñ`ë+Ÿ×ªn‡50•¿µ!ª@`+"¶ý@€‹×€n«ùÉï Ëã­^D”éxÂkl¿ðbÿqÀô†s¦‚K‡þׯÊ×åqmˆ*(Óñ„רÞ°Êóůõ–¼ý/ÿUùåSäW”§”¿ ÿMù÷•WÈ[×ÿãN ý«Ê¸2ôÿ|»òC¿áʧÉþè4ù_‰I~-öÿþãU‘›«ä×›_Žü'_–ßø“Õ‘ß¹Z~SùOOý”ßµFþ¯ò¡ÊkåÊïV>C~Kù°È¿ýž3SÿÎ{Ï ý&å?ûJèß}ŸòÙ¡ßüç±ÿâyKâ÷Ÿ+o½èçEþàyò{üjä}U ½NùËu2¥üáóCÿ¾ò_}MÞ¦ü×ÊÈ(ù¿PþpIä·d&ô;”?ÚýGÊÓ…^¼4ñ×å?ûo.–fì?ùøúÔïüÄ7"òò§¡/‘?ýÔ%ò®Ð³ò®¿•C•?}©¼;ñä0åÏD~Ïg.“÷ù½G^.ï=*òŸ-Ûú}Ê·)ôŸ+­¼Yþâï_!ïoÅþì•òØ<æªÔ:Vy‹|è¸-ò—¡·Ê_~n«|8ôÕòá㯖¿R>áùëÄ'^#‡Ÿx­~Rä%']'KNŽü‘“¯—,üÑÏßzZùåe©ò”o’­H¼MþæÔ®?¾òæÐŸø¢ò-¡?yšò­òÉ/Ý*Ÿ ½]>µj»ümìOù¶È«o“#Vïˆ|úùÌé·G^s»¹æ9rmä£”ÏØ)Ë”ÏTîÈß)Ÿ¥|§­üå»äïÏîºuήПU>÷›¡9Oywèc¿ª|·»în9.ñù÷Èçbÿµ{#_p¯œpÁžÈî‘/Üyf¯œ¤ÜÞúdå‹î“åÊ_W¾_>¯|±òrŠòúÈ_øÆƒ©W\òPèS•g½òRåGBqCìË•Ó_þ˜|)öªßмé[òåMߎ¼ùÛ²:ôwdõߑӕ¯ü‡Ðk”¯z\Ö*oQ~BÎPÞùÌ­ÿ(g^ù¬k¾ú+Ê×î}¶òuß }Îõ‰Ÿ”soˆ}ã÷弨_½é©×m{*òÍOÉù¡ÿIοåŸäk¡ÿY¾vë?ËÊÛçäÂÄ·=-3Ê;"·w<#íÛ#_tû³rÑ‘¿¾ó‡¡/Vî<z½òÊÏË7îJü‚\²+ö7ÿŸÌƾt÷‹©7Ü­ü’l¸ç%¹,ôä²{$—‡> —ï9 •÷þX6%Þ÷cÙ¼ïeÙ|_ä+î{E®¸?ò•÷ÿD®| òUþKè-Ê)¿*[•VþW¹ú‘Äÿ&×<ùÚÇ^›Hôô=@Ðô=@Ðô¡qúÿø†ÃCô=@ÐôýBú~¤€¹ßùë*@@:˜MûOoèz€ èúôýä¨÷;]åèèåß¿þíÁI¿GÖP_6J?P ÿØnyZmÀs»å}S}ß@á?•ùª\yÒ€þâ§£°Ñ·vÙþ´oËójü¾#G÷ ô—ý0ZÇ?Ü›ú¯>zh#@Ðôýð€^ÆŠ Ð#„ôÆšÀ}Ù(ýÏÿÖf¹Ë|ïwï•ò‹Ù%1ÎÉÓ›ÎMþu§ýc<ôÇrñô0€þ:ù¦Q¼g¶´­@ÿÁ-¯d'¼ï7еOžUÓ<¿OþÊú7}_4î…ûäðÃl@‹Ük”é‡×_VèÏÿ§Ü+×çnÜäúÕááðÏrº7B¿Sö…Kš“µ# ô­Ûÿ-\ì wlèz€ Ÿh 7§w èèµè¼¤@¿§T”>ùòõÃ}ô?ü±<ýÃ]rXô¯{Óyrqø÷~ßua„þWßÞ–Ù _‘ÙO@¿öÉ⯣ó§ÜýùìÖ‹  ¿QîÖ Zú7¾ë&¹Gçúo‰#ôdÓ jâ‘MŸíè¾­D„þ ¹âÅ`ÚGî(H¹} 'BÐôý¨½ožªzÞmÐ#Ðg¢óʇ6 Eéßuùý.?'Ÿr“ý.¹ø^‘]§@ÿÑ`ؽÁß:Ð/ýfÉOôôæó­@âÞ$ }wð÷äWVï7ÂÓ÷ÈŸ¤Ü,hUv¯uýó{äCÐhk­¿ûLG„þ¬'s@ŸFèÏþ~ÐgSnN{0~±q^=@ÿñ_ªã¦Í] ?ñay.¶ïÂný鯇„Ÿß¶EÖä>.<'ûÇß²5„ùOß-_^zL¶<»õÚè?³=÷-9²,ÐëRpýa³ÏFçîèÏùNô烠ß@ü=9 ?ïò Åz€ èÇèmQRnè½ÑùÈkÂEQúcãpô]+?çúÃTšÍ½WÊñ!ØŸ—úL„~¬.·þcЯþn´²½WGúß-Ϩô™+.ˆ"ô§GpÿÌ•:þWÿöÞpk„~ñEr©A—Ïnýº;å¦. oÝ/’ö¡Oõªl>Áô¿éÕ0ÝfuÜ(ö“Û^!þªRúµFËßrÊ]Ñøæˆ#ôg<Ûzê.¹/ù"ôGïø×¨ÚvÜØÐ_žüÞC„ èú‰z_ú @@Ÿ‹Î«F±‡rHJ÷¾(}i Ó¹Ä¿ßó¦<пwÓ 05þåø_™RnÞwÅËö~»ö]kú.Ì+нW>`æÐ±'ΉRîÖÀ~ úæÕ´ÈGÍú¯>¥mXüÇöšr§Û¼ø°|Âúntþª‚”›­²åÅnÔ~m&åæZÙúRú~Eδ¥Ü¬F[Û¿Ê5§YRnˆÿö>‰Î?t9)7=@СèZ@@Ÿôl“ÀüÌÌŒyä‘)Ðû¢ôeSnÓz¹yôï‰aþéMëÂúöX€^ .nÐzÅ×2@ŸFè}½Ü¬ùž“ß“å–^n>_’Šõróëg<™m[7Пó­L?Ózéå¦4Ðß.{ÈýÙn+/œË> =²Óôx¸ èï’û¤èËäÐô=@ÐôäÐ#„ª}7:¿G9éi·Û²wïÞ°alq”¾L£X?пG4 ÷¤Ü|¼ ÷ßøø)Zþü~9ÉôŸÊFæß¯u[ù¸W›g¶\Ôíá&n›äЫF±éï«×÷ ô‡_û“¨7›ë6Èoÿ½™¿_>Úk·•esèãî*¯8ÑÑýÉD`íú(å&6íJ¹yZÎø›þRnÎþv4ù³ZýþAŽ. ôÇìŒ{¿Ùy @Ðô}_@ß(èfX@ß —„PÐwsç÷HcOpqPÿÿ*°×Þ¥/ì¶²è_÷–-²KKµyÚôSŸ×ÒlˆWbã<úL—”f½ô™“=²\zÕmå)÷‰µÛÊ,нÜĺç+–^n’qçx€>Ó+ÎòѺ­4>ì®R¥Ûh–2SnäÅGäSq?ôŸºùÕL^þóqªÍ¾‹ÚF£Ø9¹/Nµ¹ïë_¯§Qì·w‡½ÜvÄMríòUäo{‹\€F±=@ÐÏ?Ð7è‡!4, ¢ókº0¯ÿ·¦!‡Á¼/JÏ—bGýÃR;Âtyäv¾ Ðô=@Ï—bB“ôatþÐC¥Qôßžì]_èGèãî*÷^x>@Ðô=@ïz¿½8ú~毫=B èÃ×~k*8€úg‰Òô£¡ï~) èz€ ·½‚Þùt]åèZ€)7u èz€ èÇè'Í=B=@Ðô=@Ðô=BhcŒ1Æ Ç=B=ÆcŒz„@1Æc€!ÐcŒ1Æ G ÇcŒ1@ZX@¿c¹LM-—Î(]¤F±LãfêcŒz„Є½;õ‘©Œ—Èl|µ‚ß6Y9e®+òÔŠmó £‹ª—G/Ç|²mÝEåè1Æ GMÐÏøõ³ÜšËÂ|fy3²´ Ô ôcŒz„úŽk¸…N£Ø‹dåŽ!4³ì%2ë®Í›‰¬÷¨ Þ-Û’Ùþ%²T{³°t}þmÃÔÑG9ê)[æ•Óærj؆" ·í·ÂíÃc Ð#„&,åF¯ÚýDx3óGñ àNϸ‡ëeÓÊ4;­•¯ïh¼½á:4à_¿Äú`ñ½L=9ʬòZNÛ`Icjøö[¦Û‡1Æ GMP„Þþu½5Z¯=T8‡Gð\[9\@ŸKÇ1¢Ü®7EÛ2Èm(ÚoÎ6Ž7cŒz„Ð}9×µ}6ç½7(.“r3H ¯aŠ€¾lJ@1Æ=Bh’s賩$õ6äì=åfiÚpUå´÷¢ÞF±fý$©2:¬—M¹±}ÛàÍ¡wì7ÐëÛ‡1Æ GMÐ[rµkË¡ïµQlÕî¡ëË‚n+SÎ¥¬d#è³ÓÕźSnú؆JbÝÜ}kJùóc Ð#„Æ èñÂþÝWbŒ1@èz¶cŒ1@è1Æc Ð#„zŒ1ÆôM(ЫcŒ1Æ“m€!"ôcŒ1&Bè1Æc Ð#„zŒ1Æôô\Ô0Æc€!ÐcŒ1Æ GôcŒ1èB=ÆcŒz„zŒ1Æô!€cŒ1Æ=B ÇcÂ=føÑv€!€ãÚo.ãÉuÑ}„{ @èñÜ\B“+€ Gô Gô Gô Gô\sz„zŒz„@Ð#„úÀ;–ËÔÔréŒâEo”Ë6auP Ðϵ¥ÙlË\Ý2¨å"Ðô=BhÔ>‚ ‹dåŽdüŒ,m,‘Ùac•uèÓôóô ¢ uãÐÜ”öœ»›ÒtMДôÍЇ÷ìu`éúº6jåšZ±  GM&ЇÀÿî¬X€á¢îÅ8ÅQƒf€~t€¾"DwZàèO 7C#qMV)#@5=Ð#„&èg§»¼Ùéî×/éF1’ßñzå´%“‰€ÄËÊL?0ئsÜÔƒEíÉݶÉÊ)-ârôQÕÊfl¿u]™ù´òÛnZ¶m o$FyJ”¥x¾îÃW.*f-»¶žÜÛ—}½7á½#­F+ø©OË.cNÚÍîö4ÛƒiÝh~º¼Ž}øn€¡‘úà¾ÑPà\x°\[ÃñÉuN]ë£ßÅ×iÛµm&°rGî‡yèBú0*|pñœV³àB_Ôf§³ª”Õ{*žÖ„Ìä˜yíê˜ÎvSÈ\Ð㇌¢}•²å.êæº¢ u²¼ðF¢Ýœ:…Û=pd_9—(‹s>KYŒáÝåyÖ3( 7Sn¨¶}§%V'}48þm> ¤Ó)Ø×–eN„¡I¹Ñ;¹û€ëzÖ÷¥Áu8š&¾9¯ÓE×Ôè–)™Ÿ¯k<@ Їžäb^ô’¨H7:Rt¡ËæP[rî]ÓY!=‰|8"+E)7Ee³FÄ=âäfR½é>8”Ýþ‚WÙEe)ÜæùK¹é´Òê$‘zU¶.Ô»–—DòͼûÜp€¡ÑJ¹)º6j ž¼)a=wOr\§«\ëT 'ûùºÆô¡}YX¿D‹X, .®ÝH}é v?9•¹i<`_èKGH´u êb_yûkx¸©z#Ê.°·-OEõ-@ŸÐ#4@ïêLÁôad~}rωƒIÚ=©ð:]å^7_×x€!4X ¢#ª1lšŽ¡rç§usé4#õ£Î½=hÏ¥iOêÂîÈ·/Ê{,¹Îüº<¯cÄ +D§ÎØ]5å¦[/iY|ë™ïz}Úàw«;"›6“‰Ú'9÷ê_ýaÀ2 Ghtrè­×F×õ,¾k÷TR©7IêNÑuÚûö¹àÞ0ðk¼±Ü¿z„P¯@ßÂìëÃ|޶;‡½¡7X*óJÒáÈ66ru3–äF&bK—ÍÓ5ócymª7Ìšš^⎊ëÍ7˜²•¥Ì|ÚE?y…¬—¥°±ìrèuÀÖ <Û]e™oä¢ï*-§¡5Šmuôˆ|ñ–á=B#’Cï¶x®­¹iÒ¶Pe¯ÓÅ(ä; Ò5 G è1ô‡¥ºùó¡‰zL·•!€/ ïvW‰è1@èñFèB=èB=› õ€ñä èB=žà› ¯«~‡tsÉÁm£¡nš›ÒžóqrSš® úåº<™ G}o@ÞK²×…¥ëGÌ«”«×{@6ЇÀÿî¬X\œu/pE³qú^ÁzÐ@?Îúà¶Óòÿ(=z4A@ßÈ=pw=P 7ƒE£píëµ\=BhÔ~vº‘Ÿà~ý™Z±-ŸüŽ/Z+§-QLÄ#^VfúøÁ6]¦,ÛdåTwùézÓyºÙ‹éY:e_Çe1]¿Éå.ÎF¹Ž>ªZ]T)¯§žÕÃV£¨žÛÕ-Kf9™imû¤O £÷&¼w¤Õhÿ"õiYÂeÌI»ÙÝöf{c0m£ÍO—×±ßí𖴚Ƴìfd^o¾‰èÑEè‹`~à@ÜKÓ3%î Ú°Ìõ)¹þ«ëqôÛ}-+º»ËUÏ= GÍЇQùàƒ å´º0ÍÀ] f§c Œ/`)¸ª‹àT<­ÚúÅ/ó*Ó1÷& æ1€V]xs¯O Xµ½Qp^œ³ë(¡¯RUÊë\¶Qý&d-¿o¿t‡‡pu½ 6€¾Ó’F«‹À>ÿ¶Av8‚}mY¶á¾ˆºVŽðA–Rãúè¢Õ!BÆ7åÆóƒO¹Ñ‚¹{ƒí:ß½†©aKƒkU4M|²^ËÊ^ƒÎàHm÷ €!4l /H †Ò$Ò„x/v¹È%çÞ5]•Ôãþ=ß4e/Îe_¿•ËWUÊë+-*T¹,ÑÍÈZ÷CJ¹é´0Ž ¹¡A½kyI$ßÌ»Ï ×æÕ£ÿÍU«ì eÞ5ìˆÆ,‡ÞóCK¹)ºæj×ùäíqë¹û”åZÖë5¸JhÈ©D=B}ÇQõK´(É’àBÚÔ—¾Ö™§8Ž@_µ‡œ²@oFÐËÞ„¼û¼:Ø÷ôF”],`o[žŠê[€>7¼lÎ;@h;\ wu°à¹Î‡‘ùõÉ}(0i÷©Üµ¬§^Ê ÊÐ#„Æè£Hˆj ›£ÊŸZÔÍ¥w^ÄŒ×Þ‹f‰^2Ð^2åF[O:ÿÖÔ–´ü–_¹ªÖEÙòVôÂÔ"Ï~YšìÓð©¿B½‘“ÞêŽÈ¦Íd¢öIνúW° / ôaôÞ’ö“KÅɤÜXRv`GЗϡ·<×yu½ÒîC*ФRo’kVþZVýœ)W¿÷ 3èTô7@ªès ëÌÍî1¤BC¢ÙéŠbËÔLMM/qGYÔÝœÎW®¸Qléº(SÞÑŸY­¡ln{J—%‰fiu<ˆz° îFÙ“È|#}Wi9 ­Ql«£G䈷 ÷æÐÛ´fRsZ-wd>~™vD}ÉzG@ÄÕ(6…ýl ©a´±²_Ë*\ƒMˆïçžÐ#„æè1Ƈ¥ºùó¡‰zLÊ B Ç“ôÝî*Bƒzu®öb€ Gô GͳÔ9Úz€!Ðã ¿¹PO¾z€!Ðã ¾É#„Nê@Ð#„zŒ1Æø`ßkŽ>î½]@@1Æ×â~sôqoíz„zŒ1Æ/ 4ˆ!€cŒ1Æ=B ÇcŒ1@è1Æc Ð#ÐcŒ1Æ GôcŒ1èB=Æ÷Û:ê]¾®Ñh)¸µôô“yX¨æA ã,tRýEp©ÛñØW=BýÄÞˆâGE¸Éb tô=@@?17¢…&n²=@Ð#ÐôýؤJ-$  nôÿá—Ï ïè}ð^ç¸G¸‡ã Gôý¤}Ùô¢W^zzÁ=u3x ¿rŸ¤èûúöí’k¶l”·Ý(û83n×Î[äªÍdß=ŽU€!Ðô“ôe4–кc¹LM-—Î8ÔMe¥z9 wmË(ÕwM@¯`þ¦ë®”o=¼Wn¿å¹ã¶›R¨¿ëŽ[dÛ [e÷ÛåÚ«7P¿0èBô5ÜØz€¾³bQxƒK<µb@Ÿz›¬œ êÅW€¾ ï£^mÃÕ0í޼HVî΃X?@ÿGî“«6_¼²J­¹í–ëäŽí7Ê;oaþî];dWçVéì¸I®Ü4N—[ÖÞKeõ1GËq©O•«öŽàõ+WÎeUó¬ºTîèBCzãæ20@èú>¡5„ùÌ1€ÖôÀé¨}XŽ%²tʇ èm9ó—ìüÿBÏîÊ8Þ£ñÙýá~ø¨#‡^Aý­Û®•}÷vdïÝ;輂ù;︹æ­Ð{½Ìs‘<&¯Ê ¹Èx#ßçÊ̪høÌ­»äªô·-¢~®ìH‡Å¿Óuéã<‘wœÇQø«.èFî£2tˤ¼æmm:µÎh|ZÞ°,îˆ?@@_èõ›º+rß½9ßì“å7ùµ ×UðÊ _Ø@ï{ ŒU0“¿ÃcV=¨ú€^?þÖ/‰ã™ìù‘Ο]þhDèÔÅÛ ÊŸ–Ç]V¨ép5ía>nÖqº&³ƒª·" _{ùsV¯Éüý|ø¯è«Ök‰m´¥Ü¤Ç¡­ŽåäŽwcÿ¹öeͽÜtnϦØ(˜¿ã¶å†ë®ðü ”o=WŽ[µEæB û©ågIôþ¬Ûäɇ|YnîUù飗€¼VnûÎwåeõû¬2÷œš/€å;¢ùܹVŽÛt_ø€°£}´l|<~XPëº`§üTî“õ¶õ>S-Bë+«~³ÓQôÒuçʯ·ÞŠ€~Å…ß—/\ødðoäè÷÷ãá߇Gÿz¾j½–ÙÆ²é9&ÄÛ~[®Ù™µ½ø¯Û’ù·^/×_³Ynß~C1Л€º(À·æ­_$=w¬ Ó_øUpú¶šµßj|øP ,ËYsÌ¥òÄks8_AôŽ N•«¾¬kn‹}=z4Þ•r“–¥»®° Iù“‡mþpº4*¿>,׎ ‚]AYT‰Ð#„úÊ¡×á<{× ¹ê;ìÍŠ”€¾—”›²ÕÐW¯ùl_`œ³azˆ·¬êÁ(~áz¸.ªãº€uÀ9ôÇžñ˜»ö[rÜߪ”C_¹^ç èŸq¿u­èUo6 Ì«ó·Ý|ܺíÙvùvË&?Ôë æÏ_.O$ í‚èÛd­ Êúï²õå¤i<»äªÓDï”™àááñ×âk~|É/J¹1×å+›1¿ùËÞÜýšû=B¨QG/7ñÍÀwér³èúA6Šõ¹#åFƒtyæüéÛ##}Âñp‘o¬;캱¼Ëlƒ»¬êï¥ÓKº¹áF]8ë(ýÛ•r3Øz«ÚËͧì•Ï|a¯¸|/7½Ôk‰mìè]mAŒ7#Eô ô Ì'ùòITþú«7‡0ý5WÈuWo’­W\Œ»Áß(6Ý;.’ãŽY/w§ì(MÅ ÑÖß»å"m¾û7Ÿ*ǵwËÏb@¿óeöòõ²vó#òÓp½Žõ”Í¡÷}é÷Ì¿6X÷™×eÑ z„P¯@?¥§Ú¸ò@7çÍÞ•rã½Y‘rÐ÷ØmeaÚ‡ñɼ dµÜå†ezgÇä–¾œ‘j_ ¥¯¬¹Æ±ZžYGÞó××(¶þz« ôýìNùhkgøoi ïµ^‹¶ÑÕmåÐ#Ù/I£ØŽ/ßPÝV>º/ì¶2‰Ê+˜sæo¾Nvî¹jcøÁ©-Wlíí¶ÒÝû¯[@ýj¹>l»Eδå­EÁ3ó+w8ØMg‰SpzíUùÙïÆÃ,ë)Ómåm söÕ´kU£Xk7»äªÕ_–ÛH·,=B¨ ×{O(ÓE©›½í&¿ _È@¯`´¬‡¾­óØ•ãÈ×Í„|)öýŸ¸)ðá¿û¥ØL£ÝÁEèU7•×nÝB¼ÙV}9VEæÌo½rƒÜ»û÷²TcTtç€' D“ƪ‰Ô´ú<¾ß¶ô™tœm˜±ßø¤ÛÊå7¶ç…×âÅýäǹíŒo’'æéþ G çK±ý¨ÁW/Û¼€~äëfB€¾R¿ócê¢Æ°uæÐ+¨WЮ"ôfJÍÎ7†Q|/Ì/p§a úªèèz€c€¬—›‡¸ÛÙèõáûwS×~ÁÓ G èzŒzþÐ#ÐW:ùBã/Îe„Fç\ì÷kÈ=B}O@Oþ!ÆãŸ;˹Œ1ƧÃr€c€žs»Bh!*}ÃÐc Ðs.ãIzêc¼`{©è1æBÀ¹ŒzŒ1è1Æ=Æ#ô?÷ÆãjõÏÿÖçBÿÂo/¿ð;ÇË/þÎ òŸßøwO”_R~óIòº·œ$¿ü–“å—ßz²¼þ­Ëåõ‹–Ëù½ÏËU>äù•)å/Èûý/ÈÛŠÐÿãN ý«¸RþçÛ•¿(oxGà?:MþWà_[ü%ùµ?^%ÿ[¹ùeùõÀ¿ñ'«å7Þ¹Z~ó§ËoþééòÞµFþ¯ò¡kåïV>C~ë°3ä·ßsfèßyïY¡ßôg_‘ß}ŸòÙòæ?üçÈ[¿õýçÊ[?p®,úÀy²èƒçÉï}ð«rȇÿå:™Rþðùòûõ5y›ò__ p¸ò…ò‡K.”·/™‘·dFÞñÑvè?š¾H/UþºüñÇÿÍÅÒ ü'_úŸø†¼ó“ß?ýä%ò®OþÛY9TùÓ—Ê»¸T;bƒö™ òž#/ ýÞ£.—?S^¶QÞ÷w›BÿùÑ›å/þ^ù y+ðg¯”þà1W…þб[äCÇm‘¿ªüùdúåeén”­¸)ôßœº-ôÇWÞ,Ÿøâ-¡?yÚ­òÉ/Ý*ŸúÒvùÛU¿|›|Zyõ9âôò™Óo—Ϭ¹]Ž\s‡¹ö9ꌲLùÌŽüÝYÊwÊÑ_¹Sþþì»B·ÎÙú³ç~SŽ9Oy·ûÕÀëî–ãîü{äs_»WŽW¾`œøÄ ÷ʉ3{夙}rR{Ÿœ|Ñ}²\ùë÷Ëç/V~@NYÿ€|á†^qÉC¡O}XV^ªüˆ|qCàË•ÓéòÇäK“U¿%«6}K¾¼éÛ²zsà+¾#§+_ù²æªÇe­ò–'䌭Êÿ(g^ýrÖÕß•³®ù®|åÚý¡Ï¾î{rÎõÊOʹ7¾ñûr^à¯ÞôƒÐë¶=%ën~JοùŸäk·¾õŸååísráms2sÛÓ2³ãiißþLè‹îxV¾®¼ó‡rqç¹Ðëï|^¾q—ò rÉ®Àßü2øÒÝ/†Þp÷K²áž—ä²{~$—ßxÏÙ¨¼÷Dzißeó¾—eó}/Ë÷½"WÜÿŠ\ùÀOä*åÿE¶<¤üªl}øU¹ú‘ }Í£ÿ&×>öÚH Ç×w.ïX.SSË¥C]b€ èz€~€^ÝØ ™Z±›=Æã ô¹óv›¬œZ$+wôxr€þ?¾áðÐ=@Ðôe€¾ß.i¯k9ƒú©%²T¿ñs³Çx #ôÀûÎcÎq<¦@ßh4ä°ÿô€ èúÒ@ßÏ}Vú:–3` nìë—Hcz&w³ï¬X^@#ǰ>D×®W ‘üÎFþ3óqóÂx )7ê|Mß¶ç´úí>‡ƒsÜ{ýoÎa¥kDÀn›Çœßxòièp€1P£ØÄÕ[³øœóÃ. çÆ#ôQt^R ßS"JO„ èúA}¯ËÐǯ엮Ðoê–œÜ2@O4ãyéåfv:8g×ç ù6ÍvŽú€žsÐ'ÑyåC‡†?Š¢ô!Я|"Û:퇻ä°î’§Ãß?Žþ õ]9aÕw»Ó=·[Þ§`þãwGЮé™+g¬@¿|_<Áó{äƒÌÿúOfg|~Ÿ|Øô+È7¦»÷¼¯È›Î{*úã¡í!Ðé¡èÏ=ç@þ?E<¼ èúrèûM¹©šº34 "|–H|’O_èãeä^÷cŒßme˜gOŸËÃ!ðw#ïéxÎaÐôý ÞáUsè«,gˆ@¯çÂGy¸IA§¦—”‹Ð»^ósóÂxýÐgóß‹Îa½Ñl:žsÐëÑyÕ(öCIéÞ¥?uïZi¤Ü¤úoÊ{4 zÓùV ß/qsè_‰ÿÝ#|G”róÁ­¯Øû¯»ÿ&ùͯè‘ûŸÈåG¯•Sãèü½çœ ÐôýýèDè1Æ ïÃR Ð'=Û$0?33#Gyd ô¾(}@ŸÀü3W\æÏŸ´Ïôýóq:Ζ‹2@ŸFè rèz€ ¿n+G+‡c Ðc<¢@ßÎï‘CN:DÚí¶ìÝ»7l[¥/L¹)ô'Å¿# ÷¤Ü|º ÷—ñ%ùµ4þI9¥Ð~íO¢¢^·Á ô/< Ó=@ÐD„~ôz¹Áô Ðwsç÷HcOp“Tÿÿ*°×Þ¥/n[&åæjÙ¥¥Ú<ãúw|Q>°å•.Ä«F±q½.gýa—Êå/8žï2ÙdÐôýhäÐF?ôc€ãú(:¿¦ óúkrè¡Ì»¢ôãû¥Ø ²1øÈit[ Ðô|)Öê7Æx|͹ŒÇÝ> £ó‡*¢ÿöD`oûzìXýa7Ë=™øû¿ÈÆýÐô}@ßÏ}Vú:–S+ÐcŒ1ÆómЇ¯Â×TpõÿΈÒo„žKô}]@ßïu*ñº–S{„!„šOUù°T/èz€ O@zT Ð#„*Éì·Èô=@Ðô=BhÄ€~Ó"4( Çã…Ö¹E=@?×–¦¥kuâáͶÌéWÞdXfž–t\ÓÚ–ë“ežF£)í­h=é„ieþž“v3˜V/C²,c¥V<]n]ñòJnKX®¹ü¸f{Ž;4èêèBh!\÷ô&'Àëzßoßr‹ Þ¶ ÄXžk7³Ë çkI«C¶wXÓ¾}Vô‹¶%ó`¡-_ý]øÔ‚@@è ô l”Ö ôÀ^<>†çN~ø öuèO–Õ‰Ç%­¶ë%ŽúWzýMB=B=BÍлRIêú"¶¥¶hËPàÞÈDÄáÒN¢ðI”=³~Ôo Âeû"ôŽr…)<–Ô„z„z„Ð#å¦,Ðû"ø ØM ÏÌ£¥ÀhÃèδ 0!½ò¶ØÊØ#€!€!„æè˦žÌKÊMP.= /IÔ>=SlÌò™^]ëêèË<° Ð#Ð#„ú¡äЧ©*ÙhxÏIº2ЫåõÞ(¶Û6êÑ&ÿÛ(°×ôj¸¾òÒà@@è–C¯E˜}i)f·•æ2\¹ùRô®n+s=æ8Þ"$"»ûúÂmI¶'î.³Ê6"Ð#Ð#„z>,…è@Bý<*J¿i¸>Ö„ª è«!€!„Æè<ˆ1ÆÏ«z„@ÐcŒ=®¡§Î0@B=ÆxÄ€~Ób Ð#„Ð(ýþu²ØˆÒ-Û_¼NöëßdXfže²Ý5­m¹¾‹»ežFc±¬»dY´žtÚí²¬1-·Øe\|êJc9Á2ö›ëÛ/ëÔüå^¼n?7] Ðc¼>ùp`Q×Ë6Ñq}uIÝ"€¾èu¨Õ¡Ýô¾ß¾åA½m9±·/ëÂõþu‹³ËqmCfÝúA2Í2Y¶X‡}õ `ü½l;7] Ðc<Š@ot)ܬ«W×È{™R ³–íè¡.{©ÛaÖy¯ëâa 8Ðo_& ±u}°a{{ɇ‡Ì4jÞ,Ы‡õ€ ?(ئàÇx4> ‡,¶:ƒ¶ª_èë«ËI­[€ |ÊM¡Ð³-åF[†‚pkúLЫ‡”LYTºM¼cœüp½Dæ1@ñˆ½CPjIKûè_Êù™ˆ¾ñ0 ÝwZØ2ºXn®’U™¯“Û"ÎÙ6W­ —ÕnU)û-„ýñ¼1$–^Wfú¨.Ã/ÂçÊdl“h~`RìËËFçí¨¼Œ’u«Árf{¬_·+¶2ØÊš¤ùö‰íøêXêôc“rSè}|ßeÞñPŸÞL³Ñרc€ãQúNrzd>„* ¦BèRÖÉ>¤ÐÁU&¸ï‹«å%ë¿}Qd¬;É—Ï]åÉCpÊ}óV]—9½-ß=™KCêÎB³ú£hyu/õ½™íënw§e< ¸Ž•\e-³OlÇz€~(@Ÿ@÷H¦ÜåÊEÛ "ô–üù(Òo4¤µ6~%ô|£Ø$Rša”â(ún#ÒDZm`åMëè‚›z °‚§o~¦såñA_ѼýN_ôFÀ·M®åU©—:–¡}w¤ùøŽÛrm©C½Ô1@Ð-‡>ü;¹A8‰XWzµ¼ÞÅvÂF½ÓTj»]ï%Ç2²Íj>="_ô‚1@ñˆôrƒvYHóUAžvò{ÊOeè,xP©2oÕuYAU‹X»¶cl€ÞLy)x`êè«€;@Ð>‡^‹H»RWlÝVšËpåæ÷Úm¥¯×šÂF±ZþýNW**_Ü•e•2c Ðc¤5[­Ìøt› Å–zémeêvÎÞ¨9÷æ:V S‘mÊ_K}"€~<>J¿iØ¢ñDÄ1®諘:Ãcÿa©a¤2˜©0Ã’«.š¿ce¾ŽÐ'3#„B£pc7 w6†кœfÑ<+Iô¿Ïe!€ G©j„¡±z„èB“ôƒ˜!€!„F è-}§š_ZËLkûº[™¾l [óÏ œŒ¯Áå†Û@5Û»K.§ûñˆLƒ[+ÿ_tC“ ôúx€ô!4Î@_¶ßÚ²}Òº–Û(™wXЕVWæg·ƒ¿mݘy·A}öÙÞïn¦Ë®ä¡£Eã4@o¦Ùô G¡Iú¤}Ý@ïó~€ÞÓH¨ì6X?om>, 4¾@oº-o G=B3лRYêú"÷½™BO{°…þ+lCÚkA•¯ø!4F@oþ¶A>B=B+Ð#å¦_ /ó‰lì{Ùëökùý@>c ÷õhУqzu?Ããùöh}Ýc‘r“£úìC•m(g¾Õö¤ÜµGc ô&ÔM‹Ð¸½ºŸaŒñ|¹—7‹ÃË¡wmØH4—k^Þ£0ÔÚ(V ÷Eä+}ú¡W£X€1ÐA;@ÆèB¨ŸkP¯= o8¾DgŽÓAØÒýcÃ;¬BÓ29ôáòÌ®. "êeºÛ4>ãL·•h’€¾®i5 ÿ¹7‡1Æ•¬_ƒª*¹þŒÐ#„z€ôã ôU£ó}R[\‹BÕôUŒ@ïñáwÉܳwÉ»Æ}ãÉúƒbŒ1ÆóêÚ€>ÂgËEÏ‹N®]°­†kåš»ììÞá<™§g°ß,wYçs 7×nKPo‡kóÉrlÙòœúD¾4{O0‚ Ðôãò®¡§ÎðØ}ð‡0Òm]¦^€ïÙÜ?Ð÷P¾w]v ªc~×ð °kÓÎõxש=”'„ùî²l0?wϼÀ=@1® ôƒ˜ãÑúF“¨p—áo HS°MàÖ¶M›»èm«0´¨ý¶œë2#ü¹²eÆ»¹ð-‚¤½§û@rlÜÇ&ug«Çøß‹îé7}9¶‡ s”xÞôm ß¿N^¶=¾xì×/¾É°Ì<Ëd»kZÛröý²n±½Ÿº2·¬Fc±¬ÛŸ_Ïâu;ÝËY·ß2ý~nª èñ}!Ž£ã*‚¦¢ ‡àêú\äY-×1þØn“ȼ Ú­ëR’ˆ;ƇæzúúîƒPðrO¼ÎxZ{=jåUu£•S—^æ¾Ò0r„¾æGèuסÝô¾ß¾åB}A™rÓn—e Ø'/Ûî^NÑôô/ЧÑráÝãL„ÛôEQs_þ¸-寵®¢áÖwO”¾ Ç%,aÙ“ú³Ô£÷ÁĨÛt_XvPG<åÆóãôÛ—ICAnÝ@ïó~€~™l/»œ¢é1è1c Ó>NMÀÔ²Øû`Z,ùø¥¢ËØŽ Ð'eHºQõ'‚í×ÒVz³M½øà1Ì¡wÁü˜¤Üh©,u}Y vD÷3¯?âñÛ—ÅÛ"í–2x§Ç Çx¬>‰’ÛRXîJ{]ÑzÀ1„Ú"ñ®á™è¶3ʯÁ½m]F£ÚüºŸr“<Íé®r矵¤ÊxÞj„^õém=¦Q옧Üô ôE‘}õfÁuß|¶é1è1o ·¥~Xr¼ðÔ¿ê=°«5úL‡[aÔL)ÉF¥“å¨õ¹Ö•éêѶ®’b³ y‡Ëðܼ¹7¾Ü~ ëuáh Ðc€~H@Ÿ@÷X¤Ü¤àÎG èz\¤~– Ðô!4H W70óÞi%7È–´šx7sl›N‡ìx\/75sYÎu)ˆÕ‚¿u˜q‚º§ì½½ù0c°^4Ø·-ß·^cœzÐJ@DíÓ4¿àä<—½ü?8ÚŽÙQz_Ùúç°>æ°l€ G¡Á½ŠP;n`ñ ¨£E¬4¶ÚÙSÑÒRôhY£–+}ð[GB¬ ÷À‹eÝÞ²×ôI¹\ÃkzIh:€ÃÅy÷¹l>X—=6ãáí–ëM“v¾™‘sí!VŸ?zè0ÞR¥ÖÑðVg.}³–{0-:WœoìºÛž)ƒãœÌO3—yÛ×lo ¦5®á::öá»9/z„ÐëQb'07ýÌ Uƒ=ª—DòM€°½./ºÉÙ€>y¸èä‡9Ó r à+{}@‚-Bo®ÓV/®ôÏŠ7… M0Ï{ÎeÐ'ƾc6Õ¬vŸ¥›ñ¹–ž} o¸ßéëohçbÃr^ú®!Öë“ñ€Ÿ)Swxæ F¦<–iœoÙŒ‡)Ûp"ô=B èKD€P6:;¿–ò’Ü Û­âüô*€lûm[f’GìK¹ñ•½Îz´—©—Jz†ÐÂúŠçVæ¡ØuÌúŽÓ\]Ó¹Î%G$Îw¾—=WŒ‡‰üÃpôðak'Ö‡kçCt#—wŸÐô!40 Ïå{@Úξ>×›šÓäÒcjúxÙf PvÛ,:ã†ë+{Ï@_çf4µ¶”›¸,à°å9—íjqô¸,ÐwîÖé-)xƒzïy`B»-eɘÆ<È5¤5‡ô=Bɼ5ŠõÝ䣜ÒVǸ‰YÓr<°ÜЧ7Üd¥#òeË>  OoøõåÐwÂZ¶ -yÅúÚ³”>þ|)7 îz¨Лi@Út­nAí5¾i¬½IYä›Ãz€!„dÀÝVæ¢Õ–¼Z)о¥72[°¸¡X®{¼º­´EÈ,Ö¶ü}/Àhe÷vYЭ_8OsÞw{º­t-Ëì¶ÒÕƒZ€Poé¶ÒúL÷ŒU(]bÍî2׈´‹º‹ÐçÊš¤%Q÷F&‚ží®Ò>^þ¤Ql«c> wìÃz€!„„K!„jT7_| /ìÿ~AÕèBha}6"V*zO™êãØ­÷ ŽyoŒÝÛ!>ÄÐ#„ÐýÁƒ1Æãy5@èzŒ1Æ=BôcŒ1@BãôÛ—eóÀ—ÍʺŶüð†,>u¥,Î _,ëöËÙ¿.3nñºîå¬Ûo¿È'ËX¶=3|û²`Øâu²ßXGºþóÅï_·Ø^ŽÜrãmJêG[F4­1>SÛ£iôy’ù|ÛPeyñ4˶Ü4mË*[Eû  ®Üíÿ\ÝûßS/…Û‡9—9—Çö\®èÕýM7B-( nËd{æb®ýmÞ|l7·ÐÛe™~#Së7 ç|X¼L–-Ö–[ܽQØ–S4_²­™yU™ã’m;µzQ7¸ä†¤~'71gýA€£.J-Ï„‡}û¶¸.Šö¿®ýûÄ6ÌR/1õå̹̹Ù祡¾è3_^¶~ØU·~¥Ø5¯ñM“`ÚÝ™/>ÇÓ™eÍE£³_K׿pœLÛÖ¾Ñýˆ´e]æ²ÔWÞ“ue<´o8”©c[Ã2ؾ>–·%­f2]·<é<Úøìpߣ³uÕG´Në¾Fh|Rn¯kû…ßkî*7/=2Uåj›¯ÊkzWDÌú*¿Î×ôå–Þ m‘Ó²Ëêµ.ÊŽ·4Òk8ò›ÕtÞ RnJšs™syüÎå:Þ,cß@oÀk§¥~!³F‚#hMóàÓi<óú"ÊÉ—— Þ³œj[m™¶éô¯<;–›ùªr:}™z‰ <Û> [W!T'ÛšÀwض@ûîí¡!œ&X®Nå2ëú¯+ ¯±!î‹âõuDÄÌSÙ†tadÒË:æ¥!]Üㄳw‘ZÒõ³¢òeÀ%¯ê­ ÕðŽ£QìÅr.s.é\i O£ÉæÒ¯ øó·k^Ûb]ó 춨/5!€ G=B- ¿¶fï¶*Óâ=íb¨²@ž|¨A5²Iæ[Êw’ñöᙇ Ü]ÃB=èBh}ÃÖ¯Ãå»ó2ßó]>ÅËí8ú‘íTì7–\:„zÐ#„ЂzÑ>ç\ø [:ŒØeˆ"ëyèw èè@B}Ðç@¾ /ì0gÞî®á=B=èB /z‰s×Ûý¦Ü8æ3’/»èè@B}) O¿–6ÀF±Ý¯ÙÏ@»†ôô G!€¾4ÐK”öâé¶²Õ)W`s¾´ÛJÛç£]à ÊÐkÙèzÐ#„Ðd=B è@B èãôœœ}ùó¡a}#Ð#„ÐýÁƒ1Æãy5@èzŒ1Æ}(uÓB=Æxò½,^¼Nöc¹½®«ê|U¦¯kû‡Yƒ¨ËyÜ_u}ðêÞ¦{äÀ>üÎ 8XEou ¯èÕXË£]¼n§¬[lϱ]¼liðï2ٞο]–©¿·/³¯zsM×Ï–g¿ûf®W+ã²íåoNÉ0cû•—m·×MäŲn¿­Þöˆx~áÜu¬:æÙ¿n±û¸íR-ó†ë:ueõeè­Û?âõØWTXÞ°öW¿@¯ƒ¼)×p+Ljû»YW#° ¤}Ÿ¥æõ ~È!€¾$Ð+ðÖoVÁß Š=çí˺ðª.ú üZ‡÷s3/“e‹µòiãÕ:²ª އˆ" ×ÇÅ7tù ê ã1ˆ¶o_æÕšAÔ»®yzk™F¼‡¡Öþª è]z奧‹Âæh"|ntÅL”›=èôY(.¾8Ç0»ÝgÞ7пU>åtü~Y·ØqÃéèËD´Šê ãQúð!Õ<_ºÇpôpG(ÓóKûtI0­ö*]Þvûð¶kƒö°ÿ»n™å­ØÁh™éðYσ·~­Ð#êÖáîíÏÔÏH×£¾í*Øaykh«#ÛõÌUgCÞ_ý}Ù^ÛüP¡Ü ˜-i5-ß9ÉDô‡ý»(õ¯®kuÝ\%«2O´DïcÀm·Š¾¿’Ì}1}˰jU7òÙ>m{ÛaÖQ†Ì|Ú²´újuºåIç±Õ§-Bo+W¦>º©ìÖ)ˆ”›0²ž¦«”„„èÆ•êÜð:€>.c&bÞ@]Ðë74óõ»V&g½a< )7€2Î×äAÙ8—·\pNg$§VãÙ‘Wmk/’nÌk} ÷çÁ €èžË…×ÛѯG=úŸËmOʽ/z>äý5 @Ÿãìs€GÑwgóíÓ(µ ˆ½9í]pÍ· °l-:íZŸý–ñ¶mGQŒ­\æöÛ~ûêS?W¶~“7€<è.ˆj™ am¯iõáu}’—¿NòÌgÊ )8xœÞ–²f©myñÃk®¨9<3¯%â<, wÖGÅ”½‘¨GOkúáz´‹´LxA#Õä})? 7#ÝE FÚã· UÍÎЗ­_ÀôïÎ^|}yGt)ŠHe_·Z‡×ôé²—F±–€p~k¯8Ú+aß µÌ› ŒG1‡Þ8Ï–¥`iÀœ­ç*[ƒpsxÙ7m¹²¤phåIÓLŒòd‡——8çç»=åÏ\ËludM¹±ÕÙð÷×¼7ŠU h¦ÚØÀ2MyɦœxÓSŠz‰£ÑÖ^u|@¯ S^2‘{=§=;]«ÕÒÖåÚŽ>Rnª½žŠ“K¹)÷Æ¢Õ}ª ÊÉêÑ‚Ðï7º¨4¢M¶ÜJ[dÈ5<÷Ú» ®Ùm¥-²eÞÈlÝVÚÖYªá\#Ÿ2dí® Þ0¥n+-9×Ýèp¶Q£5NÚ‰$939Ô)|Z†©"¹ˆs…®i#K3í$xxOçѺ¯Í wœó¹n&uøÑzôîkÇ51­ [½;êlØû« WÐnw×p´6l0]é(¾”k£X_ŽºD=)7­¡lSAº6]2.m+öœsïvx¦ñ7Š-z[}6ŠmdÁ?™¯¹«Q„&'åc<évæùºz.“Þ@vÌ·mÜöW_ŠM¾ÈqþºQt5tDÑ-%B“ôÙHQnŒ‡}þ «½Ç(¶-É—©·>×'±ÝÌð·©N 7µ6†èè«=B!4 7¶‘z„u ¯2=ÆcÝ„í¿ÎŠE™ëÖÔŠm<\Ы›£n„ZX@¯_àdá‚ݳ³Ó=>L„%C|è‰Ð¡_@^·2Û²MVNÏÓ¶ô9W7FÝ#öVú Øê x]smi6Û2WuÜ ¶µ—u³¾ÆMÃØ‡hÌ>Œvp_9­Eí3Ñü8"nF¬âeÙ†}O¿ Nuç÷¿5ÐÖ–GO­QåÕ¢üo:TÞ¬ƒ„>Î!6"Çe¶7»F¤ÎVv_}gÖ¿D–N5ò©C¶íÈE “ò•É^ïÖe9ëÜ]&ïÛ£þsLj«\óÛÜMµÒÞØõ\¼-–7=™Fµ.c¿æ(ãs¨ ^ÇbÅsí{ÞcÙ‘†Våx/³¯}çÅÑGõT÷Ååw+ƒ¹nYŽ+çñ^õ²Mï9†z:Ï{¸^ùöMÛÛ/Ðë oÊ5Ü GÚ66Ûs€®Ž´M©´è~ ­N ¯\m[“yûYÊë- O#PñÅ7 IÚ_‡mxe· +Q=YoX•Šc–G“`f—SQe·×[޲—zý&¦#©õ;¶Ã–¾”¬+W¦Ûa[–³ÎË”© þÇ¥Êl³ë·%öm±ÕOÊ—åK -e™6–mKÓëÕ ôÎ5лÎW}xë¡ÄñW¡¯R÷¥Ëï8VyÝrn{Õm)9}ÝçyÕë•sÿ×·½u½K¯¼ôtEp þD88|hhI§x·½m[{yˆ¨Z_DèÑ‚zWôÅ„L®}#œ\„² âãº1Z×k:×+ý2à^v¸u{+¤Ø\Ê®?‰n(Åm¸pÊnGÅ·ßþ,œ×rŒ”©g´¸ ¸.õ e¯ŸÙéSØK`ÜëÚ|…õZ¹L%Ï«^÷me–U5å¦h;{)¿ï|ÈuK‹v÷²-e§Äy^×õª¦ííè‹`¾Ô+ ·€cL-i5ù´LDßxж±µ1®ìðFs•¬jêi$¶hôœ´›Æ[ƒÌzµ2Û†çÞäËÕnå·m®ÝÔö‘=Ên›/[ŸÚ¶¾m‰¼ÍŒ$;ëϱŒd~Ç6Få±Á¿o»µeXê4Sq¹mähßþé_­NwwS”,ÇŸ+BïÛ7ƨ %å¦àBîm˜Zöéyu]ЛQ½^A¿_pЛÊ¢ú(|¨³,k @o9FŠêÀ·Í)È‘ÀÊåɦ!…Ë ÇÇ©6ë—8¢§I*ŽÁ/ª×Êä=¯êØ?}_*}¯ëìåx×­^·¥Êôuç½\¯Ê^GûØÞùz•ªƒŽN§!i°æs+3RP€,¸&Äê ¥–—L¬ÿö¦ìàþá^T.øÔúm—”K‡Æ2óÙ¶{N¸±Õ_Ûn–GòDÞíö.·[ÆN+ømVfÞuÄÇWxli¿Óº¶> ·î›ìú‰ìôùW¯ÚtKÓ^âÜø –a;Ê6Šõ¤ÜTzóæëxÕžI1*³½Þ¹%Snʬ?óªÚÈûµLŸi¬ç)Så´+gJˆ¥LøÉ-ÓqÜXëÀ¹õÆQ@k¯#ÞmqÔ*cP®dyKÂÔWu’š“®¿¨^]ÇB•smGM@ï¬߾ޢÔÎú6Ú¿”®û^Êo+ƒ¼nÕ²-½m{_çy/×+×u´Æí Ïlö9ð‰£è»hguµ’è5°UV(õÍŸfÑð¢rÙÖc‹ŽÛ–UÆà™«¿:¶½(%¥¨ü)´'Qv½\¶aÙ¨}sÕªrë4Ëî}²žýœ+מT€¾øÕo¶Qjts· +îþ-“êh[è-ÝbZ¾M)sEJ6œ®ÖÈ̹~×ërm;ÒéµõšÃýe²o‡uY¾:w”ÉÕ-i~™–cÄUÚz}Ûìml½£‡Ô%fŠz€²D8‹êÕz,T:×êzO=8÷µ¿‘hz ÆbK×}Ùò+úneàªlK™ék>Ï{¹^¹®£umïH½ÚE@U -ã#w¤üÌЛQá²ÐXèËå¼½âí`_iÃzó- @?Á@¿><Õs?÷te8u0¤.WñœSœ/µÏçµQ¬3ÕÆH®†¢Ð'Ñpg¯:À¬9å¦D47\VÝ@﬿SnœpZb»=ËmuŸj‚åD 6×gÊM ×Sqª½Ù> èÇÑzd«1_ýÔ(C«kcX<Ð>Ùç­¾9_&ê:Ú/Ð+h·»k¸ ¬ A]i!®F†Æðn£XH¹À0ŸßßW£X_¹Œi;Z£Êf«U„¾l#MkdÛ×ð×õ@äÙng£ØlÃÖèË6¬Ä:;%÷­¾lÇ_e —Lþ4Šè1Æã‘þRlðå@¾J„x@r5*EhPÇŸ«6è1ÆãQú±ª\=pƒ{üéo]ôíÐcŒ‡˜R¥±º¥ñ¢ù…]öèB Çè«öómë^°töô!ÐcŒG#B_æ#fô!ÐcŒ{ó%²t*É_4¢èO”øŠlaÿâÙþÓo:TÞì]†ñ¡¨‚~Ë1èB Çxa}‰/ýºÞøÂf™È½uZôÞñ¥ÚèKÍì3 Ð#„@1ö¡VAtY /óÕÔ" Ï}U5éO<‰ìò G!€c<Ú@ïM§ì1@B=ƸЇ)7Ó3¹F¬¹TœLºŒ%e§rÊå+ºÁø¥+¶Åo“•SZ~?pz„è1Æþ­zƒÔ©é%ֿ|:]% w•#Û˜v*{€ô!ÐcŒ1Æ=BôcŒz„è1Æc€!„zŒ1Æx¤^Ýu#„@1ÆÐ'¯nŒºGì;­´z«3æD2IÛR·æÚÒl¶eŽš@=Æc€¾Ì'oÊ5Ü `ZOÍöÜÀ®#­FSÚsCÉBå¶eœAÞü—šA=Æc€¾è]z奧+Âið÷ BÎáCCK:ÃÊa€ë ·…=è1ÆãÉú"˜/õ è-pBYKZÍF>µ$Ñ7´HkcvÙáæ*YÕÔSUïÌzâ2:Ë5'mmXsÕªl´Þºœ¶´[ùí›k7µ²š`jnK‰ö.ßÿöºÊLo[®£|þ2;ÖÙ)WÇ­N·nÓylûÀ¡÷Ôµùv¨Áƒ@1ÆôÕ£ô´ôÈ|Zd‡9ã øŒ€4A_&¸ïKQËK&Öð™lõ‡³\®õù–£•W-'…óîöuZ®‡=•¨Äò³ °×UÃ|Èq-×(_™2ÛÖY¦ŽÃúÕ~§udÙ> ·Õµ±~"û=7Œ1Æ}Ïi7:˜5ºP—«8Š¾Ûˆ¨&‘]ŒysÚ»ê"¾æô ´ºÊ5çX_ÙåÌ™o<)5¾ír–³ %ÅY3ÍÇV>{™õ¨}æE•:.S§ú±á‰ÐϹríIÕè{zTNª‚©ÃÁÕ!ÆÐë ]mU!Õ2>y_ÊÏ|½’çè}å+YæQú4RÐô==7¿“ ¦W‡ãñ?Çëð¼7ŠUe¦ÚØ Ì•&Q”Fâëu&NŰ÷ªãI±–Ë€Þ2)7ލq+-Ú&Gn™”'œÔUÁrsåÛ]¢ÌUSnª½žŠSèͶ=@Ðô=ÆxØçø¨ôr£ Ýî®á6Ðk˜\ÍÆŠfÞ¼­!£1¼Û(Ök.ø´­ÇÖP4NÚ”mëN¹ñtáil,ß»mF]ù–k-_‰2ÛÖÙ)QvÐÛöAe —Lþ4Šèz€ Ç/H ·|9/âü!DKÍ”‹Q)šŸ}`mz` è1Æ èÇÚr9ô#ø †¶:-÷ÛÐô=@1èB F©C€c Ð#„@ïòú%ék¦¥ëz€O”w,—©©åÒ÷u`€!„&è5==Óã²fdic‘¬Ü1š7åA}gÅ¢L=N­Ø6qÐU èk;ž³ý»ŸrËÛ&+§‚óqCëq•Y ·Õi/Û˜ÌÓsý×!ë|ŽáæzÂm‰¯céum‰Ì–-vüÛŽ­Ùé`Ü?°ô!4†@ÁMp³ÊÜp²Wº7zœwÌ>¬G¦¦çé¦=@_ëñ4€íŸìýüŽÖejòÉßÉC\?@ßÏCµ1¿kxصi§‚zLß0V)Oóîã]ÁüTp>Lô!Ð×£x£nñM*¾¹­œn©5ꦨEeßt¨¼9¹iÑ»†-úfDå¢åkëLç÷”wÞ€Þ_=fß`äë±ÌôQ½d#ÍÉúÕ>5"¥5Õ_™:tOe÷wÙýï¬?Ëöå~²oz}Ø·#„Ñd›¸L—×}¦,¾óÐUn'ÐÛ ÖØÆ£r®«ða«›^Þ"8@zvº[ŸáhRw¶zôúrœûfÄSŠz„7 ÷FÔ#@OnTáÍ]EÝâk 5*"¥ßts¿³Ëq¾Nׇgòï Xèãf80 ÷–ÉQ~g=–œÞ´FEë«¿2uè<žÊîï²û¿ìq8Vû©ÄùVôúòU48œ/S¶n™Cpõ½u¹Žñ³ÓŽô*ë5ÁWtpÔM@ß}JÞØtÓsìõè:&™t.½Ì}¥!ô!Ð÷¢Ö¼Ò°ý.ˆÌ–nÜ$=¦õ 赨¤vóÏ•¿p §wE5‹"Ÿ}Ô_) ¯r<Dˆ½û¿ìq8Nû©ÌùVêm‘Ú¿jû²Qþ.pzðªDÍ}ù㶇–^¯®º©èÃqÉKXö¤þ,õè}01ê6Ýæ±Õ'@B} 0êIA¨ èõÈi? ?Ò9ô–z,jØWvûË6Ü+ÚCÊ¡¯t<•ú*õçÝæ1ÚO=}œö±>S[}h`ï}`±”»Ty4° OÊ\¯ºQõ%Áök i«½+¥‘=BôÃkëI¨ô&D9^ÿ§ðQG˜CmëJ'(ÒªÓ[ÖŸ©Ûú꯯F±e÷wÙý_Y¶߸ì'çùV”r£EÉm),KÓ6Z8%ö‹ûüÔ¢ÛEûÂu ׈üºŸr“<Mé®rç§,©2ž}6zÕ§·‡=BôƒQ[7ƒžFzåÞÒ¡¥Ñ]ÚóC•”…qé¶²J£à ©³Z£<³çŒd\¾±e]æ•é¶Òu<•Úße÷Á|™íËýä8ßʽ³ÝD6Ç;Ùnë~q•Û £fJI¶lú6ºÖ•éêѶ®’bÍ}›y° /|Ø«Òþ'W%ßVô!ÐÝG‘ôF|XŠKaŒz„èGFõH_cû©èzŒ1@BýÃ(uÐcÌ9Ð#„@Ðôc€ Gô=@ÐcŒz„Zp@¯€ý.z꨿:Ä·ë¼±ô!€¾"Ыq9S‡ƒ«CŒ1&Bè{zŒ1Æ G!€cŒ1èB Çc Ðô!€žÆc€!„zŒ1Æ G!€cŒ1èB Çc Ð#„@ïøºÆc< èB}@Ï0ÆÊÇçz„@ß#Лšk7¥ÑhäÜlÏ…ã;-c\«“ÎëבVòw§•ŽGϵ¥™[_SÔêºei‰š´ÝÌ.7SÖfSZÍv© L—“ÎÛ.Þ[9µm÷Êœ×2_»m£½¼îq…ëªPŸú¾)»¬ûÁµ‚õ6›ÝúÎkå¶3·’rZö¯>ܾoãc3þ;š&(G¼ÍÍU«¼Ûî¬3„På@èkú±•«’@B G!€~4(¾]Õ£¦!„z„èB!€!„z„B G!€!„@B=@B G!€!„èBhDÞ켞>µ;™¾É­ ûý®Ðßz]e2úÙˆ2ý¸7¥ÝnåÖW[?ôªÏnî½/3íã}Ϻ¡Ÿ#rìNÊþC=B-$ O¡î>ÝÐwÔƒËpŠ£àKëR3ù`Ñ ~@ê·,ß–;ÎÆûØícô!Ð÷ ôÆ—13`á—€V:Mr]ÑßèmA3úº¨%™y›Ðj¿H¡¤áþB©g¢ù¢{é7V©j@ßk}šoZ­<8šeÉDm“ùµc'»:Ùù3u]á;™ùÜû.·}Ö}G»›Í´ìÉñ‘ÿ:²e?”¨³zºú±ëÛGÕα@z„{ Ï¥hÑgß8èÄcƒ…F’éÕ2’ré¿“é*¼U°F9‹¶Aª§¨õ8Þ )ˆùÆ•†¿ õéx“PôùaÚ±c|½7–Ú|Æþô}'±¹‡[%»ÑoUÆpy–åä·½\‰÷! úM/Ç®sùŽ ßþC G¡1úš,Ñæ.|ã2S-Ýié Ó-W”«¥ Y¡¨hrå)ŸÃÐ÷RŸeSUª½­ŽÓi­,åêÖlçQ è}ûV›'­Ÿ2@?Oé=½»~ wŸceèèBhœ€Þ0>Ê€‚¡w€we5S;êˆÐû¶¡ wDù‡‘C_©>Ãr¯ÇôÝeföƒ¹¯Cˆ×"ô½´Ó0ç³Eèõc¤QâøìèKÖÙP€¾àØuî#ß1áÛô!4º@oF?Í(h7Ÿ¸›W«ƒª}œÖËMÇÈ?÷¤tËADš?/7SÖXÊFèsyȹ´Ë6ØÊY6:kÎk™¯6 ï£>mQó¢}dÝ®}¬7ÌÕÎäo;òÁ}H-*’¾³oWlûOë1&ø;M+I/¯ZåOqÕÙ€ÔÓ±ëÙG•Î1cÿ!Ð#„ЈýØÊùG!Ð#„@?š¯õÇ]5!„@B=B!Ð#„@B ïÞÇ.8ÿlŒ1žXô!„&èBh!\÷z„B ôc¼P Ð#„è1Æ 7Ôlˆ4”›"søwà²]BÎÓ5Zãsi5[ÞA/ÔÖ?‰õÓÚyÑÖov©Ôô”É:.9·ªŽ+ªÏ|¶s³YáÜ i×$åùìÚjÜ®u=Æ/¤}'ƒoŽáMKÝlË~á§Žíó­o¾ëoØëŸ¨úìÄ·Ü÷ ô ¾;=Œ«½þ;ãôí¦ íSµU®! Çã±úL´²•犪›°‚¢¦#²އ·Zñ¿r7\[Y:­lÏ»¾N€’ñ¶èŸm\ÑúÔò[Ú¸ŽùF$Žúšã3ËÕ 27ÎQ~×ö9ÙαÌJû¯×õ¹¶»b}úêÌW×¾2–‰WŽÐ÷9®jYœÑûø|o¹êºìñi‰ü[çëd÷M²Œ2Mã˜HÖ©¿µI® ÉòŠŽ]óíK+~`(:}oC\Çu¥²´Æ3ÐcŒñ˜}Kƒ$츀"tuãJÖ¡ÿN¦+1ô•Å]SÓ™ëk :¢ìØv_Ì×ùæ|Ž:ûÙ>×2}û¯×õåÒ,‘ðªõYTg½ìoa. ·A_p¾qóôÎqütÓ(§«Î|Ç‹w>óØ2¼z‰Ð7[îk˜ëØu½}éTˆÐ7[ÕŽë–çZPæÚ ÐcŒ1@_ Л Rèõ›v'AË,£B €¯,®›± †š"bÙ>ÐXP›NŠëÛÞÏö¹–Y´ÿzYŸ XôeõRŸEuV')Bïzóx, ܾ㥨®›­ÞÒZzzDZ[´î^€¾è¸v•¥ìµ Çc€¾t/7M#jålß2}û¯Ÿõ™°Ôrweë³èXíèÍkeí»QìˆEèÍ4°NÅãÓ¡÷Ôu2}Õýázãa· ЇÛÞêíœöEè}ǵèëÚÿ=ÆôÖ¼Ò9ãæo‹"eæ1æM#y­l.lrãÓ#}Ͳ@ßñ縚9·Wd±•ïé£j½o}f®­¾í¶:ÓaÀŒ€6lí\¹ºžís®c™Uö_?ë3A¨j}úꬨ® Óƒ æ« æ®|ð¢qe˨Ÿ¾q­øœ+{NëÛî:^ÊÖu³Q-:oÖ‹íœmhÛ£–]tìÚÞêt ŽA_}úŽkoYJ\Ï|×€cŒú‘’ñEM¦šUß– "ôc Ð0ÐwÜ9Ï¡ ùÞ’ €cŒz¾‹B Ç“oÅ6¸š9nz„B ¯Á¨œF!Уr*ô¨|]ô!„z€žhe v) ôÔW9,ôÔW¹ºèBô=pÐô=@B èzê è•æÚÒ N˜F覴Û-iÖÒ÷qGZérÙe†ëlÉÐú¼öú’h©mþzB èz Ð/ WÐÝì~»$†û:€~®Ý¤—:Q߇èB œz€ Ðw ¢ñÁø†eO]ÁºŠæ7›ö|[¹ µQÔÚ¹Ž–oµ¢°ÒL¤;y£ }*Z_ôvÁ,«k}EÛà*K™™î|,Ð÷XŸÙùš“úñH„@Ðc€ è«½I'ÐçÒT²Ñü”»DÀh»R„>¹6–.?íFYŒõ¥·Ø"åÎm((‹÷Èœ/ó ÒC}šóYê!„z€~¨Þ{©¬^u©ÜÐW÷Žå25µ\:ýXÖñøEè-ãtHï´ôHq°«}þá"»Ìb ÷G®í@o߆¢²ø¢óæ6§ëíµ>¨>Qz„@Ð÷ ãÇ-Çiž¹µ‡e¬¾NžÿÙ‹27É@¯À0w^"³EÀè¿~ý’lNÏôÍ¡#Å!xªH°A§:tÖ ôfä9LCÉ}w}渺¾¨,e#ôŒkú^꓈ØOê!žÎ[?3²´a©kÇ2ЪÌh7eA!€ /ôŽ ÿ®¼ü3µ¦WåoÊM0íw^ym.?wà`´£¿\ŽÛôˆ¼üTôp0skð` EõÏ\ýs×ÉõÐð\0¿ ¦]`)7ñø8Uj‰íÑøn4:ŒL»æ™D 7¶¿0-G¥â8ëG=˜? ðµåZýü‘³Öwý|ç‰RYB èK¤×Äùð/¼&nÀ¶¦ÜÜ&O>eýӷɹ-²zÕ&y2x8øésæCÁErçS/ÊÏ̇†¡ï8‡'e t1g^¾¨­þ°TªNkhãÐ#„BýèFèçä¡MGûS`rùïêAà"y,Œ¶ó=¤]ÒÜúj¸™//ã±;×Êq›î«æ'èÍT lN2ЇÛíH¹ a\W¹NKì€!„èÇ5‡þ…×È]g%9ô@¯ràWm‘¹Ûdm¹"û÷«|úpºÇåšÕGËÆGíyøw¯?Zξ ·þ]ºb[6Ÿ|ÃBzO£Xc»Ãé|uêL¹±´AèB!€~2z¹Q9ôËæ³¶ÉãÞú8>€ó;ÛÝá«/8WÖ¬¹M(ðß»IÎp5Š}âzY ;ëÆ}½3å&ò”‚û èó_»ÝVÎj _§È÷Ö©%Å&ß(Ö‘¶Ð#„Býx÷Coôr£üTœV“ª›åßGúÙk¯†½ßÌYæùéK/vÇ=5˜ýÙïòa)¾Ë—bz„B=@z€ ï£ú–´ >5² ·cøý³G}ËÓ/³Ç|üÆÃÚÝiRNÏ̧Arfx~}Ý/°fÁº¨œ™/°¶ZݯÏ>[43_{ͽQÖîGk{¯3„èz€ èz+Ð+Èt½™Â¢þÖ¾–Bt—V»ã æë¸%ªÝédÀ8S¶z[¤Ü[Ns{Ê| Öøjlf[ri?Ù7 ýÔBô“ôêÞŒý. ôÔU±Ë=uU®.G6BßrÇ`k‰Š72pªG‘pM´`¾ü¼ŽÈ·ÁîèíåÌ?ÌhÛP7‹‘®×òÆCŸ¾Ÿ:C!€~rœ¿æc—‹€W3õ9øzœŸzbS-ˆ;á´DtÙ ôæ|¶}&¢_F 7×>Lô¡`\‹Ð´¯¯¿Ÿ:C!€ct¾@×›²S{/7fT¼aBnæI9×îðèïv³›Oî›/Û£NvžÜ[3¿^_>ܶÌö+•³Ù,¡Ïo_+Ê‹çÍmCn]ë !„zŒ1è釾„ÌÈ;B!€cŒú‘§ø¨grÖB Çc€!„è1Æ G!ÐcŒ1@Ð#„è1Æ G!„zŒ1èB!€c Ð÷ ôT"ÆãQ1@1è+^B!"ôc<Æ@1Æ¡Çc€cŒ1è1Æ Çc ÐsÃôT Æã±¾©aŒñB4@1Æxì­nfc¼ ÐcŒ1Ƽ²—Bh|ÐcŒ1Æ=D„@1Æc€!ÐcŒ1Æ GôcŒ1èè1Æc Ð#„ÆèéÃcŒ1^àýX#„ÆWô݉1Æcˆ¡ñÕÿrÀZ ¹IEND®B`‚stxxl-1.4.1/doc/images/simple_logo.svg000644 001411 000144 00000012001 12350112610 017522 0ustar00tbusers000000 000000 image/svg+xml ST X L X stxxl-1.4.1/doc/images/overlapping_merging_small.png000644 001411 000144 00000070322 12350112610 022436 0ustar00tbusers000000 000000 ‰PNG  IHDRX‚@âÔgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿ‡Ì¿oíIDATxÚìÝXS_ðK‡ JJˆˆ¢bw·Ø-˜¢Øb+ Š‚( „HwJwm»wQQD:·ïn°nÀ`üÇÇö>>2Ævw¿sî{ ÜäE€Ý—þÝù.fsyo?›ƒä{B²å½ýòwK—kŠ©½!£?.¼½×h¡âÖ,PïoZŸÏ@sAý^h)j©h´£ €œ1ýpÖŽDŸáÓ_Ô7Q%(ß æ tývÈ&´ÂÄðJ,.‡uj€Ù[;+ Áu°25gܳµ}°MH5Í }D´ÚÌg>¶`¸¨Û rñ¾­ÝY%ák$à#¥ƒCp:H¢À2EoÃ#d•‚Êt27êZ$áÉ£Gîßs#6Âò\‰Þñè¡íCQaUî¤Â*\*ôäŒzû1ús;Û ¬1Xg £z¬šÓ‚â›?×òq7¬Ÿ“%¶J7<ã7'Ñîik™à¤1%Ÿþ °ÀC} õ‹¼«B?¹gãMr#¬'ЪZÚ= `%ÖHh(;UPhËO$n†U±:QGEƒk„å Jo(ÍB«IÿÂÂëò=l¸žô+d«½#÷¤´ð+$®n¼ŸäJuuâ7­ñ®í/Eï*½ù¡Y‰už«f#4>ƒRKº_«¤Á:M…e @o§Ü¨A›næ|×y­w®†•8ó8%åþJo´øZ Ý Ú”¯ÏÞ,¿}Gþâah/õª:N¹*\(€Âs—æ›í˜wE? €O}FcИA»ké"´´äWVÂ< »W2ïy¸ñ¹:$¦¤:áM=ÈwÒ€ô“JA޾(¿ìÀ!;ò@Y¼49±¼4Dš_^ì¦mÅÕT_I(«Ï "“¿…äžÔ|ž M ¯>ýÅ—…¥©nŠÉKݲ‡7d»aúˆãë·_¦¤>[Ÿ;gñ*m\=0XwÈ­¿½°8{Á>?÷Ñù³öù˜{çÎ^uÿ ª<¯5¼ )äêÙs·ã‹Î;÷ê7¨x¶i_9ÿþíÛ-Ÿò\q;,´Ð*­j}WU)±MŒ%5Œî&—×6\K–Tòñ`ñ‚‹¦¨Â*>*½-¶#Ÿ˜Jï9JwjÛ……àñƒÅ,ðN5¾÷Áúº_åVy”o¢x¥¬mXøìH?OÿƒÕqX¸qg{¬ÂM*Ä©ò²ÔÙú6`Á‰÷¶L¦®5c÷ëL˜«,£¶`W.©ìe°H·•Þ²Òp¬>¯âÓ¬ws$!jðõßäó`ÑËqÌ*hVf <ÐNèe°²†bm’wÁìÌ`Áöƒ HXyøè¡ h’/̃Õ¯N(ò)œ|žÈ VVÿg½ –Õ˜ æÕÏ— ,ÄgŒð(S{ïÀG†ƒ…–Ä h¾cXˆã¸s›QaIy%U­N„üd?¬`ÁéK—0ŠeºWÃÊu«÷Âê@0cI ŠKHI÷•RÞÄz‹~hä€Àb|7Á7sS˃Šc)‹óYó>ÐXã.ed~Éåe¶n×Vöïß –àÉ#ƒúð0+¿ËK~U‘ÄáæÊNÁ&—¬s\ÏN€¦^ºàQ¦f–{å&ƒÊ[ÔtÂçóKzïíp½¢õ¡`‰zåâEîëëŒaUo^ÃÕœj¡=Fßâ©“g€‡ÃÃsúÚž² «.õ´¨°é»+S\Šw›üµV|ã°\ºBÈ8­lj,4È ö–Ѐf°ø&ž Ý#&rµü-D©fbE”´ö ÕÂëɤr#È”XG[Δƒêóд/U1[!ù‹Œç$¤j‡úÍG!%Êy!"Z–t”rŒ! ŠÇZ[«:¬×”DCɆ~=x5$’ëX~âPËÂ2¬ÄþâÔ}’FBê§>:íY“Añ‰WT¡K9E)àÅqE_NKÌl‰5X¤ ú¬N:BÔŸ&%ÖDÿSìçF÷þY:³MÎQãäŠq¹]‡U´x{m€uù¤éÈöÌüÂ,ðr ‹§ôŽ\$Xã?6?•„Çsâ¸V?úµŠ¨c×aKj?{F‰™^±ijpx)Y²ëÇ쬭:tOX¡:¯›'…ÏÌKæXã75,õ¡?ú°?`yñè!°®—^¾F·ñYiU9K°€‡ê V*Ãܵ£({‰1œ°?Ç´Ùx8~ºQvïŸWèëQVA?w\Il€õ[ïPu ü½j’Óø­³øü߬Áªµ•Ûó§ÃFö"MWÀ³ˆ/Ò´µöUÉÛ„Þ+¯˜žƒÛ‘ÃXä]ó~õX ìú2wj±“¿Z_É,POnkVÇz¨êC§÷$2……·—_Þ°€+ÁÜSÔà‚™Ð¿èy§_KlÙ |T é1°@¥­Öš' ®ëEl‹°@ÝÛá#l ;ð7Àg”¦7î2Ëx&tÊJ™/’©‹núË ˜rêɾž$25HCMÙ+Gõ^Ï€E™_ï:^\Q]šO`å/–aRŠáÀ)r۾̭L¿…L20?wx¹¦04;ŠVMö˜¸uß~4öèk=`¬ò-‹Ê{¬‹Ð!jÝDν8MAj¸ ° }€ß6e­£ß™e ë‘7†ÊÃ$ÑÏ“y…°Ãx>â@ÿ˜å…pC‰Õ”nØñ“°À}9¸À‚Ï­ðhxñ[\xFè,ª"/«åFÎX¶i›þ‚ *ý†Y¦qÅ~…Ÿ–Xܤ„Ýûôά˜ÅVÖ¸k‡eq¬žÔ,ˆ'Îq I¹³¨_ãB‘Ãùà¸#åÓ¥ã0„U»zAÇam™-¿xc°Ðöz¢û­ý›×˜¿ÆpËžÐ*A9!-ó³Ž}°À=TNú|¨E:€|ìaá`<ŒÉÎÆ`¹iûºÀCÄ…ûO¿³V†òNú³µÅ'¥~£-'au$z,²Ã@HDeÊ …áËØ«lúÁÃr˜ÕâÊ4{ªÖ Ö†´¬°UŸ ¿ÙìýÍ.X¤kƒ¿pVÒØæ#øI–šx¬ÿÖ31MÊôü„½õõ§<Ø øËpVÅü)¸¦ïüÔWTu±ì{zt€ã»wïœCc0?ªH¥ ÍWÆJÄr ¬ºCk(ãRÄ¡ ­féx`F‘óª°¾ ºÎiX s«\C?a¿uL”hV™ÿZ5³¨ŸÌ~õWßm#Š«c Î0qÎhλR¹&õýÀåBÒ¤%„ƒ–°:94™5«7”r¨ð=±|ÒÄEGܘÍ4ÌCÁ^2Læ–ªÐc³g85ü7•°81"ÿÿ–ëÀ3,*•VÊ¿W…™¯ãaü`'ÍNØèkÜRzÈŠPC˜¿";aj¹þ?ÃÊŸ±üKÇÊÖÞ]ù,l&-·lŒ_%§¹>æX ªjªè?™¾šÏÙ «päñÿgX/TƒY<عp&ý\çøâvh²_•Á%°>™ÃŠÑ( >TÉNXÄ= ~ýÿÂ"m^WÆâÁƒŸ1Y+eä{QIÉ >¦ÊüfÜÒÆ ¦×ŸaØ ¸õûÜ3`¥³«’õuÞÿê×3ÙaÕw¦¸Ú¤™£ä!½ ÎÀŠþàòÿÂÉM°Ji“y‰…¯uV= ©`5î² Ö%µÏ†E*´_˜Í:¬?£Ž² «r§n9³Õf‚ö âçZÎj2*º +alrÉÝ‹Éà´žM°˜U†La‘}&‹ ·Ï§>çgg`MX8_/¢ã[~ßkl9ßÝ¿„N#½VÉrÝ"ôð/<&»þc:&xƒOWaåÑGÞD/ ¼¯»`U›jİT’ ž¯ÙüŠ2¼³ð ¶°tqS‡tTVÍ3ù…ÖL÷+T]i¸Ëý·`äF[L¯‡õgî´oYƒ§{©™[ä.ª£—y ÞðmÌY"›`‚Þêß,6Þ œ·®}ör®FgﺇAÚìA·;´´81ÇDqËwæÛÊùïÏhØŸ)ÉòLjï¯ ËÖmGßSSèxÒ°+ÃÅ®ÂÂÓ÷Ÿ)”êLÇ—° ðP°&±šnøeÞ_JRÀtÈ7V˜æÔîÆ'äï÷´Õ¬þ2߯ŽultwÖ°èõ°*I+ËØ/øxÝDo9OìòªÉ±ú®>Þhx½Û%|UúJ5Žm°ª5bY‚U—÷|µ’@¿©‡dv¨ô_®2û¡É·äòT‹‰wÇQJff°pØÆ•!á hM&ÜÛaŸù€h(öüÞ¼Ò|ÏlÙþ©]O7ˆKS¢¯0¤IR‰w•m°@ÎäU¿;ëßÑẦ¡ÅõN9…@©ïú¡ªû^$þeÔÚ"DÚ­V»7¾!Ì5EŠà|ú¡¡¹ Ýð×È ”¯Uˆ r¼ Ä/1µ Ë°ú ÖK‰¡:þ”»ë–¯®b,à*›ÔQXµvªÄ&§\JIR^×yXhIk5WnÔÂÝ÷Âr‹Ê*k‰”¨©,-ÄúߨyÄ(ž3]Ž;/‚v »™zŸÑïû`dâªÂŽ·±ªb.é¾ùA¹e]ƒE…ZŠø<½uqß®ÝëælÛ½Ëä¢Õ‹H|«¶ÓÆ{ún!>ÍÝ·ÎÍ:²Ð(½ °0Á·¶,bža…q¡W6/_¼rÏÓ¤¦.ñv Ûñ#‡66=uùåç,„º¸‡u2ҰГ_Ð4…¥0³Ež ÎÜçéNᓼn[:ú•ðD º˜C]bþ¼´ÀJŠ,$nù)L·Â"=…úh/]ª6a~X—aÅ ‰Š¡!ÂmèÂÍŸv„°€«¢Zº ökVmQòb2ga!%c©°Â½§lÊîB‰…4À¢ìÒX£¦]¸›Ñø£aЊF´ˆ—´½½0Ä•ò8Âqèõ…³ ™R^Þfèk¤;ae…tŸ%Æí.²ßõ£Ë°Lš3E›/¥6¼›äsþ²V­Éà8´¢Õ=Ь‡´1‡Õ/¼‡K¬ÄEËÒ)ÅÍ´©ê}-‘.ÃÊŒ ð ͤ:FyVC•Ÿâç¿A› ”²RvÆ#A#PÕ ‡„ŒÔ(¥Y¤Öâ$¸a=Ѧ”- {ëIç>tVòÅoÔMQ šÖz«ÌNX¯·­ 4n×ÓÆªÉSOŸ§†ñpÓ:ÎÂÂ!N“l±(¬!ÐúÄvÛXÙÁ¥mÂBl´Þ¡z‚cTf7ÜH\)8Ò—–7‡ Kî’Ð\+X8x/Ÿ°%嘥}Ÿã»Ö5~jcˆ²ðšEWa¤ýó€<…Ûl…\ÚÀg—v`5Û™BÏaX8œã&»,|Àô)bÛÍcÕ(̽ÿ‡ÈÆfŠuRÃßM‡…„FøÑJÜ›‚AVÆÁgÈ?°î‰C ógOðïÏîFXOeí©°Œˆ¥†ìØK§¶ ÿ­¸é¾ŠµkÊØ+¥Sk¬ Šfí­iÖÐÍ;Ñ0Ø…ñ‰ÿÀàxfGõ鉉‰IAÊÓÃ’ÐÙ²n8–mhešÛ ¯a}‚VRo<Rfo.«%¬ú+(mö49!hÖ·.Ãò ȌБWYF_Bï³æ[vÁ"§ÌÒF½|h "‡=lV$mûo’Åä/¾* ½E_Âîö™–K“Ò»[ðøœ€ÆÆ{ýÉ}ÐrKfÆÍÐ?-aIö§ô‚ú7c-ÆŽ?AGDjì„ñã'Ý iûÓ«ÂëüÐö¬—ÒòþÿV…Oû@»©Çz/"ܰ@¡ùé|V¿þ듺œnÈ› <_t Ùj7­G»xñ&ÛÚX9K~ Õ_»hyU›°êh¿4g¬ ?‡aù.pðlˆã{“3›êCüýµ´X·P{÷.jìÞm0K¢¡}(·Ø"®ŒÜÖD9Ô’ {j¨––ÖP!‰aè—Q6És!å÷´“›AÐY¼=#XÈY!áÛÔV™›Èÿޕ‚߯ÿ‚.Ãú$$ÿ½‹3-®¿@_¦ø¸^>ûï_7ËÛÖ&¬úk§ß±iÆ´‰¬Ã*;ðˆåsá¶½’ɘwIiùÆè+©2rãÛ,Úšsj/L¶Ö]L¯ åì¦òoM¡+Zº!y ¤ð.çCIïÖU!œºB…õZX!ª[abi=èl´„õˆ?åÒ…ìOÒÒ’êØxUX¸k€M«Š-NŦíñX´Ã“²ˆ¬Ã*¿îÂꩨ¶w%2›L!(Ü‚Bbb"ª·0ô:KH”"‚b´—è#Hƒ%³Üfô’¦Æû'%±ó؆¶;ŒÄ56Þa¼û hmzc‚4` ´"Uvo _Vàõ·Ã÷…F$v/¬®DKXï…¨ /Ç-&ž.,û¶áaÙ¦üjN.m&-ÝðX¬ ;‚–}nèø¹&¸>÷+mVS‡eZ*Ò‡@Ô˜UÀFX â¢Üá˜IëÉŵëªYp$5¼6R^ rãS=+°ÀM­dO…»¤7“eŒ‚.Ñ&2ã-çâñ„ëvð¿w­ñNÊ}¾g¨ Äß_ÿV\uóÆ;ÁG¼ÿGBò¾n }:Í`Á‰[DÇÄáñHÖ-YÅÛh™†=ÉoFýIî]è,‡ÉŽ8¯!¾&¤ñwïkH”þ_Àª6]FéŽ- hêÓv–Žg',Pñ\q£Úñæm'H唨! -ü ÀuHÀ¬l­½¬Y?¦/)`²ŒQ’ý´>Þí·ð4ó]ì+]Ö E/ UæÙ&·Ìce…œès>,Ûi¤}7¢±Kg&­>ƒOi3wñzµ]uò Êònˆ÷¹YèâœBÐh“CÛ—OМ{—öð´©£‚‘ÿX o÷5ôdÄiˆŒsoªþ2Gßa+,@tÐX´d¸î´_mÂRD ]´ñî:ú`K°Èv ¬Ì±¯2W¦ìIÄl¿B‡É¢_Bü`8RŎ9¬ïCÑ*P^ÿFR¹õx¬”{wí:ú8ÙñÀ.CãçÔ!W)f×Òé¥ÖÇ|Åü…‹¶Z7æ§°fC}Ð'º›íA/5÷ì9pÌÊ36rÿnÐùîÅFXà×½çÕ ÷Æ“æ#Uª6ίa+,@òÕP—S“wkÖuË”ôÆÈ( ùÍR mËíø°ã›ƒQºAd N|°uúÄÙ{^¦"0>-‚9¬ºýª ìbÿ26C™œˆÇ!”/¥ ßbhrJdXL6=ë³Ô”r`-ðpÓ(ümËþ`}õ_ë™ ¾¥5k°ße/,ljòCfàz#d¤m[¼?脆$4Ž[ÜX0—Ùî£8¡I´)Á!”…—`\t`¦¦„-íÿ1‚r³¼jY‡ÕÔ ]™5¿f~$©:ˆahôÕî¬6òXÙ¦´GÙíœ.Âj¨\‹°¡nÖ×®]gÁiáaáqX8>ü ´Ð/ôsF”‹ŽØåˆÐdô·g9¼ ËFIá³"¢paNQ88+ÔãSd<– ¬È±³ïDÿa½G‡‹´v$€wü†¿Ž+Ìy÷q½{Óƒ µN‚ÿVåvè(åró‘økÏaXNý !ɾ’”•šæØ&,ò{óúmé°ààƒKwï7÷aœ~WWIóh‚y5gØ¥48ëÁÖ•³•ç;ÃYŸ,fõß$¡ð¢üùØê&º+ül·ÓÓ§ØȉJ-§CaÉÝ9¨$*5LJy·Zi¢>èÔ$…5©øÛk´!ÕUkÌ.’˜´nÝztí2íAæ HìcC­SO†‰¬HM<¶íÂÑKS˜ÀªùRFêÔ‰¤ÁªZ2ýݸœ×Q'^ù\l: ï0·ÿVÙÆþÔ5NPX;Ý9 ëžÄ¦Ç/©Ëð¼Ú³ÞÁÞÛ¬ÊEРŷÒ[ôЗ1 ™1É}ÿ‡ ¸ŠÁaŽðɸ ÷ãwÎCK£[ó_'~ÞÇ?!0Õn2$t·®ìý*,8Ͱß]¾ÖgGFæãiâƒk»@3#i 9{©÷±ò„4úBx&¬ê‰ÀØCÐÌ8)f/4? *63ȥ˜¨T8j…qx’ƒ–ð)¬ïá¾B‹6ÈÎNº4Ò‹€»>&±»†Í×.Fÿ»À¸Q‡½ƒO§šÒf£ü×°ÈGU¨½àî“|Gú³VÉ7" °w—/ý67ç¶ÛXsÑ BaÙÅ÷¢Ëþ•µMÔ=,ÞRPõ=‚<H±K"ˆ×¨ó9ø\ß!ÐqlöM‰›YᯓJ,$r°äþ8`d8 ßéÓÏ* Ÿ}]Bà"½Ä$¸? —ý@Úž>CÅÁå\‚f¥ÀpîQhy*m«…½—‹z;81"Ÿs„op0.r 4#8ó½WÆ|­0NÞÛX~Ûhc]\HæüÏ@ÌyôÈå!3›*-\²‰ô_ÃÃ<)_¬Ü5X ÏfX_·ÏxEf¡ÄÊÚ%®ìO¥E™ÃŠ= On™n€mD%Þ· ÿBfÜ)ôw?bòNZÌwœzê-%„f=Ï€»0‚ô&-¬d«l¿ úh8aã(cpt¿Óó@ûßÿOŸG+àç:T°;Ýà/Ç¿¥”•t‚ó¿²këëÈÖ© ¼Ó¶ôX¥cr„&¦&‡H5t‚(ê[¦V.l„uâKùƒñ»!]´‘”µÚž¶ÐAb¦öw¥i 5?dóTN©eIؚР–‹DŸ×mÂ"X ­ošÒ N6•†d ¨ÓÙ: ëm?ÁƒµmdÞ£ÍàÜQÓ¾¡Í›ô;',/£?¶jõ¦2ÀåAAüÀü&±=AZë÷ü +y,8åü Ê $‘V­ö°ïH'´ â.í† «„ \¨°L i1hóê…„jðÍíY8$zrŸ{íû`a_èîÅ{Ë7ƒ…Ø@c3ZÁ êëÑ6¬›B³ãiI,z‰…d<ž,"0Ñî4¬ÊO¯´Õ¥CŠ8µmeãî_$°ƒ^ÿ|Q³œ€Èí¶ìþ£iö9 HzÉ“ç¨^nyE˜s•O°)ø…CXT„¿‘Ÿä2‹¨Y %þ‰ŒÕ~´%ÆÁñÓ„ŽmxŒÞH\ m§>@\ƒ[Á‚ý†ª„ °WsX¸s|›1ÿ”XÚ1”Æ»Ӫ𑔼3:Zãe6 V–/ªú„<´=»'S”çQÖŽ'ÖVדÁ¯8z{ìÍðPÎÀê@ÑóŸÀB‚4 =ûd$I°™û©å8¿˜¦ýé]ïnL¤î{œéx’Úm+¨4Õ*ºˆDkcÁѺКLJ›mIŸûԊ袠ürÊZµ8chàK‚|öµjcÁWÇ&ÁˆûE?< k¥•¥/þi ¹!fŠ!¸Ê*º"0þ#Xˆÿhc2Æg_¹†¥ÁJ>ÃØËâs’àîþ•o¿u²îÜ#~M•yÇÔî†?Ruoì+¹lŽ0í„nl¼“¿½3ÖEK¬~Ë.Ä4l|AÏcYI*~Dù8)­I¦–O# ST»®jÖ-?ÿ»‹náðÏTâ(Ïq–Pþˆ‡Ï ôwÀ†“ü˜†}ÜWÉ‘€à]WÓÖ¥^¢wÅÍš‚ ZBG32^Í‚t‚`8ç´²r !P[Ð*ÆavA¢[‚]¬ŒÄ'-„Lè!RØâa‚‡êfl÷Î+L],ñõ•—`Do»çµ·nrï‡e'´¶qÀÞÝ0£å¸ öêABrÓ¬"‹È­¤éæŠÓÂß/[Ú8%Õ`ðGêÀ\鉩r2=+įïõˆ,\f„)$yþ3Öm ¤6Gÿ’*¤w<ù±´è¢‡/f/¤7õ£—ˆ-~ðnç|w´JÍ< (5yææC¢²ë|“<¦CêO?geE_“‚Ö$`Ṃürƒ5fzÀI/4 éž‰8\êøñÑQÇǹÂÝ «p ÔgùËx‡­ÎFÇh×bÞŠA\ ùØ×(»qÃÃíím+W¬A–XÄ•“uéd;l×ß½ë}Z½Ëé†|œõbã´™[²‘Ä«;6n4²N„㬠ÐvéÙ–NÛö1ûҌ͞„G}‡]ܲzí¹˜fåfÔ¥µKVo¼B]*Ž3Ÿ=Ç4Êy›uÆíÄ–›Û%$Ý7Ú¸qÛYW Ž81ÊâSŸaØõà¦[Ì]`\ÆÙÇ,̺wó„Õ qÉ›%”Õfþ–™5îÚKº4ì;·—XÉËç7l¬ê¾SÓ¤Ýé™é·Â‹˜ô¢ÂÆ}Îhb¡HE²c¨Ù O à‘f7àÔX´aŸ—SïYÑ)-VB…‘¬¸ètÚÐv\b2 gfSflãŸO=¾¡*OŠÎ Ž| ß…Ácãc1ÈcÕ×tÖ¾u”Æe}¬ ;S€¿3ÕôXä¼Î]"îs¬)a‰ºº{»;¬V–üs5Ûb<Ü’E&E4ŽxAÏåªÿïãš²í <Œv­£ÝÕ6¬$·.Àz#HBAuݸ¡¯0]á%à,r}]³¨¶Jà,òéд2LÝ {ÓÞLhFÁ®0éé†n‰V°~û~ôh ׯõ‡Ý:ÿ+î0ÞSË¢á.ëÑÉUïz¬yì×´d#¬²$V`e& I”¶P uÜœšÂ!X9Ö"ªî9ð«z__ ɦ€&˜üuÅ¿HíÁ*Ù²…²„Aü¤a’WuË–ÖpV Zæºqº £åæ; ëû®9oYè„FBoàèã/)—†.¶Ù•b7S´ï:ÛÏð+UMe¬cäû¡Þ˜Ædñd̶ùžäöÒ ‰ËÞ ^¼Ôߺ¡&ÌlWèîuCm'eúçÝ“tvQnì»^À6Xö"Ð :¡ß‘Æ.^ ÅÅ Ëg%Àœ€•áõÎÑÑñSòëù¢ŒùhüHÜdõz‹ÉÚ,· hqe» Òtsç:½ýphcWákÅdŽÀª1²jœoxG gu–o¾M,tB#~â­ºÿ¤Ý9‹2˜é¾=£[ºyƒvÆ]Çg·u6ßHñï«m(¯õµ´cÖšèsVÉnÚ~d+½olN7Ôx>Ìa%å+§=y*%tú§|™2ëg`uo´„eý˜öêcßiël–»=ûÖ~—N«ÈyÄX•#i7Ÿ‰†q8Aê¿+1; LÌž³é ·p½–Ë)ÚUàÛ>f]ï+l‘òŸG`[6¯«P_È—Ã Ò$ïFHÀ´ç]Ù¯ðÿ Ö÷õï©s…Kƒtï±ÖñIxÁÊXj—VÈ塪ٜ……K§íÌc¦íÀr ,ðnø¦»oì­6ô‡FaÙ «|Ú62‡`—êãVï^?K¹PËáª0ølvÃLϬOê«2`.UýT€ þ‘$vÃú<â)଺÷“)kœð©^,æt'´¯æ³ãh5ηæ– “OÌ;vþ©ÌÎÌ,lÖŽÁ ÷­ùN£ÛѵD2‡a5¥Æ|âXhÁ¸"øó…Ͱʷ-«ä ,ô3SCI|„ÿàt‰¥d–`ýÖ98«j‡¸%67‡yul†õRšù‹X¸®ÂZü%§çÇ×ÍaÝRþر³iAK*Y‚å9̃c°j Ç—·ÿôNÂò–…Ö—ü—°–©ííùa4tz3X¾ãcèoF›Ûé¼’äÛS쳚¿8 <›F_n-§ŒÍ°ªÝî!à¿„e2Bûÿ!Fìj«Â„¾«v|X[g³ÌùaKm¬Â¥†õœƒõ׌¾µ-§»tþ¾Â"¬ºšÿ@ÛtXÅ>ö”ËòððÀƒNmŸ42kW…‰ýÇ`ã®-:~“·Ì§fõ0Xé,Âú? :,Ï ²ŠÔ=nå$Ù›nx¦‘Æ9Xà’??5 y §aÁ-‚àb‹éý°pêâêƒb[¤u ×—r–ß@ÝUú«©aøÃ°à¸‡VÖMaµaaÜëaÕÝ í!ZÌNX¹C/Â*:[ÙU$΂3÷‹6lj"""BùÂ/ëÕûa‘’s»ræ°^â$,Lì¶t«]:¡šÇRCEZ{å«ÞóÞ«¤)…ðãa÷ë”rž^üVqÖ«ñ® qhÄm5ŠŒ§ÜHïým¬àDú}w\Ø+oÚ)2'a½ ÿ]öep¸*´2hl¶ãF?ÃÃ0Ë3¡ÿa}2‰'4t}ä³+©$² V°´?à$¬‹«ž;RÃéÙ<»ð¤b¶ÂªüÍJ'4l³¿ñUÂáÛ:“ ý„å!/¯ÚjÒ†M?™Ì´œ)ûÃB'4ùêè\ŽÂ:ßbO…ÕlœWøóÐRVfBX—D}™0á©Ôªt˜K`ÉÐW¾Õa¶{3b´2 ã°*&Õp–´Î„†+§3u’άöÁz% Íb¡N\2!ðI¯t¡½ÜRb}Úê˜LÔC7ÒÓ“_c2½ÓšZÚñNèÔAGaÝ|\RÝÐÍðsO ©®Â÷RÛ`yöƒÖþe!…8 ›zÐÂâðbHõ=Â%°üš:¯¾£ü+”ñ3ìû@†ï„¶•ÉYXîô)GöRö„þiŽe¬ª÷ÖXV¤8쳑‚||Ä?è6–[J,¸i­ «÷è)3˜L1-q¸—Ó᪰jÜÎÂ* P%W~ŸÔËd_ãL,ÁÂÁÁ§Ž3s¿æXÍ"='i%#™žÎŽ_f޹8 «YFí'jüÿâBN¥ÖÌK ŽÅ!˜LnUÔÔôÈC ¯p­-…lH7¸ÈÄsV6ýϨòBÿ®O«>‚aÁãñ0>â%·´±éãüÀ-gê²XrÅVÝ‘é†õ$šÞÆ2BÛXõ¥dÂÂF;?¥ÆCs·\î JM§Fæaëü¢j¶$H‹†œ&qÖ…9·ìîSâÁÍiWÜB~±5AÊ"¬ì{#$Å©!ÆoÂ-°<úKÊ4†˜„âh£p`…¨}†Õ"AÊ'=Û•È1Xˆ¿ÔO£ah’æ%<×Àê#Õ¢²²R£>²Öɉy‡¥8{ᢅh,š£2sùÒÙs¼9 ~(¼È3ª1R¸&Aºï3’C݈ñ˱»yy„Çs» «xáz§aY¿©&’( 2ÐV_ÄåRŽÁz!õ4‡>~”[`ù7šºú–ò¿ep—aE{8 Ë iÑxßL0ƒ1Ö†Ö äs ¬Ü¦AX6®#'xvÖcÇaý©¢Ý*½E ÿÀ„,Î]ÚÄc©»Xà¹(AJ¬©ªCyàóH-ÝUX•›V”rú)ÌNLÄ‘HëКZù‡-°ÈL`n VØÅù ÷¤„‘ž)·\ò·'ëõÆÍ1 ¤ÎŽž°¯¸«°¾(Üœ‡Uút‰¢ˆò;êøÑ@ƒ ®5Þɵ_<^½`~üøñ—¯Ûû¤ý¨ïèU¡§<ý•kÒ n¦ z9®(®p„²Œ1—VØ|±¼Æ$®D· ëƒFçaU—„úŸ§£6Â-ˆ©ª Râ¯Ä7ÛõÆëŽªÒ¸ÎˆªÖ˜ñ㦛|ÈøCî@+qžÊâ•ÔX±ò·dÞ B}×8$8é{žmÑ}Ì/§Ì0J]mÙpNça9KñÍsþQcwìZ×Ö -r9­×wÔJÃ+âò~—7ÄO|ô›3[—hJ/º\ÖnU_¸›Ùn¹*t‘’¾[@’éÏÁ«Y‹:§§0ŠŒÇ×Û„•?åà8¬ºÐÊ‚ÍìÀ·½]˜°Z\žªªmî–öï^o€X÷zß ! 6Û²‰1¬˜Dúµ × ›¹Pº¡öÖë–°>1¾€!¼l–·†ça•®—t °ª÷xwÆBs¸wó5(êò߬RÕµËo³*Lò¡×NÜRÚH>TXõà¬[`]ü£”X{•©ã¯ÝøÙ„Uöd´ê‰¤öV6© Þ%?׫¾Æ{àI\ãR‘Ècyw£÷ÃJ¢'y0 ì€õzP\€åD[°ä×X–,Ï_ó‘ôb’Çʈ¥;zr˾3ôlï‡õé\îojäÛ¯ê:,¢ÙØ’ëÒ¾€JXv­°ên¯eñegMng[9ÎÇ ö}Õûay¨Œ l1®3v”œ&¡ë°¾;Aì°ÎC B³²«T‡å14å[ j™l+wß8ʹu2‰Ûl’ʰúѯƒE_tV„¤7è°DÕ(½Åj}åu’;k Ëïrù–‘eLïSï:½oˆÛƒÂ3²¸aÌû*ÿÿŸ«[ßá» ‹|a<Ü3`ݰ.(A£ô×»ËÄNT…ã7±šæÍ1¨”YKX¬1„…$”çÚ¦qAË—6Ä…ähò§Ë°*¦î¬ï°Üh«+¯w–žF2‹/ÛUDƒ,I©þ!)¡ ß_í|VG×d/ý¤Ë°µž«”–•+Z.Þ‰]ìë'CF¬YßgBÌ`ù.üÑ8IçÌø˜ÏÎÛ|`®šÜðNÌ„ÎtÖmMlEU™ÈNÀš$£òš•˪Sƒ©1r@›£sÃ,†ñ×l¸VQ—û « çV÷XdÚš0Õž•icm9¢|¯ã)Ò£ÊÖ–*ŒaÁ)AtGw-ð8ìû)§{?¬ïï?d‘@Y¤gÂß.'H1šÖ ‡Àòؽïb& F0q­îLK׸|Ì­ŽN ù¾Wî&Ñš ,*.<€ÇøÏa0ª-z¿÷ÊTšp# šÉ¿WÓUXRŸ{ ¬;êWSËÈΚJcîÔwÖaðÇ\quDG–ù*u×ô  X1‡…$y¿{íìŸFÙ»Ÿ´"¶÷à 2À“@Í¡æÖÇtqÿÜ=Ö­Wèé#øÖF…ÁƒjÞNT:Ý^¾Ô{£ì²Pô,0‡…y¹\E²Þ‰22û9‘ Ò ”tC œðZò‚Ü5XEÚ¦ ‡ÀªzˆjªÜ Æð4¦“°ùëQƒôr˜(Ïz8sД—Ôînæ°lU õ)3U‡ë>hX-‹ ¤¶üYÍ+ ËŽÔ5XZ®=V‰]nÄ¡Ÿ•‡Ÿ; ¥•g¥ÂÈÝ÷£‹ÿLQÿ#Àj‹ŠÚ‡Æµ·˜Á‚#Ç@j'²?mõ°Ü—[¦Ø¿zIª±—qGÏòÝ7-«BÏ„Qä¾fë´ö÷ž‹|ýiAôxhåoP»9»ó°Ð(ޏ1mðÝùg"3°_~¡ñ3›òâèŒq#/z˜H~Æ–½È€'Xï½- ±8Å-“)àe†Ûdù+j“N k±«R°ÈÃ?FxýcXWo öX aêøf$ Ü™µ,¿K°(CºpNG¶Ì«(.®9mꔩ“öW¿|óiïoÍçÁ2…u[p]&Œ£À"ØmÌä’ È'ŠHlË?ío?h±*r0¿¨ãà¿ÂV¬âÐc`‘—kÌýD?"cÓª»‹uù¡ž/_Ú^zlÿÒÙ3*ûOëV=3XÈk‘h£ï½=&kÏnÙ™ð~!ŒÖ-ôòdŸư^ÈeôX€\ù§¼½UºXE?.Óƒ2…6lW6ÖÊËýŒ¹¥ÕÅh«fÍÚ’«#ÑX̃éU!öì¸ ‡÷Ñ„†zò`± +Oý2àÁb8Å>Ådk kˆÄÔ—8‹°G„ñ`1.±0é÷/$!±·Eâx°X†µO¯˜‹áUaÔc’˜ ÃøÜˆ×,a}Ÿ}ð`1l¼û,÷ŒˆŒ@#äÆÊd,aù÷÷áÁb2‚TZIjRrK°È–_x°˜®ÝÀG ˆOð¯Äb VÙ,£j,&Uá¤;NÎh8]Ö· Æò`±+CñàÁb +èOÝ£yµÖŸ—Çb Ö=”Þ«v¯ SC5ÁdŒ¸¾Äª"0 üßaÕ­[PÕ›`Ÿsbùuœ¨b2‹æ*e%´Ž[:¡™F¸´$“½õ/,ÌøK 7Áª6ÂêÁJ¯8‘˜Žyoü¶[ßr}U,|ò¾-£xpÁ°Çþq½ iƒ>«ŸˆÆcÀ,óŽÉΦlß‹ƒ³x™÷`Qï¼FñÕáß~uÇÆö*XÀ^™Õ´œõ€p&ëc…®_¶åN&l²â€?̃EšŒàq˜v†&ÿs†Ø»`ýZ0—µÝ‡“‡ªf²¢_æ*§X\ÀTA©as‚yU! VfèëSöíÀŠw½ ðV3ûË¡ò–jSF@3ÞKg["‚¤l€ÔïúàªÝ¿Ú„•ýv»¬ÀÉv`›Œïm°ê 4ìød6̬aÔª“aËá6ß•¿‚üÏdñ`5ÀÂ&xçoVżMÄÞ ÔÙ)®KïØú¶Ä ½Á€)¬ç6|äDhALð2ÏæÁj¬ á´™íÁŠÕ~z,Pí2BÓ:¿}Z$ÌÑ3aXº¯ÿä¿™À¶ë ¯*¤7ÞSg·Ë®k[ªöPXd8Õ ßöÈùúø³#†]¤Uš ¯ ÓvR5ÍJ½½\ûV+XJ2ËVåÎy彨ߩ6rGàfýë°óÊ!ƒÏ¤ÐŒ‡&65òDœáõÑ;–wUØœùzûÜõ§<ÂÊtpÖ{ú¤ÚÇÎÀÚ•KŸˆ‹Ñb´@@]âÙY2šûîà[0rÆíÆvy…e·ÍŠ´&XšŽ ãqéé8<ŒPúÃ`„Iàý¹²Ä‚?/œú(9‹!¬J!œ‚u?"¬1"ïuÖú(ÚóÂ3Yy"ßÿƬZ“g¬:õä}PRRrR‚¯³Éâ4Õõm"ZŽÇn‚uµéÈ‹ðׇ¹>zßîp„IËhÞOÁ:)ØÔ…).èÄê¥iÑ(¦çK@ǘ6ÏË¿úßÙºzÑœQÚc4d†Ö?{Éê}÷¢ý³!p,AIVCÈû`18‘ˆ0n¼“ÿLdïGX©o›…ÅÎOŤ?·.ïᦻÿkIzÿþÜkoQ”ˆ Íí%) ©4´”’ÒÞƒ†v²CJçTŠJHö({ï®ëÎóüι׸téRŸïœ÷?×Ï9Çs^罞çy?8šßûÅÄjYNDhXÀãa¡q9•Ü«5•ÒâÊx’{á=,>ã™[RÛˆ á+Gÿ_`µ”¬é¢n$ðVß±úÞÒ')º_›Àê©ÒA¥&8. ­-°N ,ê`Áhm 8|séW¹'z“áaKõï×û’ßj«{°Ôì˜ÆÚ0ƒ¸ä+wS¸§Üp¥‹€_õ³m¢ò4çôö¼­š/2Ü$=#ûÏ?°÷Â¥,ćtÌÇúWC`÷7®`YEËy®ýf_›Ï•½ÇìùÒFê‰Yêf¬8Ø&8büȨÿÀ$_¼Ð³ÅÎÖ°lR/IöºšÊUc9«Fu°`Ý ³¬ºð½Zªúr¶ˆu‘G‡œåšIû Õÿ÷#0Ji>ke-Ä…Wák* V‚9¿ºwÚÏ`­Ù¶piu× –/“žÒ`ö-IŸÖ_foWÔØñš Ëlû˸´?–[ø3;vÒ³+¯$Rqd:VÒ'-°11%xá‘Ü,ÓYj'` ¦ê9“:¬x»Ã¥õw9Ûn&Ó`ÍC]QþÃm­ýx,¤G˜¿Êî€ÞÊšÕô]kpj:VÒ]­Aº‡ƒüihL;Þ¬õÂJA] ,¬*¡>`o0 2Ö; VùË 3mSË>-Ap¸ÕƒB^õVúf%Ÿüª±4ž5Vbl\|\|Ò÷øøøØ¸Ÿ4Öz0ººk…¸õ6>¤©¾ôyÞOÁ*BJº1u€®Õ9m„HQੱ½ù»éuoŽåºPQA “q½Å«Õn9îõ`£ki§û9æ\%¯±Ã5¢¸™ rV ýª>j«ü’Œm÷ÌêÏ6³¥Q½%2ÄÚ'·¹~õi׌Õ([ò+nGãþVû`5íYÕB2ï¶‹Ïv1°à3•Xòúœn¿Qº³¶¸üðß{'˜Ì˜í,öeV¥øFá`ý`½Z­F€OÖØ>š×¸$kß4&ž|Ä¿9õjïXÕÑófm¾Å‘º¯¼õðºÆÅϬ¯Ô³¶Ua˜åI¬¿,Hýî½JM’ø4æÝN¬åIo!ùom´ûð 7y_piƒÞª1Bv‰±Ÿ½VõU³Hk:)z 0m‹–òTVäiÛ +÷é ÁÛ8Xݬ ÏŸó¿w½oÔÓoÊœ’›Aä :pµ¬ç“‹ž“óšÀr‘>‡ê§º­`y)džŒj-S°¢† Vµ9ÆÓjL&„Y‹ pDº#X7ûÓ¶~_@çµIìX>Ä4—{·œcà –7œkk6fñî ‹Üi«`Œ"k¶ô'Q¥² ³¤AÓÎSX`•9̪a@3’ùc°G¤;‚ÒGðŸ½/Ê;2@SaÔÌÓZƒÈ –+7ZE…¹“€9£,_© ¬b"Uú`3õ‚kŽB¢a¦PpÿVöú äP¿]ë8"ݬªm.¹ö»~$’ËlN°ÎqßV`‘7€eÕ `]&LdW©Ùæ˜ {5§æòkˆ™4Ìò͘K[ð®'¤[‚•¦2á೎YÚZÅNI´vÀª^ F¥´Îcí«ëÀ²ßÙiE0;su³;†i¬í7;}G/ ü¯žžCÊDþN°î‰ 3~÷»":ï,Ž@hèr¯Tr[`…Êmè­Á²û},é0ÖwûÀúŠ`¤::æc9¼”ë}ël¤’žÎ±ÿ›ÁòÓœ]üw‚¾KW¢èØ~…<>;Å Q_PÉÌ™s‚àF°ŠæÅL `Õ®–öo+F\d±³–pž*,ÊH^…,ç9+48}Oq#¡*l qο,7ͬ¿4…”¼96¶½&[¾+áMoÕE9(Š  eìјÆj+d$¯Ðüããl°­¨EÁÒÂÀ¢šƒ©.écÆgÀ/*`A<™Zvñ•n([ f ½]¿ã-Fmé/«Üûý/«ÒÇû'¹ÿà¡çÕõX^ %´¾(h9ùywïç6Þw´ÅZ0&¢´Î=‘ÂvÎ….¥&„½<>¬¿i,ÇA® LLDÜí30·*UQÛr3m†ä¥:H>®õ ñŒAÕt‡Þº|s&.@ømX–©ú›ª ü ywûE'{Q«拚ƒÞÙOVæ»eb~=,ßBq1·Ù©®é@gÆÔUxx@ ìf¨ՖîfJÓ7X¸ýø´oœJ ¬¸³@Î8låÞ0é Ï:ÎT5Øjñ¡;;ïšä³b$ꉫͿ•XÍSdÈ,|o3Zžä&ì.ÂSãØXxUBÒÔ;%y˜µûMn‚>°E óŽAþTJá53ᬈðÆk'FÚÁ‚9SJ`Å uÚ¿*tŸ\,¤$Á{ÞeØ‹*CõIÕš£ÃËÑݬ¬ëkU¨âéclÏk9¢š-'bUþ9ä/Öi’(eal:ÿ#1i,YâH\X¿9‡0ÂY`C=ÌëOô]ý7™c‰n`Áª“ýŒžÞ_d[ƒƒ… W°èûÀv&æCx3X°ÂÚâ²ëvý'n`ÁÄ5G®y…ÿvEj¬¿ ,ùF° ˆ¸ÆG¸UZ3X5‡=éM¥ÓS{ÉÅ4ƒµˆ›Ê‘º=+¤q°pÁ$ZY5ÿ$-оøò ú çƒÞ»üíæù °p€{îã3™µ®ÏÞ…~gBL“ a¥f¢`•ÏäCM'c_ß^/ƒ¾fÖ 8X¸0ŠCßëâv H¬02w2‘ˆi‚D±^ãÜ錄"£÷ØŒÊ7êAQ )Ym'*ùËn>í_«?O*>%¥.²`Ögãl/ $&)-?7¨‹Å¨â˜‰A'wÙ[Á™ÿ#u÷¹1Ì/'gÏ(Ò''Èê`WgçGÉL˜åle}ûÒ#Üœ]ž¢¿¬¶ÞºoÝÒÓ4UÞRÝœîU~pqv~œ_ðÈÉÙõ&™Xï21š7¥ÿôì®Vì`ŽR@QW»ê­Èä¨F ×ìü[U­Õ|+Ò™½ÕŒºêªò‚Ãv\dmpePIÕ•å‰fï»X_D8ª®‡ïª=ž²›ãͬ•=ÅfÞWjÌOy»sûžb©Å^kÎGá`á`ñ*ûÀÆt4R¤åûáºtñ ‰«¥LˆS¯Ü¤â`á`ñ*šü£7Ûìßj´?ƒ»é´—Ÿ¹ó •Ù’ë¿9Å«GÅŒ´ÒQSŸhØVb½ÎoÍpUÍÙgãwŠ#V ‡KÊêÛl¥×UR?CŠƒÕÃÀú_ V7‹ztæ?%› 9À ×1ßÔ5ÅÈœ£æ©lÞ„ËC ­¬úU&ý1™<#±ùŽ%O›ÔEEç<X&áòçdJl“)dþQi‘Žë²Â™D˜¸üið‰~¸tç\p°pÁÁ \: ù[YçÎCÍÅ,”%¶ÚP‹YÄãf55yíVË$gþô5³»Tfv^<ä3¿ì?Ë}ð®N”F2n¯4L„°z›–ççE¶Móà¡=#ÆNß²ªí¯ìµ,¿†d{nþÇý#}Ƥ(Šß§*Ë}ƒÞƒÿ,Æa0½°ã—Uÿz A½¿ÅSÀAŽéŸOÊò]á¡=Éc XÔvá>ÒÃ…„A-5ýõq¶™Ixoñç8¿-ä·ûe;¶Kh5VÊ• N•z ‘ˆ‚Åx5­ÅÇÕÿð„víáSaÍÖzºn¸…½x¹üÀ¹ø’?•èð‚ÕYùÚo 7‹T;ŸG°®ñµ Ö[Ö`Qf³ÀÂåIé#K{|¸HOzáýìV¡8"22²ªȈìF—yú&²êx—E?ùæ5…ÞžgÏX›d!¥Ÿ‹(±}“è«ÌsÒfßñ¢DËBH´Ñù' Öº­R\n¦Èß÷vLĹÍ%Ýa«ÿÄåÕö†Ç®ìZ)‰‚Uì½A›­òãÜ6/וÓÒÀ¢G,Q0õn\ç†úµ)ØÙ=ÊNL+ÿ ±—ÉåH?-V ¨šMš)°ÊôßëÖ“ÁªÜ¬ñ+vó/-@£gE"«šÅ=¯³}ÉÔ÷ƒÄï2ã H­=o¥~¨,gqKæhÍ€IGeÀì/Í#öñëŒ)¤î³Ð×Y¢±-pÇ`õ—NƒÑ1NQ}ºSÈ™õ³uÜLK@o Lm¾ÁÒ õ;0°è»e# d^Œbu¾1õãˆ4½d1¯Ëç„/å®aêÍ@ÌkØgÔ ­è0a8–G(>Fð®ÔY¬Ñ]A ¬ûbXpg3X(¢ÚBX‘‚„¾â7iy¡V2PB}ÆÞIöKéцÐUx?vÇRÇÏ2!Õ  Þrè$jdæ¯G;±Ì(ƒ)ƒVõ´ïe `Qv€£( ³Pí¦ N¢÷6s<Ð'ñ­¸°I Ê Ãla¹ß GŸKÆÀ²'.®DO= ç*›ÏŠCÝÁ|.^c=ÛZ†÷Å|,æA ,Æa`Ï€¹å,°.>ÚÊ™Áx"=ôE=,Î…Tù8Ì4¦¾®`™Bô„ð00#Û‚Û/_¼tf¬ÀšÊr¬š(¯YÎ;7°*õX`% ”ûˆ¾x‚éhÜ&ÌFÁ*Ö éÑ©©9à +÷¹ŒG5G¨² Ú•™‹Q3Ø®ýÅ‹—õÅTRa¶¶Ä#Nç=äXD ¦ò£¥x,ÀR„ˆ=P£rH¬$½‘£eøjj#Xu“Á~f3X¬ M\ÇÊ8ƒCèßÏÄ—rY•?B2¸9*dƒ¿¨ƒÁöE Q¡žâš®d!è½'ÁnüðÌÖQáiâjmÐiwww7g?r”’Æ·æ¨+XSÙ`©Êa;xÜgm(w—0 óS'KÇ÷d°R5»PÑ]1qô ¦š+ô6GƒkÂkYìz·KÚ¯ETÈDr]ÐàG-Jñ8X0Dª·ŒN3£Fì r+Õ@W¯Q¦˜7EY ¬¹‚/ñOB p–æÝG¨È„üÝo$Qpî;&ÿL¹þ-Mtœ q„3 &(j¦s«~žà—&C+8$¹ã`Å«K ý’¯9¿ª'ƒ1@Áòn+¼×¸òND…`%*Î.Eõ¥ÔÏ+Çú#ƒÚF²±ÞLļ©ÐÞ*,µ½,Ûì•q™°vÆâÛÓö»a¾bÑ\±OŒ @õj.ëœQGñ*Ñ;á)Âjt"ÊÞÅž¨(ë¬=°³g~ÂêèÅùJŽ@m2}%° p:ïnlç½,ê­kµtìႜªØ½„v§XÑSâ0ÉXun¬l°8¯hpk°’zÉ¢Ñ9I—ϳ,Gþ3HçÀšººTÌtÁG¢Z˜·|,D!­ŸìÑ <¢hç÷)â[1·4|,%h]D«8À/}­R-Áô–o=ƒþ#EÚÆµ°vj_}å%í›ÿ;Æè­§˜«gà E0Ì6ÀoûÖrlz–ê²Kj`úx |*4ü¦¾ œ°øù*ZÞFB¯§e0ZQäfmÁI`^.Rt±‹ƒeã0¾û÷Áö"wº(Ÿ ë|މ˪íÙ›‡eé œAõAÞ4]vršaV±òu¦@bûÛhã“d˜¡%Ξ-S8Ü@Á’å·©Êw@<_R‡‚e‹>àºS2açÀR±ð :6Ñ•kbÖ‰ ™”h}0È·¼Ú[DS'Äø$då®çr£Ègû Zf´V®áãBÿ1`äóêFõ;ßS.ÍüŒäáƒi”ØE`€W%ü¢Æ'Ò«O_¥ñÎu=š¬èùÏE|ÚeÛðþ£’OCb@_ˆ(%§¹¿ɼ,ŒÃË!3ûN_°$¢¦f¿ÈGs `šQ:L:ðbÍüpØI°Ö>·²8ý…»äß‹¶¶×?×ø¡/n®¶¶ükaúróµ3§hËþSÂ;¶6±ðývñe93ÉÍm—Lå|Ýùæ•8ÈŒ¼ikkï[’ål{þÂã†,X¤ËÍ;Ž0 Æ9ºÎÒ£}„þ½`k{'¥ÒmïúcñçÌ6Ÿgo)^ód—Éž·Ñ¶Ø&š¥·6Yþ[p5”Ä¿uÞö⃜[¶¶—–Åß°µ½ö¡ö z—lH9il¾dŽžŽŠü“žm ‹Ýö<åÕäàþpjœk—ç`¾þÈ[*¤‡Ú£÷øA6¤A{ðÂã"˜}nM(’`±73…N»vßÌ…k ¤Öµm5Š,îPH%…¹F¡ÜPK…LN "®mgZ­¦qX“QÇ}B2“\ßtA4 6­’­G]§_-3Ü·äQ«ÊŠòÓOééãÐÔŽä#ÒÜÙH}[wœY‡õ•Öàc‘)m™gç½Íï l˜¼G?Ü=ŽU/5üõò:Ä¥óú®Áyï,X{y{ß×NsÇÌ-ßé2¹[tˆÁ$}6™U&àxt^ŠÆ¿ß«Æ,moGú) i£c—Îî\nWÚ=:$x8QÇÊîÂAó¯8¿! ƒCçÁJ==²ßˆƒoÚÙyºæÞBy…ñ6Á”î’À ß9¼¯¼æÊûÅ8¿Ñ‹›Tû͸‡t¬šôÔ´Ô´¢öœ\„\–YXKïFBû‘›]EÁáø-)IMKKɨê´)Ä—ÎÈÿ %5Z”±ˆ³%tEXtdate:create2013-09-23T21:11:35+02:00¼~%tEXtdate:modify2013-09-23T21:11:35+02:00uáºÂIEND®B`‚stxxl-1.4.1/doc/images/layer_diagram.png000644 001411 000144 00000152331 12411125462 020020 0ustar00tbusers000000 000000 ‰PNG  IHDRP«_©sBIT|dˆ pHYs ¯ ¯å †©tEXtSoftwarewww.inkscape.org›î<ÔVIDATxÚí½¸UGvçkrEΑsÎ B„B ! ‚B9¶r–ÚÝnwÎîV'wP{Þ<ϼ ž7žq˜ÏóØc{ìÞoÿêÞ:ÔY§v8çîsï¹÷.}ßÿÓåì½k×®°ê_+ÕïAð{ …B¡P(ŠôÐFP( …B¡P¥P( …B¡J¡P( …B ”B¡P( …(…B¡P( %PŠÄ†û½ßkb|ˆ•!nqwˆã …B¡P(Ê‚ûC qMˆù!ú+ª?¤©Cˆ+C¼⿇ …B¡PÔþ$ÄÉÓ•@U&qjâNijÞ²mСkÿ {ÿ A÷ …B¡P”=L ºô´éÐ#hÚ´™Lý(Ä%P•CžPþ™í fÍ[½†Ì &-¿#X±û­àòý_T( …BQ‹Xsë§ÁÂëžFÍÚ\ré0I¤¾b ¨º#NÍBœs‰Ó°©W+÷¼«ƒW¡P(Š Âü-çƒ'»$ê¯C,PUûäé’ß°Ñ{øœ`é ¯è U( …¢‚1kÉ mÇ–DýŸ·(ªÝèºoÒøMš4 FÏÙ¡ƒR¡P(Šz‚å»^7¾RŽ6j»¨Ú!PO[ò4uõQŒ …B¡PÔ3¬Þ÷qÐkè,K þ¡QzJšòÉÓNËXGÍÞ¦ƒP¡(#¦¯»?èÚç²<¬¸)?0ã²ù»ó®ÀѾýÒASó¾kÜ¢[tL(cÕ-—ôjIÔŸ‡è¡ª<ä©SµÓYÐgÄ||ŠŠÃì+ š6kQ€ŽÝÕËï™°tAò²¿wÏà —ç]oÕ¶sE~Ëœ°o „ó®y<öþ­Úç}×ÐÉtŒ+eþË­Út²s킨ò¨S4pË6ƒ•{Þѧ¨8ô¿lYd2¹¹×œUU‡pv¹—œ¢J¡¨ á­žkÿb¨¨lÉSÏO™w£8EåÙó÷~´hÙ6’@ ¿ºA¨áÓ®IÓ%9tì6°A¨öûä}×È™[tœ+eËõIСK?;?ßU•-º…†mÝ®K¸P}¤NQq˜¼âÎØã Z¶îPïÆnU_P,R(µ‹)«Ž¸Z¨NJ ²#P&çÓÀq«t )*=ä%ˆ Útè^@>â¢Fq¦t}tÀ²_3×VìyÇü›D±Ã¦^L»üÞ`ÅMoG–5÷êǼþ>ìòøû²ù»‚Aã×ã—Ü,¼î™(2 »ïš¹áá§Ñ÷ƒYO–ì7ß2à²åæÿ¨ðmÞû ß:kã‰`ì‚›‚Ó7³!Ï Ÿ~M0~ñ>“W†¬ÇQŽð Ý%½ó¾ƒÌȲ½ ­¶ÏÍ\ÿ`ÞµEÛžý®…[Ÿ6 ÀÈY×Ç®4«É+ï æ_ûdìs|³ûžWÏ][²ães¢ß;ræÖ`úÚcáïŸÆ–·òæ÷Âgn7õ°í4tÊF“îeúºcÁ2çŠJÓâ7kÑÚÎÑ«•@eCž:W'Û f„BMš¢âœ w¾4iÒ4o3wgбû ¼ßzžYÆâp!•deòÊÃfAmî1 ¶nß5Ò¯ªkŸ1y÷Bî–ï~Sæ]©F³8—ßêS³°Sï8MdÊ}Ò™æÀRλ„ÀH³@1‡žBL‹õ¶÷°9±å’}9ŠŽ˜qmÞ½M›57m…iÔWV·¾cÃñöª·,ú±yŒ9—x8¬¯Î[E%ÂIkð¶¨lÔj{(ðš}ë STFϹ¾€”°°¢È[›6 –ïz#5ê9xZÐÄ gõbÛÒhs’TÇ® 40hMÊE  2ÝûOLEbOX[”i4NËWn5{Ó© ùÅs,è+·ü(E“ "®¬žCf”ƒ61íwÒ:o•ˆ‰ËØqúŸ”@eC vÑ z Ѧ¨Ht Š»@A`¢Hæ³´*G€º2‹f›Ý ®UÍ‹Oc ”KnЂuî9ܘÑúZlv8mÇ(|˜0;Žš½ÝÔ ¹šš·l“¯™k×Ù˜<-²Nà’ˆ’^Âu3®x 5ÂäÚî’^Þþè?zIй×ÈB­!uy´$rAùœjï»Æ¹bq>^Ô“/¦Rˆ<¦<´tJ • ܪÇð?sÞ­¨š¨cêô©¨TàS$·± ÷Ä.lÅ(È‚ë#i÷HÓ•@aÆÃÿÈÞƒoÏÔ”5Z°õ‚Ѽ¹÷­ˆ?ŽlüŽ&,½-ßGhÛ³d!Oó²x_Añ%*wLò½:÷"†å=Æ®H$PhÛñër#å=|wNÓ¶ïã¼6Æœlýç |µ®{:˜²ú.»ŠŠÍ åŒó>J jN ž¥1ûYªLQq@+’g‚ /׹м÷{Á‚-O¥"PÝúŽ+¼ïúÌ1Fqæ7I ¨Ïɲð§qïkÖ¼ež6+ …Æ­€`.¸©ÄöþÔørAÊ0ŸA2 ]Íš·Z¾Ýe'P²<´ƒ>©lשg"’)ШI!Ú¥¼ú¶Î¯/'ÚÒ­óTQoÉÃ9äŒõiJ jN ^«Rí_®LQq“½åÅ ºÕæ‘ y÷,ÙñbÁâ7dÒúT*ê¸"i:’ï” óX”Ó±|§ë œb!—e¬ÜónQí Qê3|®1»¥ò£š¸¶¬Šˆ?IÚúŽ\à-G¶ä×%5>勸ƒÜJ‚ä^ç{|mÁûÚwîô1/3og°b÷[:w gž/PUsõœO`(u©kîñh¶‹¯1R / þ9IjâÒÞ÷Êh:ÄãT×¾—ù£ì–¤ÙWžÎ”@ɺr’@1m<~ÑÞ¢Á£dE–Š>•ÒÃg«Py>†@A°>,(‡eêêÔ}p¾3vH┼g^s÷,ÎïÒy;*gSAޤmÏøOAðç" ÉËM V“šˆbŒÈQçó_rµ|þ4…å@º“¾í¾Y8‹K3¯ŒôK: P¡¨›”0¯ºcµŸ¨š¨›â…¢.ÀbÅbW*Â$•D |fkülZ´j[–$PøÇøüs8^Fj?ˆ.Ë’@á«SA–\2îý$§”fTI²Ò(é0_¬”ŒŽ“fT úF:Ð'ç*@É„šø@áÈî#ú¼Wç±¢‚ƒrþ%Ds%P5'Pks‹@Ê«BQn *•Å".Î&eÁb8}sbùUòØ=ï8C ²ŽÂ#¨/‘4aºQ8öo|Å$Á“><Îñ±ä¢Ç€I™âkB d4$é»D‚Uò?¹÷ué5ª, Â÷=$¸Ï“jAç±¢Ò0iÅ!;Fÿ\ó@eC .­f£fG¥ƒLQ ‡‘4-1ù£0xâºØPô¨4ÎKrHò£Ý\:óo™™ÚG XÈ9΃r(OfI·ÙÓ³&PhˆHÝàËE8=Gɰë„àué52/¾:ò¹!“®¨Î¥ô©I…€OUåsfGk„‰mÔ¬mù¹’í(³Ï‰¡ƒHáçK^:cÝñ²(’¥BêÐи°ºZãˆ?ÉV帉Ê>¯PÔ%úŒ˜oÇèÇJ ²#Q?¨Š®Y§ƒLQçàÜ3¹0ö¹0Þä·ç‚(2¬ãT³Y®}ÎË’@¥)‡Å_:/g•H“ÜCrO“‰"â3“âÃcýž †²]} ²–tØs1ªJCvEQZG’†&e"/•@µïÒ·@[תM'o&{´e@ËŠJ‹jvܶ+ÊŽ@ÝN£¶íx©šñuŽ¡“7¦JÞ(!ªMÈzõi>…†*îükÜä˜Q §k’{J‰kÎ’Yȳ$PíqÇnƒŠ>ÊeÌÜ"ïÅŸí ïLãê_*‚prPo’/íN¾0Ÿ™­|*ä&Óy¬¨4pX¶“…¼«¨ìTÿÿd•%·ê`SÔ¹š¹kŸËrÀ¿&Í9,÷9€y/Š@á?D¹Dœqü ÎÒ,°üu$Lªzÿ1cæ±!ñlHú^œ—ø3©¾ò(’Vº×qXŽ Q·èÓ^m;õÌ:þß¶c£¡ñ™é©7&Gë,N½!ó7ŸËECºu€tEÕóèÈežé{ñΔ>Nn™Ô;R#yÝ3AßQ Mt¤Í…f ókŸáórõôTî{º÷ïø:+ï>̱²¯ˆÀÃ,Ùº]—<Ísˆs¿Q‹‚9WÑ9¬¨@|êz|93î *G¢.ØSèÝH!…¢! Š@¹÷0îÓd–Ž"Py&śޮ{•ýÞŒF+Ê¡Ü( \%.ËBBZ÷šòOÃ}9V­½¢ÒÇïp£ïÆ)Êž@õñ·42êptŠÆF Ò" R(ŠJI ƒAµ¼úB¦¼AÉS‰:ä‹`R(”@)R(õ,óø¾ƒnýÆ[Yõ?p×QU^õ®u¶ÄT¡B ”(…BQÿüžœô"8Ž/Ïœ/(i* PmBüÌ 1qÙAˆ %PJ  E=Áª›ß“QÉ·—…/(iò’¨^!~áy!36+õJ•}ë'Á²_ËC‡ñ(Ÿ·œÊw¼V( ¶Ç$’S( …BQ”–¤˜(386HøvþiˆMµÆ ” E¤ú„¸5Ä7mær…B¡P(u†¿ ñJˆu!ZÔ*'PbT2™º„°È;BÜâ|ˆç …B¡P”φx$ÄW‡˜¢iñ%C …B¡P(J  …B¡P(”@) …B¡P(R( …B¡P¥P( …B¡J¡P( …B¡J¡P( …B ”B¡P( …(…B¡P( %P …B¡P(J  …B¡P(Ú …B¡P(J  …B¡P(*€@…ÿµ qˆwl÷ô×AwqÏNqýq½…§Œñâž•âúYO}Ÿ÷,×§ˆë¯{Ê8&îÙ,®ðÔµ“¸çfq}¿¸ÞÖSÆPqÏ&qý!O]_÷Ì×gŠë/{ÊxHܳI\ê©k[qÏ~qýfq½“§ŒâžÍâú1O]_÷L×—ˆëOyÊ8+îY+®öÔµ…¸çq}§¸ÞÝSFoqÏ6qý.O]+uNÔ– -îY+®ŸöÔõ¹ µ%fŠëóÅõç^ÖÕSÆ]:'t],~N¼\.µ"Ä_V‹Åâž.âú]ž†[.®o×›yÊäéd÷ú.O}o÷Lׇ‰ë‡P„bNk¤ÄiKˆŸ†øP…BQþw5‘[K¼?ðÕ£Uë6A›¶Š‹fÍšûæß?„8,Smh^í'òY|ìvH×‰Ó‹W_lØz[°éºƒ …Ba°îš½Á¼¥›‚1f­Û´sù?†¸»\‚¼z“÷ßíûFެÚxc°÷ȹàÌ _^ùôOŠ>üUpߣï×í¹/˜³x}ЭGwþý8Ä%PµGž6T'50}îªàøãê@U(©úÁcÏýŽpùOd2¿Ê©æ!>°åwéÖ3¸ýþ´ý:ÿÂù·fÓî i³f®6x‹¨ò“§Ç­@êбK¸‹{R¤B¡( /~ôëàò«öHA¾6#Yu•Uh¾ž~û'Úæ …ƒc½ôé?Ìν¿—ëcŽ®kªªx´Ê ¤É3—O¾þ„ …"AÞ»ß+ÈÿJaT‚¬Wm Ö\y“¶±BçÞûEpiïvîýQ9Ê< ÂÊ8yêâ?ÓÈã§Ì×§P(2Åù7þ(èxIW+È?®¬âpÙ?¦œ~‡s…¶¯B£§Þ š4ijçÞšÆ {Ej‚ ]ûNÁ¯~WB¡È·}Êõ‰ÚR¢¬:ÊóDâ#mW…"–­ÝæšÑ)ÊŽ<­µBm÷ígt°)вaÖ‚µV9׳yõ#ž_±þmO…"­)ïý_»^jçÞn%PÙ¨¯Ñ¨“¦/Ö¦P(ÊŠ o}æšò)«šU;ÃjÄBQ$ˆ¨¯žw/Ä̱iä¾ÌÒj|ˆ³ ”mÛu^úè7:È EÙq×É×Ýã':!¯vð¦mG…¢Xgò7ì¼û§­”@Õœ@ÝIƒ9A˜B¡¨³Éq&[„¼:Ï3¦-lPíqÓ뮹%‡ûªx3¬[_ðÐù/æÝsâ— î!³õ9ûò· Þõè‹ßйæIiàDãMRUsu–Æœ:{Eƒ(/òÛàñW¾cÂ7{Þä¡áX‡çßÿcH½ô-#Lg-\gŽ1fj8UjÕÚ´moùÒ"ä¥ –_U¹­.›8;c'Í5Çj¬¿öÖ`÷ÁGBÒøYdY¦.È;ǬÒ7´ÈRyöÚžCåݳïèù‚{N>ýå²ÔçžGÞ*x×]'¾ ó̃æ-ZÚ6Z1ÇvÅEé•B °»Ÿj êUsÉê- bp°Y°âê EËVÞN›6mô0Ü6&¸}î©7lVMqúÙ?4åí:xºàÚU×ÊVˆ=ÿµ‚w”3€¬ÒQ¾øÍŠ ×ï}Àx柪@­C8çu]] GÕs¸r»öƒ[x‰»(%PD 4 ¯X´ôòëêýÀ¸óÁWbL 7a(*‹èO>ó¦¼ûO\ãÀÕ,¿÷‘@ÉwŒ›<¯¬‰åû¶ìº§"ÇýE¢•@UºvïmûâšÆ@ Ü³E•@)R¥ªbpîµï:w+J)j¸jëM÷ÔuՆƤKòEðò'Ÿ«@U•)B >ÎÀѰí¨Ôœã3dQɚݴêÙw–÷M \YãqÍïR½(%P)jÜG&®Þq88ôàËF;…c&'Tc“ŠÉ>qú"/ZµnS`Œº¿+%P•&]ùä¥5ÊT Ty ÙžÝ{:ÿ©™“ò¾á£'˜Ç!>¢ñ¿̻ǚæo|²öß}Á±âüE?7„èŽã/ûîzÒ¤ˆì4ŠrÝú|SÝ{xOÞw‡ßè^Gž¢ÉBç1¦ÜÄw}ü›‚{Üë¤ÅØÏÓ&B” uêÍûªž»ïÑ÷ ú$îÛ*˜@}bV–>PœNÜM T½HfÀî/n·ƒ`»åð©ÊîÙ{`^Ù-[¶N|¦’Ô}¾›ç·tììû9¡óÀ¹OŒ¶fñêkƒko<Üöƒ‚ç,àÞgŠ2cÊÝ+ÂV.Rh¢AŒÚõöê;8ï~"êØ!2&ï÷%k¶æ´‹IpÛIŽ8w ˜´»gÑ‚_"jñ‡(ÆÕÿ’.= ITU¿ ¾q˜ƒ«å(…@9-ç¶‹V­ÛFfk¿ùγF+›4îÛ´í`6µI 6ï(¾§¶ÒHHÿˬ”ï|BüB£TÕa¼MbM|›–(RÕp@‚4_äIcLc ïyø©ßÏ”@zæ+ÞTiÎ6jR,ŠZ8J!PbŸÿ-|¬pv÷™3\…³wÁìÉ] rð¤PÃCÜ®ªrñØKßJuŸTÏbQ•Ž@å#ï¹v×Ý‘Ú? G¢ÎR¾Ó—*+%û–ñ!µG¾,÷.Ú¼ó®‚ëÒI8 $9M㢙ȕ@8‘7m–7­9„ɽoÀѱNä;ö=X´|mÌŠT28Fú#’^£ð4 ¯^å"Lœ'Ì2¾{pŠ–ƒšðr%PéÄó”{D%ª~„XËë¨J–t±–Ç(”‹@Q©Ž—‚™: vY,¹V^癨(9w'Ï‚'èqZû6̾ä†J ²s m¯ ÈÑšUA ‡ó|D¥s3„‹$®¾ùÁ8ã~©©oÌŠ´.²œö.1íLÎ/R¡ø’s*RU/i’Ó3f˜«®?dìþ¹3“9RÊM ¨—Ì™ä_œJ&P@jó'ÑÆDHœƒ4þBVà[gsŽº¸rûíA·ê ÞÒ»œ(¹(x@NêF0B’÷Ž7£à=‹ mË•h=v¯Ò‰ª/´ßÅhÅï49kSÇŒ J ”›ì2*žL¤ÉübÞÊ{ Ï'¢ŒœOhbÝÈ7%Pù÷ørSªŽ•@)ªŸ™ÈÓ Oÿ¡©NìΊ@%!QéJ“‚ÔN‚JÀ¹PQŸ]›—œÔ"b!Ž:ÂÂj§d^I ¬V)ÍÑ>.R'Ø$›¾ÌƾÅQ ”(6.\D3Âíq`:r¨CÇ.©e“¨ÂÈ]|Ÿ¢ò|áÚ óàù¢Ÿ+ÔjUˆžY¨i!^WU¹ ô½²Â‚štئ¨BÅéèQÇø&*™Ç%:É`“‚ä€å$PO¿ý“ GÏ~‘õAcæË2.  I¨/qš£\ÌyÓ¦+%P›@™Ã„?ü•Y¨åa€¤ƒ«û+ÖßàÝ ¸ wÑr8¶¨Â3±`@¤Ø<á4>{Ñ:sX9×%ÁBK­QxJ *hˆjâM]W0°+`×>lÔÄà†[Ž<¸6*Ÿ~—Mœø ù^ÜgҀź˜ï…ÌÈ2a–÷f›w'z Ò"Ë"‰dTÖw|Íà1÷LyQQyz|ƒ\S*Ú•½ú›Ýô· νöý‚gJî;dboz€ëå=—4!Šß‘ •† 3NÛFH"eÛȳ¸\sÞ®ƒ§ƒqSæ3ˆõ³ãzDiÎçÖŸÅÄ=ëŒßhs̤Q™Ó•@5d§û^‘ùÒg˜ž¾9”òA’033>Ù\J_DEzà‡+5|¥’k%PJ êôˆœ+‹!LŠòƒ¼4›PiõºðÖg™ŽÊ‚l•R&ì,†˜ø4 O TRÔ¾ö‘¸on²æ %/LÚ|N ”(…B¡P¥ªñ <}€– fN´ï˜qç P)uOA ~b±(%P …B ”(EYTÚÃëÝˆÚÆ…·4į•@) …(%PûîzÒ›GFÝ€—ûNÓ(R( %Peâ8y[?MÇQÝEú`çþÁÕ;î4gSnÝ}opàÞgŒ~T"]%PJ  …B ”BQÏ J ”B¡P¥P(²w"7ÄŒ, T´^éJ  …B ”BQO ”¦1P¥P(”@) %Pe&Pä² ã²B¡PÔ:vêZ2"¾¶¡BQ<š5o®*k¥P(u„kT^)µŽ(µ/ÄÐ,} ú…¸º!¨æ-Z˜¼ …BQà|ÀR Tóæ*¯ŠRD 4 O}  …ú@) Mc J¡P(R¥P(R¥P(”@)R(*- o|ˆNY¨ù!¾¥J¡P(”@)šÆ@£ð”@)j ½ô­àÙw^«ï|áƒ_gžÿZæåžý‡Áù7þ¨V¿å©7<ñê÷”@)R(”@)R4&Œ¼lªÉCR›ï|àÜÇAÏ>ƒ2/wÝ5·„Ø[«ßrí®»ƒùË6)R¥P(R¥P¥J ”(…B ”¨àåO~«ƒ´áØÙ÷ƒ[?Qðû#Ï-¸nÏ}¹ßvï3Á‚W3æ¯ ¶î¾×˜Æä3Â{­ÚÌ]²!Ø{äÉàÙwf~¿ÿìÁª ;óW0uöŠ`Í•7åž9zêÍ`ùºíæ÷ [o3¦1—üÜtÇ£Áþ{ž6×â¾ã™w~j ë}¾[E8n<ÌY¼>h×¾“y¿%o/|ø«`ëM÷3ç_nêË·ñÞM×Ì+óžGÞ6uã›o¼ídžÉN¨³/;¸üª=Ásïý"±Íùêµûö3á|ù<ÏD·qëÓ¼÷žGÞÊ™!©ÿèñ3ƒ~G˜¿O>ýåxÓeøk¯¾9lÃOL›Ð'=—û®%«·‹W_kúF>KÙk6íë±<¸bó¾àÌ _Ü÷lpø¡WÍuÚs,ÿ^¶v[0cÞê`óÎ#©¾] ”BQñªCˆæY:‘Ï ñ%P~_Uo F›aª-[={ ®¸&xèük}p°8±ÈÔ´œ‰ÓÛo9Þà'Ó“¯ÿ èÜõÒB±¸êúCæo«Þý†Wï¸Ó,úã§Ìú\xë³Üý³]\Ò¥‡!ûŽžìA¯¾ƒ‚§ßþ,8ùÌÛö3ã‚Eûú½˜gÖ_{k8f:†co[pógƒ1f;u |òsòéêÓXpãS±ß±|ÝõaÖ{=fêyƒBÖ:^ÒÕ¼ÿÞ3ï„$åG¦îý û÷àÊí·Ý.íÌZ°6èÒ­g®¼ [ö›ñ̼`L­¸bGЩs÷œ?•K ^üè×Ár퇻N|Á¼Ÿ64l¬!–´PisVò ëþûê‡MùÔb5lÔ$ó÷£/~3ö=|ÓfÍ‚þƒG .í=À´G¯¾ƒ áÙ±ï¡p® 7ï²ÏÑf$Ûƒ4RY ×¢CßÐ&Ü3xø8SO~‡ØÞpëÃæ[zôêoˆ¤(…BÓhƒÜ|çãA‡Ž]ŒàdÇÊ¢°ÿî ÆÌÀ"F¦a¹«/7vî?iGMËa±g÷Ý&äÁ%(O¿ýCd ,¨U„äçϠŨjó†|°Ð»÷°H»ýïšðî{ô=3‘¥S4ZcK  äiÏ mÆßùà+Á¹×¾ï5áAôXü󵦟’f ÔÑSo-[¶6äÒ½oï‘s†¢eu pÒŒ%©Úš1¹´¤Éj€ %J÷ÞG_ü†!3§ŸýâMx(æÕ:Ùöä7úËþväÄkAë6írdºm»¦ýܲhžs d쥓wucãÑP › 6 Ï¿ÿÇ3‡ÑÀú´Â•€çÞÿEN]× oJ ”@Õ)ºõèS¦Ø™3A|÷°SæžÚ$QY¨-»î1‹Kc˜PhfX]_LLü™kúÜUfñuf„{ÐHÉÅ?É ¢4eÖ²‚{Ðð4iÒÄ,P¼g줹©¾±ÑC«äŽGI Ðxa6ôõ·%Ph³øî4>PL6hÚÒÔóÔ3_ IQ{c†³ÚÈG“ É‘í‡~­ì¾e‡Œ1 ïAø[ͧÞ4ÇhY|@ ùж,†@a"tMG¹=,’CÛC oi¿±Â; V³å(Èf-Ûç.¨—%Ph©è³$…ïÚ›bsÞ‰›ï¼ýþÌXnÙªµ™ƒ¾6¶m[,jÕºmÁïM›6+еïp‰{5檯<4l.r5[îx‡´¹¦Ý†F ¨ó‰ _6}†¦âÄ&²Uë6ÆìiïÅÜë#™µA ÐîÒF”!}GC$P´í§*7ÇÎ’L3K5 Ä6%PÕþ!!ù`çÅ_Jb’`B±fqcÇáápï1fªY¨&L[hvÊCFŒ7>;Ô™]¼] аëb!âš=8óƒõSAëÁÊÂ5täãÂ![½i—ññ™ð ”‰S5ßÍB‹™ ó#° äçŒF„çXÐYh1i¢uÁÁ×Õ¸pʳ&«}w=i¾—z°âËâ’¨«¶ß‘Š´¤×Ü0„˜ö§]¢0aêOtÛ'¹÷ãðŒªhÿÈ‚(Òß®µ%2|/¿§%P#÷=˜‘¬ÖFj 0Õù4¢¼Ç¨m7ßï܃@CÒ¬ ±ƒÏ>Eh>ÓFZ­m€oc­þQò~üÅå;µB Ø@@€$ÁÄÏ‘ùé(ü± ´€a»á·ÕPMx,´˜.½¾—w_0ßb7lÒØøÕ¢¿˜Ï•b6«t…,ÃßO ”FáÕ šÊùiÇÀ%Pl$ªÆÄg¦<Ú•1ÁoŒŠM²ËÎ6™vó†œc³GŸ0/Ø,Ѧ}úíyÔ;Ÿxõ»†ð#síýÖ'.Š@Ñ—¼ÏÎE¢L±0ÞÙXðn6´ óUÞO[0Niú³)®l,åZÄ&𲉳ͷñ 2RE~ÑÞü eÒ„‡FßKúïeÞ3ÏøÞ)ƒe Ší;v6óÂɆÆn2X[h·JÒp)ªcE¸µ›Ë§8¡Ù+'ˆ}@s@}¬ÙÅ„ðû¡ì5dÄÐ>Å„NJ¦ˆÀH"P,¾(&¾] ¹—ïp#v\ Ù•¹J.>ä\bq‡TJt Z¨«»pÊnµ_{1rÁ®mQÜ.¸˜ãU I¾®ŒÚAkPÌ÷°{E˘äu ?½¨®,ƒ¡ãƒ}" K <°cÂjéz"Í8…?$õ²íâ#PhÌ%ñ8“¯ 9æj˜1ÿ±Ð!—]‚¦†zp¿ë³™D ¬ŒwÝ(™‡Ÿ£+ÏÐÒàv ‰ùà IÈ&7ße@Îä¼çy6¼;Ž@ñÄÞjìŽ?þ¡‘©Ö•rh d¬lh WkÎza£Fe¤d(ÏÓ§róC=ØÈ¸.Z"’åÆ™Î£¨S!ÆféÕ\PJ .šðÒFG¹`Âò.ü{¢î8Yb T”°dgÁ½îÄõ(vIaæ˜ø’šß³$8´¡ñLd©ã“Ô%P®ÙÒšŒØù³[b×ꆞ+u —@5¦Lä,´h)!­%f´-,”®ÙG ì&Ë´KÝÿ1ÌFh=}© Aø`ÚtiäÍç…6–{mMu!}Q]-(¦JÄCªHUÔ¦ ÓïŽ#PÈUHí¥Â/3jƒ¡Cd¯C ¨“Ïj‘–@¡q÷ûsm̳.àŸ‹1jö¯¾(Â+3Â&À(VÓÀÄÇ×'.A%fêÃ.$G œ +˜\ @¡VEõW7„`òùØ n…'öt »CÌ{Q°~ÁÈR¬<µþcѦøªö²&PD°&*ÊÇ—¨[ÞqÅÌÍf9î”[%PJ ¼¾JøzàóG!b‚{\Ï¡®ål1ßnÛ7»׉¼¦ŠH+Dæ£b×Èn…g² PÖç ê;ng\¾Ñ:„û&N~s}l®qÖi j„¾ÌÚ]Ôñ;¡p´É/1áVÊw50®£L6Ö%N{¢*:öØû©4PüÁ±IYäŠ#Pó·Šê9‡æì´ïE…¹1Þ¿tRê4È4ŒRõ1Gh|ârjaN•¨r(ÒSð.+¯\ñå³s“×C¨ÌÏÂS匟ÁˆýF ‹?‚ŠÛçôËu´4TÁ ."ÀpÆCÝëÚý³ P†… ¡†ÐÆIäêX"bˆú#MBV R±d‰ œïcGÅ·°»b#ó@¹Š]!‰J©N’.H§{OÖi j¾«&YÛÙuÚ úÔÍC¥(?X¨ãÌ0u_ª¨â”=ÂÇ’pº>Pøm²iŠÒð#m Œ, ²Šh³¨$œæ§Ý}Ì‚,»Ùé‘õqŠ2]™hƒìFùŠ,Œò3·Lú@ÕbÝ ú.êX‚…4 O£ðb#³Èy„g`B $ ê(§k»k€t0øØmAœå•ƒzEí>ð%bñv#š uü¥®&Ä”[| ˜”äòáwÚÈ8ëÔiµdäi¡Ì( ùT|Õ.»3&7mÃ÷ÊÐveˈTóDÇ`ÿÇa_F–#AM4HnŠ…bA;Ù,ôJ *@¡}b¾*ª2\á¦Í•q,úhêÙ ¹Z@6VÈRÂõ]ß*›Ä—7ú1+eO@6ž$ ¿Tä¦ë‹‰ gvR}Ø”® c#Ê3q Ù‰ÿ“5qbÑÀŒhÛƒ+›k|Yå±aÈcFs,µI Øø±~A€å™ÈgH±/p ä~Úc”@5P¥h8€²³}ðÉOÌ‚Šf¨ßÀFè¸áÃeŽù8p"¬¬Ó¿%±äè" ¹ŽÐ¾ÙÝ#å LØD¯@~Ùa’ß8„ Ÿî•‚‰+‹BrÐÂçfÁ‚0Svô‡zÕG+œY lø´›y›{]ÿ „,Z?4ƒ,0®çáYŸ<­Â¼ÕyĘ](ßCT%ŽÇ>3vX´ÈeC4)‰_¾Ô޶gfÞ'5ø‡aÞäÈ ‰yK7™T´/‹1æ~gA¶G´° £Mf °9"²”Å€o¡ïÝ´ $%M T2¢m? ê!`¼C0Üœo9 {8Vñ5efsG?Ò÷ÛªÍ[å PŒ{òs’µBU•Ðø oæ;÷“à9GjÜ’i2†ŒaÚ„oæ{\¢É|EqÍ-¦FæuT¨Ú"P6Ï eRòE±)§½˜ç¶/èK·ä*QÖªR¥h à(„9`pþg'ˆ`%¤×5' ,X ,¦® M×Yì«Î ô=›Rwƒ‰+G±QìJ ”@)*œ@¹Î›®…PXÒAr,BS„öH4kŽ¢iÚÌøpA ÌŽÌPq>PhwÜ|7˜@Ùé%©¾Ù"(dâO+ÐÒ¨ª¬å½ |Òh×Ò(ˆ(»s{Nšk¦áX ß‚è#P2 bëúíÙ3ý¬S+‹.N¾¾¨(K |&<„ÑçE;ð],®ìÔãœÏ*.R9Ó°1ÄÜè;ÇÒÎ%´†®Ö–´i7NuD Æ‡è”¥ùä/*R4t…vÆ—°Žñas£@(\‡zK 0…qÍ÷<~\ìò P¨Ý]‡Ð8…ù dóf±pÆå]qÕò˜´|× >i ;ïCFAèSCUrŸ'(Ì-¨óeáËpï#P6¨ÁýFiʤ.ÖÌ‚¡4¸AqÊÍ»#È1õÚÌÐÉJ ”—~ó(š_6v£…œÃüÍÆís%ÕYÓ(R”Ñ„ç‹va±´‹5$É5;XEÈnÔó¨Ü-‚x…‡9"ƒ9bãK›!É’»™´-BÝ.³¼s^¡%Pøs@.¢ âøŠYM¾‹6 …ʤ¨2¢"ž$â°i÷7|•¤šAγÊgÊ@;—D Ü#G$âüp’(*RÕ€i‘¹€kä„yÇ&m|1©(”@)R4@»*È€u4"Pø-à;#5:øAš ¥(|£x?Â’ƒ$àÜÎ{$Ù²ÚK  ’ü$Ð(Ú‡`yÞ»M«†ç 7ŒÚmK¾_„©Ì„i_Ÿ¿G žïL>|ÃJ%P,1‚s,¾TÅ Ü ã"*Ì]Ñp€?!&0Œ¬«3L•@)RÔ¢ ß48$œµqøvÏŒ"P6¬%ž'Ìšü.h|\'òb ÀÿÇ˸d„…¾>W‡tüpØÄ±Ãw]'rLƒ3{IÅ€ðïÈu"'ýNÖ˜ÚÐ"àäJØ0o%»NæMJ‰þM¶¾¾yep¦AI@Hç, eÈ!ˆô-ެø?ñÞR e“IŸ¿qJ§üRSX4¥PÔ'þ·/ÄÐ, ÔÈG•@):"¸*ÁR£i UáØ®v³—«Ùaì¸Ñ0”ƒo$ƒçñ°Ïã3র-•õ¯á`gY7"gJÉ…yo¢.Ô ó„Â%.8DÎOˆ4´+Üëjø¾††ŠÜ`î{Èy á=”…ß9nnœC¼”;$íôO‚85èû¢¨v|$ï7¾A&ªå½.Ñ"jÿ,ü± p(¢æ ½–@¡As³)ÓßîÑö=&²ö»üü‘SUÀÌí;D×5-£EŒ+¿>"Ñü&Õ¥hJ£ð”@)J%P•X7êåú˜°éŸ"ˆJ\Y’@U p—Œ J ²Ñžq)Ð,Bà}iH‡É&1%><´3ZTÒxÈ Cc”œ(£z•@)R¥¨hEêüÜdž|ŽäñÁw|P} P J jŸ@‘ðR˃©$ÏbzÇìÌ\ÃGM¤½–6VC„=«°&g~*R¥hÀÀ—%îÔóºÌî Ç/˜¬¢Î5T(j¨ŠÅŸ°z×s,>Šæ(–-›ƒÒî2@¤ñÈÆï™h݆ ‚@½bF–>PíB R¥P(‡@á{F dv´dâÆÇ·í·„ä9ã[¸ÿ‘êö´çè±ÄLI`¹ÌH$‹?#~‹²Þøy‘K ‚H`.—MœmÒµÈÔ"øƒU½§êX)βÿ’oÃgïÀ_Â(3êD½©?ßÁ½6[=ÖñÅÔ(<%P¹pç8¶ŽPHrf¬ p²¬§Ï3É9S¡P4<Å™hdewŸš}ÈÂÈñ"Ô‹oB–pÀ¿‰ètI!Z•߉fäÿ,¤6Õ¾FD¨òÎÃÔLZ¢&ãfOK ˆdu£6!øõÈ­q²’ˆÕ8SNêc˜åWo1gP${½é^“GŒ¶!¿QŸœE‡ßÕÎý' Ê£r,á^€ù"äyü"MŽ­ê &ˆ"F«F¬Úx£ J!2òç¶#>‡øxA~x†œoËÖn«>³MÞÑ5Ò„Gó~’ò4Â{8㕈L=óÂ×ó¾ƒ ´|$Ü$Á/Qµ¶¿!é¤|+ReÌ-”çOƒ*ØÊøÞ/êTˆ3ÑVîoL&Wþom#Ï“R(ÄÇŠ?žµÈâ qÁ4ìF­¡eá"Z«E²ªgïyç£M½Ü8wÛŒû¤ß`‘æpÚ4š¨(…VHšï vÔíNÚï‡ðÙ”$>Ú¢e«œv³Id­ÿ"ZÈPUdn~„™»!äpsâòMÒµ€M/Ä‹wIÅ¿íy3>Ý#ŠñÜËo®%eÈ gWÚ¤À>Å¿Ñl¹ïá»9pšhU·J ’š]ƒ’»ŸÓaBÕLb>’5¢Zåo1…ª\ª¸Á-‡Ÿ0¶t„‚•lØrR .Ç ‰ÁÇ„Êî†¬Ðø8YçP&B‚¿mî!K Ø™ æ:ïò‘&&“Ÿ%BÇDv¨vBÙ¶A¨ŽŸ2ßø Èü"$oä 5TÁ8zžIq^Z%‚ï–jî8`²ÀDB¿×÷œ+n†uEý&P÷ŸýÀhp6Î3;mÙoZ|‰â@fz—@¡]q³!HqïGV`âo‚,ßÊX2æ»fÍÌ1FÒ Æï?’¶Ð>%Ígd)ó™‰Ï”ÜÃÄÉ·3ÇÓ¼ _+kÜ=vq >IQæWsTUµ³6rŸ¤¨²¹Fôab½ñ=GZîµ>Tè¤ñJ‚Þ "PšÆ .dˆ ÃŽÊüûýü'(’âtˆª¡QÁGJ{ª›¿51ƒ’ÆQ#¨—!^L:œ]ҰΛ¨q9ƒÌ5á!€¬“(*yüø›c,,Â4‰Ú–¨œÙUPΞ® lÒý4H%ÿƉ¿üpjDCƒ%>μ]é¶Û&ŒãH†žlÚÑ=32ìä.Ü£Z¤ÚœoðXu6»Nꊀ†ôBDù;)«1¾˜6h“ªû?Ï»FûØ(ß{ni3'SþCç¿èÀ7ðN<€JÞ·[eœùvÉ–@ñ<ïrwãi`¿ ¿ ©%À¡AזŶõ£oݬñnØßªÊýY£!P|+~-¾Ã©Ñ´#ÒFÊù2çãØD²¡ °(ÌJDo¹°ï÷(̇݅ù΂ ²Ò7®}!ä]Z ¤†6ãæĉo‡H¥yž dÒ!Ìl´Ò(ˆ¤{Ög"êØÙDK äwâ3fÉoT@ä\ T#'PØÙ¥Æ‰È«‘Š#P,¥]òƒ’jÜ”ùF°¸ð¥ÄòeK ˆvp#1Šñb£­’N“”Ëj.â7ÎNs‰„‹2 O®fÍ„ï±*]ÎV³Ä“EžgÜo‡à1’õäBazå 9vºVxQvÛ´ u¥Ÿ .Iå;O®±P@ù ðw”?œë Ê³ô5÷Sí"íÆîí Z9;ÆXˆX| š-[µÎ#²Q$Ÿ¨„¤–÷±0AH\"©‚w1>X” ó€°gwñ„$Ñ7ŒCα#©K 87BO9siF‰mÀ|€<³©`L0OО²© Üs-qâ|ßGÝYĬS¯¯½ès6 ´I•v£½Y„Y lVw„¼ yglð,›yXsC$P¾L]}aö,–ŒÍ¨Ä¬È"Š]L}Ên{Þ[ä™þ,ÕÊjÓ}÷cŽd~&Eâ¹ã&î>êèŽGÆ߆•€mGj¨çiGûÌq¾;ªnÖ9»Ò ë"s+*ÏkG%ù@)ª#Å'}ž0¿±a±‰#PäÆàù¨ ³K  2”kóhH°X£ž¶÷[%ý˜Š!Pì}ïbwçN&f(© ÁO‚: ]“e°("ü,YaQŽjä:a‡–õäb7 éåo´AVCXìA·,ÑDV‹C»‘ Å%‡ÅšðØ­Y‡[ë?€Æ ŽÕöLH¶ì´iZ#ÊC€Û:"ÑJR¦½³*>+ðXdÐXB„,B³1ÀÇÅnˆ>å7kr†@Aâm¸;ßÀ¸Øwד© d‘9b“b®aA´š%|4¸ÏjÉ ›QüMª´W?6‹Š ——íÅdÜ’eæíË÷Z…Ʋf5a ˆ‹U©ç¥ÕÅùƒß8­ H®Kž­æ™æj®| 0¾˜óDeÉŘy.% â3ßå;nŸ0ßèí|-Û“A‡Ž] ¹NÒ<² dæ†ÿó~;¶ø~ä6rA~Ý|¹QxŒyc:=pª`#„¬ðEáUâ;Ù¸©ÙGfÐï´¯Œ¬Cµ*DÏ,} F‡8®*9u Œt¢dw.SHEn•¨H7äÖ"îe7W'B­¯“%P2Œ´U?4|£K 8HVÞÇ¢-µUîÎËu˜Ä„Ç¿¬ø_ÔÖnaŠfÃ5ÑAbn^KôØ•B m»ÉE¤& ƒ‰Ì©‚ D;yf!B §ñ«@S ‘c2€ƒøP&Fú_qZK XL|&T´›Và ’„Ÿ´‹w ”K’|·{¨0$4F3ˆ ¨l/ú²Ââ,Û ³9ß.5w,¸f 9ÚDjzY+r\4$E¢íÃQÂÊ"æ¦jˆ¾N˜ Y\é+ˆkº"PÜÈ@!´$¢d¼Ö$ã/Ê|'Ê‘ÁŒqëõAóÉD;’& 7óŸo`~TEªu0›÷ÈmŠüæ€v•qi¶]_P´g”‰Ó6ó‡Í6•¨§M%ÁcqCxò;f„ÚˆXÄ|Šðn×¾“šüÇ.ɧi`ÜXóí&w¨5!P8è£õˆÚéÒÖ´J› [úa%Á¥_HòÇÂå{ž!go“œÜ} õYŽ‹È‘J.´,rnbF€†‚… ¨ïûX°0%ÈöÂ,‹™Ð÷~Þ"OB×W.ãÞ.2 ‘@ácƒéÒ©•Â7‰ñ,dEH¾t]`!åÙ(Mí¶­+ZBœÖ‹ÉDNè’Æ|çóùÃ%m3ßÑg.Êñ–ä(Y“™8\´/:è5çr•;»$¢ë -´ŸUSãÿ‚‰ÃO %ýî’ª´Šg¢´RÒ‰œ6@À éÂ÷ÿ̶^Å(«…‘çlL„h=!Uî±GjvÞ‰¹˜oÇïÅ~/&Ä!4˜"hü_lzŒ´ ?‰¨E®Xe“øQW7}Hû³1±¦_{Qwæ&¤„vå;0⿃Ð:ÀS&qL¡V,£RӲǧZÏ,B^æ×QY¡¨tX_.”)|¾Ø¸ y²&DÀϳzÎ…Wsµ‡ÆdçZjêì·iv¡Ø~mJ_(„<„ˆ…‡…ˆûÙù²€âÜËýÒ©£fqƒ´°p@v¢ò@ÕEûPê‹þe6²ªœi Т~¿鿱å„sH ZGy†6}éŒ rŒmJŸ9Ð’© aÑÇWr!°y–ÈK$)"Ïw@|¹’0KãOÂ;!MŒ%È‚ë$OÛ@&ÐŽÒø¬¹ß•›"ú\ß ÆímBÛ•óÁ¸þPAÝÐɹƸtÇ×y–ïÃÄ ¿—íÅ¿ÑÀ¡¤míI÷|/Ž»®ó2å@¹’ë‹Ø*ÆDìòEÈ«Å<ƒæ3)e…BQi@ö gÝdÓSln¸šDõœûO1sL Ti š6’I‚,7*(Ihâ\Ç{ˆ°âo+ ïdqA “))´,d£E˜³pQO_8.γ°{/1¡PÉè1Kñ>i*á]2Š" É éò%À¤ä:#²¸øvæ|+uðÙºY¨\'EÌM81B)[æÛ)WƒÚð­²9aʪDœ×yQŒ3nmŸ+ˆz©i²$ÎÕúÙ#2ðù*WÝ©ä¢IòêK¼|§(Šw›+vØy÷i£$PáÍÈÑ Ð¼†eþ²RW éA½åƒ6аR7·Bá‹Ö‘Yš-ꚨT*03b"Æ´Hä%Ä Gú¸ÜA5¾uÕ‚üùäÕ¿åYLÚ Ezà.P=ïî™_?±°¡¨ÁŽêÛbl ËRߢðÊD º…ø Ê"ÿG]u0ö`Ìvt2N¼6Ìg;Y¤P(êchýcµÜúJ‰òjhˆ¿«J¿²MÛU¡H±¶,S=ï¾–Q•+wƒ-/­OS¹€?~2øIà‹$ÏÆS(õ‡TË™ÿ¢w äÕ­Öw3*•‰B¡ø“êÌéì¼û›}•@eL \Õ8á鵕ÔK¡P4èȬëj(«š„ø.eÙK.-mc…¢0}®/μۑ)Q•Wv—ê¨&LÃûÒð+ Ez³Ý̱޼ú4#y5(ÄßRfÓ¦MM$®j«Š‹)S8ƒÖ™w_N9¯> 1+K…М†N ªËâs[6Éí©ô …BQ ÈyCâNGV½¢s†òjxˆÙò{÷bˆž~î«ÚþŠF6$Óå$rVÏ‹ ñXˆ6)çTƒNcPVUýŽV!N‡øg«Â¹œœOßàK<¨P($ñäè’u²ùrdÔ_…¸²L2±iˆC!þ·+qEàý‡wQªB¡P(ŠçÞûE°éºƒæ¨µêõû/Êy@m¦1ÀjAüªr"yÆåž·äiêìå¦Stp* …BQù8vöý s×K-‰ÂŠ4£!äú]ˆs!Úd@r…ønÖQxá»-yZsåMÁËŸüV¤B¡P(õ¿ò ï€á–Dý—}J"MÚœ^ÃÃþþ6ë4ÕçÛýeÎZ°V¡B¡P(õ½ô-¹WÍ~–&wTøß…³$Pý8Ù;ã·ÿî ‘}pôÔ›:nµÆðàáãrX°âjm— d{Ófͬ¼YQ¯¢ðDy#âΕKλi—Q]ZÛˆ¾›ï<«­‘(‹ác¦/|ø+%P%(ÀN/ê¹Ñãf(RTFŽV0÷µ]MŸ?So Tu™Íªýžþ±ˆî?‡Xžq=çñæ-‚§ßþ‰²N úôô8ÂäiÖ¬¹wœ]½ãN%P5 P½û ñ>óðS¿;¿•@)”@)ʉÍ;ï²ýý§õš@‰Ãz•‚<½“ä=_âû·S> ª°†O Î¿þÃÜuóÒË·Ü3eÖ²L ÷=ývÍ}ë(çÉ×`üˆ*™@C¾\ð &’š(̃8ôãšÕ·ÐžQ¦Ûäç~<ñê÷bû$ Yû]žã¼ZÔRÛ¦jÌE'æ]5ÙLðýYö's-ɧQ TãÄݧßr³“7‰à}Ò&ûNK2–’n ŒÎåk«£„ë 1´LヒwŒ™0KX##PàÜkßš4i’wÏ€!£kD ^üè×F‹…@v·ìØ©KX§‰Áºkö¦Z0XÔ‰ 2b‚yÖ–Ó¤IÓ gïÁâÕ×gžÿZÑ ›ëûÖlÚ]cÕ²UëÜß“¦/.X$ÝÓÝ{£‹àÚ«o&N_tëѧ@cؾcç ÿàQÁúkoÕ›2?ï[¯¹áˆù}ï‘sÁØIsß–õƒ›½h!E¾rî?ûùnÆU»öóÆ ¾]»÷6räÀ}Ï&¶ãŽwñ]<ß¡c—`´…Á¡^2×ç/»2¯ÎKVo‰.ë ?0õ8tLî[¨O¯¾ƒƒisVÆFb2NÝ÷p`:¿ßuâ Á„© råµm×!X±þ†¼üx;÷Ÿ4íG¿ÒÝ.í\~Õ3þã¾ý™w~¬Úx£×ô¡Ó=zõ7ãæ®“¯G>{óŒ]êɃ óÍÔ…t4Œ ‚Dd#ÆL5ϵiÛ>o<µnÓ® l=µayéôy÷z…ç©h‡/¤4ßý]ˆ[£Xc êð$å#Ìt€5>uôÔ÷,\qMÉŠò0a%gÎán±mϱ }‡K˹åðE¨-»î)(ƒ…û…~Yc5Ù¦‹„¢i³àÑ¿á,ԇŽW&(ΠLkÞo×¾SŽ|H@ŒÜ{!#ƨ²8CËG¢6ï<’º>Œ½(’LÔ§sNW >ˆ¶ûûèñ3#Ç ä&®.O$|ÄžGJÃ1‡£¿skÐÚ •bçG¥±b¼:áä^@€¾ùuí®» î'Q¢%¢>XÂlѲeëÔýxÓª,m@€l;ý;¡Þ¨ð¿…!þ} äß10Ãz¼J¹K/¿NX# P·ßÿ‚¸¨rYñ‰’÷ì¿çé’aørW›´°ù´78•ºŒbÔµ7-x~Æü5©ÌiÔç>Éû÷Êõ;sf ´öw´hŠ%P ˆš_[téÖÓh7’Tš IjÕºmе{/C}eørÊa‚tµ’Q:"MGT$¤ÔªHO¢H¥r81±þh÷ä»ÐjùêE;úÚpî’ ©TRÊô4J 7ÐNV÷ïüzG ÂÿÚ†8_}°pÔÀý‹çr¢ævgTŸ×”@iž]´Øý—šÆ²"ïAÛ±8qáËÆ%Ì.nBÎG_üfÎlâbÖÂuÁ‘¯’†3\÷”¤%PŽódž¦­Ôò}Šï5nzžV-ŒL]@Ý©w5hØeFËÂw[?#ê|úÙ? ®Ø¼¯ Œ [ö'(K^ÑBÝzô©`Õ†ýÂùY²œí·fο<¸óÁWòL†ÔR>dÄø|2’‚'^ýn^ŒY¾ñà±çÃqò±ÑÔ9>’@a¤}Ý{¬É BLûÌ]²1ï:28òÇ(‹yK7’;yæÒÈù‚Æ¢„™P¶fSÙ—=zöË»‡ñKä3c„þ…´ÊwÐÖI 9ÁÌÝ]O{5Àä|³e\¿÷3¦0ƒ»÷\Ú{€ùÝí¨²´Á¨õ-‘æ¬êüMq ÙÛ8‹“]<Ä'Üûµ}•@)² Pý4»äRÔñÇ?,¸>}r–­-tZgñ´×‡ò:‹®¯N,”8 '(L– ;3w"‡@í»ëI±p=h™'* JãàÜoàðÄ6÷(©Yd£ª^T} Q²ŒÃ½šw$=ø>É÷l¿åDå#b÷<òVAöe©½èŠDµ`ùUy÷àÛ%ï‘.7÷:ZF÷:ã@–±çÐcmˆ”{„<‰@± qæÉ÷s1XsåMêD®HK ¥M“”–dL ñzI4ÿ%fû«›Ä3-B<âÿÄ<÷7v¬J‘E(vÒ;÷Ÿ(š@ÝxÛÉ‚ë>gXv´qi¤PÇɶÿ$I |&EÎ…*G `ÖrÍjî=D=Z_˜´ gz´Ë×]o4@8jã ¤9 mNB"5o>Òrâ—¼õ,㸾håf£¡¡Ïl}dh;rÑq¯ÿ°à:„Ú„€Y+Ž@Mš±$ß§îÒ¾& ¼ŽÚydcȘDõÐùOóîqµŠÇ{?ï×ÿ­Ê,×&ïºÜ@¤÷y² ¾øãI‚™D |DÌ:§ç4¸ Ö*R¤"P™Æ á,¼/†¸4æÙ)!þU9ÎÂSÕ¸Ôéç¾jü!ߘqÜè0ëS‰¤E„’¼Nx{Ö($ÒßÃõõ`Áp¯±(×$Tßž, ×8–%ŠœÒþi $h”Cs”x‚ôøÝ~¦ ,Îtï9õÌWŒ¶£BŽ™1NC…YÐ×¾}ú%P˜›JIBŒ™Ú%’@ÑGÒÙ\’#ŸvnÝ5·˜/Ó&PM‚øá#Pô‹l?—ă©³—+R48…öh{ËÐb)R”…gCÆ µB‡‹"P ´i$a³!ÿ˜¤d¨>!üY(|{N>ýå²(ÌŠ>ÒãúŤ!P„ÄG14¿÷^i¢ðä=Òô( ¹…zöä­ÙÖ'ÎqÛñås'M“’@‘F¡dBâ˜}%¢\YœáåCÞ#ûJ(’×–Z_4~qÊ7Ïzõ¤JÑ  Ô7J1½…ÿÍ ño•@)²"P8Þ&¥2H"P›fgL”Xœ?’Ô<›àU(»eÔZ« *s¯÷F@¦%P”%뙊2܈*œë‹%P¾¹Ž3y‚èF‰m4‹»ýnL­q dšÈ8ˆ´LM ”Œ„ƒ Dœ-á¦Wð¥1H"PÒ<—†@á›æ^ǯ*m}]m°@á .ë#É•@)Šð:bd–j8üfD þWˆ[j’Ï /βq"ù”@)J&P¾ ÙÒé4‰@±°Ëë[oº·à]ø}ÈûðŸ²×‰hÊ×¶4 Ž}¿FG¹<ö\f ‡›§)kEÒI÷Ú×t”D dJ€T¡¦fD­(ù½ø(ÉìòDG&$éÛ„Ó´Ûn6Ág’9ŽÞÒ¯+ãýoó@•ƒ@mØz[Ai|ûd}³$P$ •A$*7•@Ud^5úa–Å«3¤ÿ™(EZE¸4ÉÔŽ°8ûò AtŠ!P8:Ë+£éq  i—‚S—Kd|šž‘޽Dàá?Cjƒ4i p´…ŒÉ0rÚ£ÊFBÌ ”D èydn¾¶*’f^ןËgâs¶–Êç´ÍÑA˜UѬè3ÏI…ÃuA ‡¤Dõé&Ï\R@Bk‹@A.åøcã•Fƒû‰ô“¨Y(éÛEŸÒNÅ&•U(ª5U†2;%PŠ,¢ð¬yAž–&”/&Ž»À—É·0^µýŽ‚zs„†/oy†ˆBÃ!×íRL"M_¤¹p|ÎîY¨8$(®Lèˆ4ßOBP¢ª|IËE È]åk;‚03IŸ¥(…4)s¸Õ<&%Ò„ø2 ³)€(Q·©³Wä9œ“‰¾. ŠRÖ—vÃúB–fÌ[mÌÖ¹d¤ëo(bîy³Á‡ugcC«ÊQ%P•D š(RT2B…ù¨Ô£\¤¯GX}¤ƒ³Íd’¿¬Žrñ-,6œ%VI*ΉÜ%ºRëS.E_ËE¾ÌÙi|œð¹“¦<))ÓøÎꄌW— RÌùrÅÔ·œŠãzâÈ, *G%z1Ää,} :¤uªJ0á}+Ä€ ÉÓb5á)jJ ÌA¦=û›¶Ýyl1‡ cþ“ùdn›n}8ñ„û+·ßyl‰­·Kd:÷×L‰¼ ó Ä_|Š|„G~³ï¬½ªú7 ¾§§r" åóÅô /ù¼{<Š œèI+Á·s$ͽgÞÉež>ù̈>}˳H¾•wïxBûe}|™I?aÀÇ Ÿ%Nx·×‹mS´2Dò¹‘…Œi¾ä»ãÊáyüáòÆÉ–ýfüÑ×Qcœ¶pëëKfJfo÷_‚Ø3/|=ïé›ç;ùAÒZ|Ûˆz%gõóC‚4Ë6¶GüH*÷y„Ma}~fH-õÎ}gÄ© %P•ÆàK!z–PÎ̪i  E}ù 0ÓF]÷E…&…BÑøiþ·W§|¾eˆÓ!þYi*Šú ´CqrXᜎÆ‚D¹ˆ¤|#¨@ÛM¡Peó~/Dטg'„øå¢P(*æhy`´B¡(«ù¯I‘”%BûÓ-õ»añ_B¬Ï4 qoˆŠy-V_%P …¢¡(„ü’5[KJ1¡P(fƒE!þC‚ðx%DÇ#Bü$áÞ¯„èUÃ:)R(µàqPÆiš¼Vä ÃtG ª·Ý|É N E%PÕ•ƒ½œ@Œþßsã`nʨ>J  …B¡PUÙÊ©äšj³]±É9fH†õP¥P( …(›_²G–>PcCœ*‰êâÝ”ÄéCÉú8K Hȇ¿B¡P(І‰æ-ZÔ¿(¼„ò7G¤'°¨Q¶ñ4J¡P( E£Aý'PáCCüQŠ}ž#e”@) …B¡h´Šƒ…CÜâïŠøàba9ÔœÅëÍq …B¡P(&š5o‘äuuˆ~Y¨ñ!ÎfHZ†øv‰¬‘\ROqž:‘+ …B¡h,i v…øÿœÅïñv‘ú7!æ(R( …BÑÐiþa)ú¥ë,þ·)Ä_ÅÜÿ/hÆB´R¥P( …¢Á¨„³ðþOˆC´ð<×#ħ ÄKÏÂS( …BQSõ­ó³ôj^Ó¸õ¯BLMñü¶ÿC ”B¡P(Šr¨ŠŒÂó(k~k]D}C|M ”B¡P(ŠÆH þ]ˆy5(kOˆ¿U¥P( …¢±¨çB´ÏÀ§jpˆï)R( …BÑ T“´žíE’²ƒ!†+R( …BQC'ò¯¤M‘”–dt"™fF 4]´©íøHê2÷z,yÙÔZû†¡#'ûîz2“²†Œì;z>õý;ö=Œ›2¿Ö¾µßÀÁÇ_T\ä›÷•õÏ¿ÿÇÁU× &L]ÿœû$˜»dC0¾Çd)6jbpÓÖLv]±#X²z‹Ž7%PJ ê+F›ÌZ°¶ÞÖêìåÁ¸ÉóbïéÔ¹[°yç‘ÔenÙuOоcçZû†V­Û×ï} “²Z¶jmHQÚû7n=t»´o­|çËŸüÖÌß›ï<«¹ÂÁ¦cÞÒMe ÃGOÚµïLŸ»*$NwøBдi³`à1ÁºköVtû´iÛ>¸öÆ£5*cʬeµºyQ(R¥J ”(E='Pw>øŠ ·ÝûLî·1fý¯í£J TDÎÉYú@ ±O Tåáðï=ô4Ò(þ>râµàøGO½ùܱÇÞî?ûùûЃ/½ô-ó7§U£ÒÞ°õ¶`ÿÝ‚'_ÿAìûO?÷UcVK{ÿ©g¾’»ÿ@(tÏ¿ñG%¨G_ü¦YÀ)S×¹×¾Ÿš@½üÉçÁ=¼l½éÞàêw‡zÕ˜!âÞM{m¿åxpí®»ƒGžÿZjõÒÇ¿1¦®Ÿü¤du&|}IÚÏÓÁùטš@½ôÑoÌ8 -®¹áˆÑ¼ðá¯bÈÑçÁ½gÞ ¶í9fÚçÑ¿‘š@½øÑ¯ƒÛï!8qáK±íøÐù/O½ùãÜ7Ñ>Ͻ÷‹Ü=ŒÁœ2×n=ú”ièò(kçþ“Á¦ëšrž~û'Uï9Ë{>Íõó‚±ùì»?3‹ý†-ûÍsŒ£¨²i§»N¾޹»‚­»ï5eÒž´‘-O>ÃØØ}ûSwÚâé·?ËÕÇ7¨õÞ´ív3v˜·>ï»ðÖgÁÙ—¿ÖûDðÜû¿(š@Q_Æs†¶?ãÇO½ù#ó®¨1¼9qáËæoî[zù¶ I“&ÆtÇ¿Á%]z“g.5#[dh©øÖ+·ßn¾ý™w~Zp² 3à ü28ôÀKá{ßÊ÷=únõü=ld`Üüµcúº=÷™ûžz#wZ…ŒØvóý¦}ìoÌ+¾qبIÁ ac«¾7¬«ýFú’9Á³Œ#9˜c˜>!¡qýéÊ+ämA»HÙmûŽwÞ}ú-ó­;ö=hd´;–¨óyv&B–Ñ^Œù'^ýžFáUh"M%P WßÁ¡è4mÖ,hÖ¬¹ù»Oÿ¡fðó›X°È´nÓ.'8Úµïh&çª7š~éÚ½W0dÄs¤Á!Ë`Â[áÙ¢e+³ãäYžÙ~ËÞ…báŠkÌýXü…¨k«ÖmŒ0/†@­»æó®Î]/5õDB8 ×8…ðÇ¿¨I“¦¦í 4oÞ"èѳŸ!žòˆ)õ¾´÷CTù›gÝ]wZ³i·ùF»˜C (kùºíÕ}ÒÛÔ›wÐÆÆ$…ï?x”1§0&¨;c‚º# å;O>ýeãRÕ6ƒ‚¾†›oåÿîBE ðakÛ®CžÀ.XÐÇÏ4Šž}AÖ»ßS>Un}ؼ¿}‡KÌ÷pmаËrDÛ21sþå¹qP5žÚ›÷³X]6qv0{ÑÕcïçæ>Ú¨{Xf·}Ì¢ÇØ on¼í¤wïÓXAÛÑ,X”q&|a¨{ÇN]L}¨ cœqÅØ–bÈu§,Ú~åïI3–ç¾p‘œÙ÷Aö´›%fi ›äßLwèØÅÔßC»Û ïòÉ 0xø83ï«ÈG3÷#ümÁ¿[¶lmþfÜÚg!þhk(Ÿ¶a ð7òàà±çòÞƒï÷Ž;ÍÜù¯Ú8}#÷ò€¾äoÊòi6lÇôÅùÎ\†|'(Èã€yÇœàYÛïU²§ƒiWÆó-vCˆOØØIsMý ìüflò}нzŒóI²lýymÚ³÷À`àÐ1¦ÿ© äŸwZùÌ¥,æ!ÿç©7ÏBê!ÐÈiþÍÿ©3ýÄFÂ}çˆ1SÍ;ìø/F®J Tƒ0áA’¶ì4ä½ &–Ý­C ävvîΡÐB‹¼-²ÏT.ð.Ýz,Äì¨ ‚ ¢žvXn=!€æ{Ã|B€ ´®æÅˆïAB:ÜÝx§ÎÝÍb‹ Ë-ábdñuï— ›^¥˜ðøV„Ú W£eú$쨣‚šÅaì¤9á.ò»y‹ØÄé‹ÌÂç~÷àÇ‚ëjXÈg-\g<V‚ó­h!cô@ñì¢U›s$6„˜XREYh@xäƒ>ÃÚíoEO—ôò;㔺²IÅ"J`«a¢´…;N?û‡¦ý1˸mÇx`œ@¶%š³x½©'3w|3ø¾tç'‹%íÅwYÍ$ì–ÃO2…ÿßã(êÄBœ¤å•ж€ÔQ¶{¾êxI×¼zC ÚÛ½‡2ÑÚIM(ä2J=,i£ïiÚÂÕ˜C è/ä†ýM›;È)„Ëj&ÑÈ ñ¢]òÎø¢í!\œ+®6óG ¨+÷Ù6§ŸÙ$™ð PÔ¹lÇ(ZÖ½ú› c‡6áwd®•??õûyòŠ~c¬»ßÅxfÜ2¦˜/’@1/¬œeã ù¤lÆí¤é‹stÚgî’æû¬ í¤‰9À¿ÙT³R¥ªÑù@19˜°®V†¿! L@û âå_ƒð¶“ áE›afñÝ?{Ñ:£AÀLÿÙáHrâb×ÁÓ¦¼4 ’‚‰ÇwBÁä°“_(êÉ@ùÔäìôX òî…BÈgÆZ¹~§!VºŠ:Ò®I‘…q Bã.à.0 ølŸI…ƒ…ØgŠ¡¾ì@!3ö·É3—íj#ܶY²f«Yèø[(Lv|ûêM»’ôðŒ©AA{@™–ô¸`\Ó–¹ À®»M?£Að½ƒEÝ-Ë(úÊg¦ƒØ»¬DnB`l¿8M™’G  ó©E±ÀäfÞ_=?iC´˜}ܹiqߣï™Å—MK  ¾¥ø@Q7Ì6¾ë,înÝËE  ý¼‡oóµ)š…Ú¶Gj’'“ƒ’ìº@Æ?w“‡¬‹2͘¿Æ|kZµa§Ùĸ›'L‹ie[YdÉcßݸ¸š.6nÔðŒy«#å˜6g¥y‡$P—_µ§à^Êåš4É1wÝ2 [6ÛÌSL‘–Ì7¨9!ºeI f…ø¤ êóß̓•@(„߆Ýþ†Ádý,Z¶v[¬/ƒÎªrѾ ì H˜R$N>ófA=PmâBÀ°@ÇÕA—†@ñî8çfkŽô(v£Öà¾T´—%~@¹¤q"‡„°³( K%Pï¨ë,îô‰%±.bgnM€QÏó»oêjîoÙژϊu"‡œ@ÔX¨ã|•\Ž&ápw^e¦:\p ž»¨£UcqŒl›°ô…$Pø|D9Ûñɽ©­qÁr  ‹_Üw3öìü´‹¤Å7‹;Ú—@EmX’„/)”ßΫr(´¾h‰¢¾…\²Ú· Æ7r:ª 3¦›ViÆ(ب>·ó$…œäÈŒK†Ò(ÌÒîohߨ„DÕŸ1Ç»\Ë@œüAsé#P>’J»EÔ°°ëZ),´±~›…WûQxYa¬(>0îbI‘*h&ª´ƒûÊahnš~±æ4vÛ¾]Q¾ÀY‘Š@Å D›‹Éš{\Å΋:áØé HîÁdÀΗ¿}N°IŠ÷CH¢4zi TR~H,~7’@±Ã¥î®ã­ÏLJ{ØMÛÝ5ߦ!P˜Ã[h/Óš• PhF}× t>ÒG¸»yÌ8믽5!Ö„åšÜò—mÆÇÉ:÷r¯kJ‘@+è–‡¶ÄÕ‡P;?é×4óK \³u1*)Gã•9]N…ŒIóͶMYÀ!‘®eÚõM÷#Õä(.Æúq%9‘S'ë1rÉã”»id#¹KSæ­cÖ)=j£€FK(_ òžŸ@OÊ›“Œ6”‰¯‰S¥ª^(&Õ3vtã§"„ p\ùsëi$QíÆÁN<„3¾I Lµöê›cïÁiÕÞã(Â&n`,àëÃ.–½˜Ä(Tå'Úœ›š¨¤ç1ña‘Š,ßáúãH ˜«Túß57³x%ø/IÅ·òÍ´S±Í’@áHëš ýã W’Nÿn"D´.Yqµ¶‘„gßÏ[ì! ,8qõañ²ó“HÈ*ŸŸ7bç5y×”@%i™wh‡Üo‹Ò¶ð¥(Ú— X’̰Á’@Y>Ieàãhc×çÍØ‚¶1M&,\ WRÛ˜†@Ùv!&©þ˜Í¬ü‰“WvS˜5²@~à¬N½}¦}%PJ Ÿv²qž_´ò¢f„6 4]:;SWž‹r†·Šºâ%‰-mŒc+íf¯IÅnçslÆG!â&%Ý{äIó>ߢNI«Ù³ vÑ ´ÉøŒÅ¥0ÈŠ@až±m]8~dÆkM‘¡˜[|m‰±Ø(ü· t,2„" Árç'm Ñð‚ØpØ…0Ž@QNq…@ñ,ÎÕÒßFh"M­ßŸ%DkɲXÜ«deñŠqAÐƒÕ˜Ê WUjއò¢ð$YbÒ/¾®h ñÝq‚P_Ÿ{ZfdCBãäj;Ü÷¬é׌—–@AF  ¾úØqåj­¼òù&bv·r;KE`ƒÕFZ$2Ð×Ì9Æ(ænÂKG2†øAÔ?…ø‡ p™¨hew,Ê8+ú‹Bÿ(&/ÒBt–L¨ˆžk8KE"=|-(ƒI(‰;ZÞƒc%Ú0³è÷èc„&Á4 íZ4Þ¦ž˜ Õæwj•Šh+ˆ;SÓÞ#猶 ß.ë ©DhO˜¶Ð,î/¾“Eƒ¨(—@Ê4ž!6®P£¾³¸Èú “퉀ã[XHq«ÊóØ€T°A`™„ÜÓnz†(ià^E  Cø2æl2V6RhsÙØHs%×yí¡¦nlÂø66b¥(@2–ð«AkÇø¡‘ 8iË

    m“‰À ‰Ä"ÂF¹L;º„Þ“c2Cj¾…ÜjŒú')eRgþÏó|¿Ôj¥%PV[É¢ï™ch«ùN¤o ªš“MÌýÌ ä,ã…úCŠ|y jB è·*-ãufÜ£cLæ÷ãYÓ&¾µD ”¦1¨—@(ø4H£Ó:Ešt`¤Àg‰{0u!Xºhd¢L%ì¦(&ÏAº¢Â†±Ù³ˆàŽÀÀÑ—4‹ Ä .ê̆ڣ¦'RļwèSZ'ÔÿÒDˆ°ñ…~“ƒáàÛ!Ÿ¶(&‹#-„ ‰VÊÍž]6·Àd‡rhµ7h(X`Ht÷­,2h@ènÚÌö m%5øjùÌìÈivÒhˆ ‰q~v QLY”‰f@¦C ªJ†Æ££ Ü\\…ãôzC@¢{ŸÉ ï“άDQ±£c°0^!®$ˆ´õ ­x>Ê©ž ƒ/}u¡ï eôCØ™”' óÒjû ­-„…ú°ðú)è7ÚÌæŠ¢íeôã÷IM&ä<Τeå¾t´dbI[±‘ØE9‹£Õbád¼¥EÈ<íÏüuÇ.óQÌÜbcâ+í!‡ ÛÄ¡h™{R“q°>—>-„ ³3‚~‹šDsÅœ…@ Ù¨YM+Cªâ"i7že³‡|s“œZ’&És4*p†~$ l•U jNSŸ(ùƒ¹ÙW%¯:™~·É_!n–ÈÂ8ñ%YeK’šÛh…B;'«rÿ6~^lJéIБ÷q&n%PJ X¤ÝÞG ôœ¤òƒ]'š4I¾õø81‡¢H¡ðh1ãš‹‹0ZS´ËÚŠÚh¿âÜ”@)jP@ë Õ°J Šì€æ?'™  Q¨hqJÍ ¦P(jÕ‰üÅ“³$P}B¬WUÿ€O=ìS ”BQ Qd£Â\ß Ó"æüƒð#Ir®W(…Wþ×5Ä%J ŠÇ<2.K´{¢·B¡(øœá”Žï l\¤¿šB¡PU6þ×6ÄÃ!~â¯ûÿ1ÄOC<¢¯(…B¡P( Š@…ÿµ¤‚¦!Pá‹Cü»¹Ÿþwˆû”@) …B¡Èˆ@ 12K¨ž!VILæDŸ…¸¦½Å3£«µLÅ$Ñ|J Te‚„¾ƒ-£@þ ô:}U©å”‹@óýY´UÚþÌò]hŸøn—¼V¶¤"ÆGÒý¥¨¸÷&¨,çk%(²ÑcBw£›•@5¨A!ÚeI pìþi¤ä’¿óœ+#îo_‚ï“ÄY%P•I ðÙá˜/G0pÅ#BF(ÎDHs”ÏsX,G·DùB `9£WЇã:ä±¾-¿S~Ôwqì‚=ûjëM÷š#H8–‚ ã ážcfÁ œÅ¶éºƒæL1{¬ elíæ9Ž áø ÷y¡rœíC;r| çh¹þ?©A8V‡ã-Èíe$Ú„gePËãÈ”Î$hºhoŽy 4ß]ØìB‹ýʹ|œOFþ#ŽºÇ¾Øïä3Ž­ Oì™uÒGŽÃ«©¿sF!ÇgD7E ðO᎜ ^™ÁïÒïësŸ˜w¡Ùó•ÇaÙ–pÐ?´ßmÛP>ßÂß2Y-åC®¸N’MæÈ ™R„6$å™~£ í52?3¦8V‡1ÆQ,œÉ™’ä—ÌÑ0˜9&ÄST,â(êÎx´Çä0N%Áñ(¾ã@èkÆ4g269’$Š41?ù6êŒ| ïåQAQjçþæ]§žùŠ·|d=/.- Cö»8J‰yË‘/?Åyqvìp¬ eó·Ÿvl¸¾aøFq"9÷8Ž…¿}¹ùè;ΚÃ<ÏY…îáÈŠF…þ7>‚ä ¸C 1úO!–‡èbRµºï¾ï(ª<…Gø²È"xže¡ç€Ü8Å™O xç9ñ<‹BA+9®:è²)Ÿóê8Ęsèx?»ºçXù‡€²àÉó®\pè+ g‰! 9ÅÏžGÇá¥üæ>iàŒ4ŽßàÜ5mv±|‡wB&8êƒóÛ8§Œ£?X„ìó,^œGFÙW`¦9•ß)Ÿß9`‚G¹ö€dÎiãu§YTø7 ®ÙQ‡¤‡ö ~œWG¿PíC9î¹^v¡…B, ”ô g‚YÂ*Þ!eæãé‹MqF ßéæÊy‰”mÏ“c^FÅè#P,@œ¯Æ¹ŒÀÌ÷A$!BwAò™a\0n­Úlþ¦Žl L¶ýeÒwüMÙg9ø•{i#_úBÂXfqu% !ŒOHóÅf3g ѯœùùd.pF"CH$#ÿ÷3¾x„óŒ³×¨kŹnÔÉÀœµVu¨n³YpûC(6 |}ǼÃq¢oÇ®KmRÆã†wð. f|26Üs}Š9@›F 8SÍ”ü½êLÍ뼚9Èxæoú„óéOÆäÉ;œ‰HÿSg~cŽñoKØ9_”«l¢ ¿†,†ò‰ßhc÷½ÈÆ>ï¢<ÌÆº–4>åKYð÷Q~J˜öbÔRqoÇÿÍsß_+ª<EÛ"då"È®“…Ç[»€I…†Ê VY6'޳8Arš§¾n ’•;eØdaeÁ·I z,òQ‡ÎºŠzrà¦O;ñ@8º§Üó,Jûþ3J²ÅË2 ,”îù…_•üȳ·s¤J–&2XÀ“Lx´1}¶O–ÃΛv±°Ú…–…]š+칋–˜™…=ìˆÙégÿ° lTʶïµ ÍS±Ñ˜ô§%‡òØê áe²§ÕC „ŒñåÞÙ–¤„ñ‹öi1!ü4d. ¡û ÆOs‚f£ªÏïÊë#î·ËæidÃyaÒ(Ƥ/¢ùÈáµ(eÚø’®ÞQ&ut5Qhwè_t Ưý&I ˆÊC+ˆÆ6ÎôGÿÒ^RF}l0@aû6ÉmÎ PÜGBâb} ø~ó’ß×ÜÃŒi ¾Yn ‹@môœÿ7æþAž~qÿ9Ͻ¿‹rPWUwŠ]/Úßu9´H)B¸²3é#uTüÆ'¨ZU¾håfCª¢î‡D±0a&’Š]$ 9‹dÒwYe’h% k.b±qwÞJw1’m÷£ÑŠ#P˜wâ¾™…šÛj+| ­寙9 rV3bZL'¾{»‡‹šÕXÙ²!‹Q¾$hи*ª]’ P”37s«Y(bLAX0%úÊ…ÐÒ÷KB ¯.éÃÈ0Qþ4 W\c´ŸöߘO¥6ÃÅ´ÊÕ6JÐçæ’$PSˆˆ¨çÑÂ0f­(é~d‚ß.‚0¡…Ãlvá­ÏRŒ¹ÁÜ%òÍâ—M5æI´ÀîïhŒÜŠþtIZZÅØ¶ßë•!ùCë(÷ÝŠÆI v{Îÿãÿô/êtÄ3×E܉¨Ê"P,jq÷Ì_¶ÉUÂg„…"êYH“s‰uè…tëtŒ Ÿ…pƼթžƒ@A ãv½H“%.üíž7ˆ–‹oÅÜÂØóvDÅ(.怨2X4xÕrùÚ³0¥Ì+eZé§å¶+&ëÃýÔ#ªŽÔß."–@•âûÁ{sq÷@H­‰±\ 2ƒv.ê{Ó®&2qú¢<í,&0HTT³®3u'dRãj2|D• BÕ¯6‘{¬’K ÐÐp-Îg smæEX`1QxhhÑè"3Ò<{Ëá'LßYR[EÞšsdÖnªhKÚ¨K hïb£ð vü›ÍTT?bÖåýV+Î|ÆEA×:%PœýÛ&K'ò!Þ-‚”ñ›?¸wnŒùnqÄ3Ë#î¿T Te(Wƒâ×\oÂù7Ιà eMTh>ð»(fÁEó_‡T¥'(LLi"¶ìN…—4u±eñŒmh‰š$P–@bšŒ+Ø]¼@]¬ëOŠ"PQáîø‘Ù]7γv‰«Ÿ]„-ò9â§éOyÜ=hðÑqI¬›ÓíZ bL⃕Ô'ÖÄ+Í`øŒÑÏÔ3© üqðµ‘fSÐX¥!P>“òÅ’?Ë37ºuÇÄ 1ŒóYÃ/s.cÍßé :ˆ#PvL"Mµ‘2|NôøW™ NHœ0ÿQ®Ý{åæ&6ÚÉý”WiD£éJêGëër}0AøßíróO>¨ð¿»#ÈZ©E¨öJ *‹@a¢‹»áb5?’@±;ƒ`Äe&æ~ë#ÁýÒ9iÁe‹z~ÞÒMfçéóŠ2áÙ]«9—dI…àMJIÀnØõòi ÐtÄ™\X`Ø [æ#PVÐcÞÌš@¡Õâ^ú4ßYS…Ù+î"ó QnDù¼ÑX Â\dߥ9­ê“Ï##ÙØTÄ9FÛ~uÉ‘ëp-QZ^œI~áñæÖ3oÒ˜FÎB|òîO™ÉÜ(æ,æzü±˜·iLJÕêZR„o^ÕïÛrã³;‘§îs¥¨*Mb‡Ø”!5wã¢J ÷ïŠ 8Ó=÷~7âÞ_Ç”¿%ª‰¨Ês"ò­Á¼ÃŽÕªË%‚p@—÷=v ÒcMDûT9‹¾é½ŸëhBžxõ»¹—Étï™wŒÐcáç"޹Ê]\¥ƒ{‡Ž]Œ`Ž"Psùž|æLȳëgä#P,²,ô¾H"Y»ûMr"gQÔæó¹Ü`°š¾b ªÈšô$ ¬,28Û&(êS’¹-+*‘avc1¾êúC÷2æd§%P¤ à›|ùŠx'¤ƒ¶¶æ]‚wéÖ3ç'%]Âé]í.&Rã 1viÿ´Š9å¿…¶Ÿ;KÈݺŸÆðY××OF½1¦­?ßOYDúî‡ ²¹²Îò–@Q>ïG~`–L›‘¼ÁDÉ<²>}˜Rqœç÷*²ÿq&ªÊî Cê}Z9ú-»%rJ ”@ÙûWG¢×Ä}“cÌwOÆ”§çþÿR¢¿Ö«<¿dÍV\e P,081³H¹êv"PˆÖïhŒ|i p¨¤ 7‡i¥†*>//ÍôÅfgº÷ȹ<çaÂÀ!W„‚» .[®“9>GŽ8Ó B„°ÃÙÙ]ÔÒL¹ùäsÆÜ%s %ÂáŒ`µŽÕqж@«âl.WãÀ·ažpá(õÐù/Á‚ÆÏL«b±&âЖS,BÓ@Ùhe¬Ö„Å“…‹vb‘‘i $¢opø%B‚t@r ;Ö±žÅ–ç0áH“2óžñ‰vµƒ´)ã¶Åû·Œ%—Ìcj£<êá.¼>…É ¿0Ìxn$&Äšö¥Ý\ÍíC_c:tÇ¿³ø£ e ¦!PÜ‹ùÒÕÆbS ¸m%ëNŸ2ÏÓÖ|mÇc³™›¿ íóYánøNˆ7élP€%Pn¾'L‚Ô‰t%idýJÛÑVîœ`Âï\—ÏÔ„@1gÐpC]Cä>nô¯+k”@Õ Ér5g˜Á Žs.o–jhˆ}E’KcˆG¼\A ñw1÷Í)ÿ=Ïýß+‘@µ¡Ï:¸Ê“Hß vä„"»P„,Z?W˜ú$ÇpvŽ,<a´,¦6A Ì9´rýN#T‰l²9UÐT¹áüQy Î<ÑÂÏ…{ºì‚Y¸XL­¿›® Š@ÙoæYrͰ˜›]vHe¿@ÙEÒ:C _Uyš˜Gâ2‘£uaÁ¢­)ƒ¼YÜ‹9ÉMzZ,²ùzø¾Üw†}IŒ—hF(úŽ…]æ>òõ'&]ÈmÎxaìÑn$¬”šˆZÆ#SôãÍ^©NäÖ,iÇ-c¥w¿!æ»hSi*ÊæMÝ |ö9Ìá¶?|ÎÓøÑ@€¹‡zÙ<[Œú´‰4!*V{Ì{©;íÃûez_Ýé;üy7u¥ß( Réó7ƒÙwPW;؈¸%*‘&}D§1¿ÛÔ$êÌó[qµùÝ ôÈ‚@YBη3hCÈ÷±1‘é ”@•Ö‡¯—Ud^u%þ¯dÿËÍbÊþ÷žg^(±ž‡xžI¢,[ ÙyZbƒð…| òå9a!eqñeç~žcÀ•ÁZ„nó>„/Ìà;6†|EÔ#ÊÖ(k¶ k6‹+Ô~o¹2üvIª\-DáÍŽ:*²ˆçã|ƒ 98òR?¢Ø"Ó?„ß)eë9%+¹/Bþ¡Œ(3ýî#W”M[±ˆQ¾Ï/ Í e»Ú úövóùàö§­#m“|T¤Û?Ô‹ˆN»£Éqµ”!SWà?W6Z'"ÀHŠJà€oL1fâÎ6d'EíW :\õRL¹c"žÙUb=·ØÝº0EÚ(!\¡P( Ee‚kç?( …B¡( ˆ¼ûIˆOC<âî“•@U>ijâšjûèßk©B¡P(ÿâ\–é ”@eGžV…øÛaT8nÊü`Éš­Á¦ë;÷Ÿ nºãQ…B¡P(eÂu{î .¿jO0oé¦`ÀÑ’Hýsˆ—Bô©Ázÿ­ó³$PBŒl„Ä©Kˆ?´Ó´Y³`æü˃}GÏϽÿ =ƒH¡P(Š:Äc/}+غûÞ ÿàQ.‘ÂJ´WÓÔyâߨ?e~pâ—uÀ* …BQaxù“ß»o?t»´¯K¤ð“j®ªvÉÓ¬C´iÛ>¸õèS:@ …B¡¨p¼ðᯂùË6¹$êÛ!Ú(ªòDªø¿¤á{ôê<üÔïë T( …¢aó낦M›Zõvàêý²ôâöF@žÚ‡ø5 Þ¹ë¥Á¯~W¢B¡P(õ;ö=èj¢îÒ(¼ò(r:-Z´ î{ô=€Š<<ýögÁá‡_ËÃÓoÿDÛF¡H5~â™?ŸÕY}:ÿżºÜöƒ—y꙯ä•yÏ#okß×1–¯Ûn Ô¿„˜£ª<äiBˆßÑÐ7ÜúpƒH8Ø‘rAâÂ[ŸéDK»N|¡ ÿÈ=¼¥mÓðÌ;?-˜7{=–b‘þ4wÿñÇ?Œ¼ïsWn¿=˜2kY0ò²©ÁÀ¡c‚1f³®3»èÇ_ùN&ßq÷é·¼2Àâªë™ïºïÑwƒ§ÞüQ¦mÈ»åü¹ëäëuÖ§“g.Í« m^Ó2¬¸:¯Ì®Ý{ëü©sçòσQã¦Û>ù‘¨ò¨¯ÒÀCFŒo°Ú“-[y“mÛsL'š(EB¨´ì{NÒs®3ëƒO~Rpýø£ÇÏLLجYsSÖ“¯ÿ Fß±þÚ[S''lÒ¤i0kÁÚàôs_m´ŠMçàáãò—ÂF Te‚¹Çx®î—+¸Àâ=²$PÓB¼Þ€ÉÓ;àžz£A í·<)(*iT¥¨KÅηã%]ͽÝ/í[¸8|$rSd+róßÝqüÅFI ÐÈÉ:?ûîÏ•@ÕC°¨î—_k^¶êtC'ÃFMŒ’'ŸÖWJ Y(—0à‡á^ƒ4mÚÌ;Éc3rì´ }ÇÎÞ븜{íûµF @ÇN]jlF¤Îh»]Ôe }àÖeï‘'kL  „n™d?•ÌäN?W•¢ÁŒ@C8§ŸýÃD¹fÓî¢sm P1 ’À¬¾óüT#;ù¯~/^8¦ P<ÿüûœ‰Ï ‹6íwßKý¦Æf‰šø¾Pßó¯ÿ°"üÎ}á¦./}ü›:!P+ÖßàÕ´0F $²<2)ŸyáëyeÜ~ÿ A»ö î4cIfjçþfÌ‚Î}lز?hßá’‚ûÖ^}sQïâ;i·,æƒ Æ;ý×÷Y9§K ê´M©ãýÅ~míSoþ¸bdzÖ@#X݇”@eCžFÚ‰ÑPs>qn<ÇÏQgtéÖ3A(ý68pï3ÁØIs :;é®Ý{¡ŽðeÚE­žë?¥²‡à<ëÞ{ãm's×­Üœwméå×™ß1¹Nž¹$g*¡nd?óü×Rí@'L]ôê;(hÞ¢eî{Z·i\6q¶qÞMC ^øà—Áš+o2&Ì´GÏ>ƒ‚-»î‰DÃÇLÉûžë÷>`~çH‚n=úäÊß¼óˆ·ÞœÃxiïæ}Üתu› ßÀÁìEë‚Gb¾}õ¦]yï½è ó;HÓæ¬4Ú ›@‚pâ—Ûñ–ÃO˜öBsbµ*ò>È’¼yåÞÿóûî°ùsAùûòÇ~‡ìG|ÀÜ29ë¬Àç(lúlêìå9âÙ²ekÓ×VÖ€ý÷³¹lká’Õ[ì7~/†Êh©¨têJ;ÙfÂoódÀBIˆ­ ‡|9²BZu¿m4lÔ¤¼kSg¯ð–ÏN[.î"‰IC.^œk}u€E-0• Ó&~$(‰@!„QeX‚"!}_Ö]s‹Äòy—@!ĈÒJª7eS–o'*Ÿ‡äÜ|çã†TûÊb¡‚Àø¾’J?$Õ2ER;_RÓ±xõµ÷ÜwY&!èiMbv¡/'b3fïc!s¯±ʲ,Ž‚¯ß‹ÕC 8¶JÞ'ý¸.éÒ#ï:‹Óµ7-xΨ4>PôM\ ùví;yû²WßÁ† ¡E“$Ó"ë#§q>PòZ85ÚÐæÍ[äðÜ|Dèýõf[w>³á„Ø$ùÐAª|›°("…;|¥A¬‡.Ê’ÿ^›QxK“¯ê1ÚOƒâ#Ô ”(±è³#f§îþŽVÊW„È7©ØÕ³»Ž#P,Îî5ˆÏäÄ"ãÞ‡6ʽ.  {Ò¤ww9Ó¿ úžJh¤!PIõ€Ê]¶@9ªe/‚Ë6H»á¤…˜z¸;O†žìU÷§mG W£˜%b,ûLN,,cÊò…¤/Y³5‘@¹»å¹K6šzb.ÝzÓ½ÕQ”ǽѓGN¼<úâ7MîétÌ;N>ó‰š »ì=rÎôu›¶…B?:·v¿òÌq˜4iÃû ÇSÛá,Ÿ³ PB(í#¤ÕšÍ_Ôó(¤#jGž%bŽØ9å’#-úœÂ“Þïj´\gó¬ D>D³#ï[zù¶X•«W>Fîà†¦2Ye}¾ØŒ17$uïaÎ_ve¦‡ï)†@YsÜä{˜#®£¸K.’Ú4¹Á’î…÷4Í3Å1Þå÷7Ãl–é?ä¯Ô°3ÏÜ+„*¿}™1ž“éïÿÂhðqÉè?hdƒ!PøÉ:ß=T TÍ ÔK4æ¢U›y‚ØÈEŒÄ|}KÞð8”j ð‘¤%Élµ·þyjÕûž$¯CA®’& ŸJúØÙ÷óraIm„oQwMfIJ &œ¨¥¹AúŽD(´pQNãÒÍŸtfõÕOö•@ɱ9DÞㆱcJd•>…,»åÜ|çÙ‚r0UfM ¨›\,ãiKq-†@}ùÛ9 °{íÞ3ï”3tä„Ù?ó˜F;dB h/´K SçîþTM›”D _Q¾\¥(ˆ¹Ûg>Y1výÌ$9b¼VB¾S¨{}·éåûÝëlv¤VŸM”¯R+ç¾gÓ¶ÛS¹VÔdÎT*>G TÍ ÔkU»«ëB‹#'?ÚwRHíÂI–©òi]XðÙAÄ¢5­í_4'¹ü!©K"hRhRžrC¹|+ä1_jØ) ­a”éäØcï{’“##ì5·ÔV¹Èû~tD>Ö$”@í¿û‚0÷o,¸²îÞ³që‚{dÝê*TÁ†Òq`ãˆÕ½ŽfØ}q©‰Þwô|$QsçmZ˜c¬—qH¿æ†#u~ÌN9á -ˆà¯“û2KÕƒìœJ ê¤9‰Es†{ϲµÛ TÊ2r3˜$Z¾¬Åì™x¾º`sïŸ1ouÎ'IšŒ|çJ%Ík6’LÖ‹¨ž‹ÉDå8Ÿ†@a2÷¹§qó(„yÔ{qâN›=~âôE}G 0_úœìåûˆ(´×èòú­GŸòÖG:LË~“ʧ NãD>nò¼dŸ¶°0ÕJMYÖÊ:Ö³Ó•QV˜k|9–’ÞIÒgî-7‚$]»ëno„®$P¾ÍWJš}äHFVú6}®‰ª. îÒ ie¦x96\™Í¦Ä.l®Ü6J4Îÿ2wÓÝH”¦1hìªÊŒÐ´Àׂ† HOšÝ&;g_ô®Ö'j`CÌäN¨OÿaÞï‘ rX9ô¥X%w~ÒÄW òE¨IGÑ4 Ÿ§¨÷úv‹ÒGÌbÆü5Y¤]s–$Pä*HxîÔãT1DÔ9ƒªš°u%P>í#}˜D 0£NŸ»*Ñ™8JË•b×n#CùvŸó½/â1)÷–¯ ÆyLÃ55á¡=Å„„ïŒsn— ÍP9”Ô„¸¾œvIhñÁ«TåÛèÁèÛ|­\¿3ï9Î--…Oó,í©J T£!P¾]ZZÄíl9]/‹;ß‚åÜJ4—Œ0“ ~”ƒ¯/¼‡é8…Z»Põÿxæ™Èi›b ”ô JJ„ŠÓ|‚shRƒGËå3•JS•…tF–‘cÒñÝ—öÁ¯qøbd[‘¿ GâAÃÆFîÖÓä +…@ás6Þu·_3,L¼Q›–ü“&ÕÈü\l^øÒ”ƒ@IÓ¶@ÉÈÞJ'P„VS7>—2- AòY駆lŒ; Ú¹í« ›HÆ*¾UDÖú¢óˆîS¥ªÑ(ìê¥( Fã|3dÎH•Ï‘s—´ý»ö~œ!£²XgA |D¤˜]|](Ì'Rk¥ýþÒ‰9 åóÇqƒ\S 4U5ÎgË÷]ø¥%P¾´2• p¡ ?{]:öÆù&B$£ Êycç Zf%På%P¾¼Vq‰aÓ(´ÂÒ-Y—6Bšˆã´ñ5qÇ·Ôe[Ó£|êº=R| ú…¸Z Tý€/Ò_Käýìà]Á‡OA”£¸\  PÒÁÛ¦4 ãyi‹óÊ‚@ADÜ É.y‘¾a-y¼M](@ˆ¶|7þ®@ö9×bnËš@WÝëìœ ¿¶×1ý‡«À!ØÉ‰äó]B¸YòÎ7¹Ç¡D(ê‹6&ŠPð»lo阜‚¬Ù¨×CFG¿~—/M$ -©5YQo4¾‘IdE T6ʧµ&Im”ɵ˜Ã„}ÙÞóú%â=_¹rNŽ5¾’åú2áRÁó˜½Ê|9±ý*Ò(¼FN 8¾@~ß1q‘¨l­ÿ m” ùEh+LHìÎX´¥FšÒšãœ³ PÖ—Ë—GÆ[ù!ï ÷¤I¤Y[ŠÅ†B'Ïñæ}=zõ/¨f¹ëÌŠ@ùrÑÐfQC¦{QödŸ_ßE›ImZRDƒ÷³ˆAÈÍÅXõ‘•b±MC ÜtI&9üõ¢¾ï÷çöY©çË)*Ž@1΢ú…~èÕ’Ú¨ìéDXFõ1¤ÈçŠ ùʸgãöÙÕr¹ZW62öØÄÐwlB˜çlž¥™äšÅ„§ª(vÁRøJ"àƒÌ â†[•q™ÅÙ—IUP.8V$ͱ•D ¬é'ê蚂ȮPàÉ$šY(€OEÔàó‹ó¥`q”\j¶ dŦ1ˆg¨•É|þ²M¹kiΩ#ò*)ºµ +|HkrX³¨â;iNlŸ¸Å¨¸²}ÉwódÝÓ_öHJ ’Ð*RÕà ”/ÄÜ=ì2~7”ïÎÎÄî¬}GeøBžÙý$½KÛ¤¹¬œÊlšt4Š$uM ìe>'d7á!Â7*‡K–ÊjôpÖŽÛ¥s …LŒ*Ó4ømeNZŒ¤4D F#"3íãƒ%͵Y(ÌÃöŒîEd‡ u¡{Ø7Ä¡”º+ªB[IpCÔ¦«&Ê'¯}i¢|•Hrœ”Ö€q‰¢8Aî¾4Œ{¢V°ÔȲ$P Cü@ Tåãôs_5‹Ž‹¤é‹voå=Ç$“9lphEóÀÄeÇÍÿÑè@²Òæ×‘É!nÔ;î™Î}œW7N+/ȼ.ÒòÛãûYýÞ#O_„ߪ7²@dŠÌ})‘åû|½0ϸ÷Ü÷è»…éáÂáÞÃñ'Åô3 ! "¤€…‡>Hr,æH÷½¾4ô£üθri'LÄUü:H€y—¢Ó&°Äƒ…œöçàjžµ '„NÖǤ9xìyÓ6R"pü¦ êV“Ä€hveÜ1è’ƒåë¶]>æLA,Ve0·HÒ Yp¥©)Ø(;GÒÊ ™¯É·àÎŸÏ LÕò©™Æ™YÞ#‰%mò{„•;÷ÝëIn¼G9w]?"¢æÜkD-Ç•é›kQ‡wGþÃÿ±Â:Æ y†ß*ßçK¨.!¦)R( …B¡Qx…§J¡P( %PJ ”@) …B¡P¥J¡P( Eíø@uÑ\ ”(…B¡P(u…þ×4íéÄJ  …B¡P(Ò4 …B¡P(”@)R¥P( …(%Põœ@½üÉoC|ž÷Û þ*xúíŸÔé@{ñ£_Þú,öžçÞÿEðÜ{¿ˆ½çìËߎžz3xêÍëN—>þiw9&jk,òî—>úMÑÏ2ž}÷çÚ‡Š’ð¿ žyç§5»ŸWÝS9sù£ß$ÊÐJòüÙw–û7kýÒÈÔø²ôšâ‚¨lq烯ƒ† Z´lÜqüÅ MÛÁ]'¾`®]»ëî CÇ.u:ÐözÌ ´¸{\±y_ÁïO¾þƒ`ÖÂuæ(£i³fæÿ]»÷ÖlÚÝà&e–¸ûô[¦­N=ó•Z÷Soþȼ{ÿ=O›ßxÛÉ s×KS=;{Ѻ`ì¤9Ú‡Š’°aëmA÷Kû–üüÃOý¾»÷=ú^î·“Ïü‘«ÿb|Óûž5uª 1¬M,^}m0pȘܿ/éÒ#¸zÇaMc Qx•E ˜ìtä¤K‚›îx4xôÅošN…TÕeë|â—ò~ß{äÉ }ÇÎAŸþCM¾ø ³;|èü§ÁÖÝ÷í;\b®=pî]8*œ@]¿÷ Uë¶J õ’@!›ø­®d(%PJ Ê€µWßl:ÒªKÑÈìÜ"xâÕïÖÅÄêÓXÞo÷Ÿý hÖ¬y°dÍÖH-Ó¹×¾Œ7=èÖ£O½,•@Q‡ÝQ¥¨—êé·?3rµ®Ü”@)R•!ðZµagÐoàð Sçnæo€ª™ÿŸ~êñW¾lÜz ˜;ýìzýQ“¦/¶Þt¯Y³&PCFŒÏ3ßa?ïÕwP0züLãKWöù7þÈ|ÿ¼¥›ÉĆ-ûÍ÷\sÑàò«öäùìØ÷P0wÉÆ`Ì„YÁ²µÛŒ)4ÎÇbÛÍ÷s¯.›8;X½iWpè—rš–›ï<›»—6»íÞg"5}%ý„Ö›ésWã&Ï Ö\ySpü‰¼¾e·~"ìÃ%ÁUÛïÈ‘æc½oÊ9ÿrÓîó—]iþm„3Ï͘?gÌ_<ö||= Fôãcʬe¦h%âÝý¤ºÇ¨-»î ÛåÎTõºùÎǃE«6›þ„„3Ñ^<öœé{ß­GŸ2}é+ƒ~ Ýøé²c߃‰ã…EM©¯l|¼Üyêú­íÜ2ì³M¦ì¥—o3åøÊ8zê cÞ^qÅ£•­‰<9üЫÁŠõ7˜v_¾îúàðïEú¯ÑŽ78,Xqµ™£´3í-ï»ç‘·ƒõ×Þj|wVm¼Ñ”ÍwÑæî¼ä½”3{Ñ9͹ æê±³ïÏ¿ÿÇÁ ·>œk›åë¶{Û=Š@¡Á¦>ŒUÆòÀí[Ìsô ²„±KÛòoÜ!p%àï'^ýžWÌ]²!7˜SòÝüvíGM›^·ç¾`Úœ•Á¸)óM™l‹!PX=v|ÄiÅŽœxÍ´/rŠ:n¿å³Ù>ùô—Í»íætï‘sfL#{™´Ï„i MÛGù^1v„?»Q÷Ù»N¾kÂÛ´íö GÏ~9²‚`uÇOZ@Œ+RÃB¹ô)×¢TRÝ%:þø‡fFcT/¾…~þt´'ýFÿ”J è‡~G¤/Å(4O‚P>Zä|MÓ›f¡pÇä˜Øþ›q É(¶Y|X$+2ŠŠ…M0š8»ãg<³Ð±€Jm*Òï(¾‘ñìšá!NȈ†¶ù&HŽ;þiWˆ$Å%%vŒ#߬VÂG î{ô]ó.´)nt'e¡aL¸&ò’yi<ÿú ê)bl¹ŠçioW«N T”“†@1>\3}ÂFw·[æ›Ô†Ñ¯h¯˜O’@ñoW3oMñÈbúÐÕ’2®©·Üä2/,IŽ#PÔ6º÷Ì;BŽ~ÙÌ_H¬$PfÞUz¾»K·ž9m2ßÕ©sw3¶”@)*â^ϯˆAɤ@HšÈ“§¿lB‡rù@ 1Á¨ÕÝßX<ÙÕó&OœÝ(ׂ*ï““Ø‚z!¸­@„S»˜”ÃÂP*BÛ$¡ Ø}"€ñÅ`aa'YŒ²G¯þ5Þ©QwÀÌE ’êî(Ì,†hY’̹¼‹…Ò]øò|æ¾ð£Y)•@¥/v÷\ ÂLοÑ@úýï4ßf'Ú¸&}ˆY˜wº&5©‚ŒØ:¢©aó…\ˆ2RP—@Ý~ÿ ÷ÒÝ­ s™ñÉXw „%.ßín}Ù\…f‚qfüs"Ú–~blZ‚ž†@aªÃTé'~3¤ƒ9f æ:ÿ8û†)McŠ"-hÆ­‰ŸñY‹Ë¥F{JÅæ9n.ÛyŠ 4)ÈW‰(Eß2–ÓQ}Â\`>» W÷|/ëæBÌ©H Þ 1#K¨6!ú(ª…ã8×h:â`M Öq ÁuÀ­)Ú¼óˆwÁc±Åd‘6¤àÒ!W(v,îo˜L˜Im“3÷#}¹ª\à3‘–@±›´ áMû²$ÕÇúñ ¤‚ã:þFEáA¢Ð^°°°Ð°#,¦?íÑuüö9`Ǩ¸º[Åî’2ìÎ: OÚ?î„®K ­ÜIÖñûp T±ãgì¨E _!wžâ“¦lW[Ë‚ ù£ôb<—˜ãÒÞœÁŒwõ Z…™ÆwþhÔÙw -¯kŽ„@¹QÚ[(6g´ò$©m­æ0 bw͵^RÖÁNp/äÛwó¿Ê<¶ä(<ˆ:c™¿RÖYœ- Pl£üT] dd:ilE(6MÈž¤þ°ÕÖÕ§F¾° æ:üJòÒ(¼§úmžïI±Ù¡1ÿT©×ŸÏ„@±›•æ;ðH¸ËbpFE0ù„q’Ï”@¡(ÆTH;'i p*u úÈw¯5qØIÏ"PŠJâÉ…ˆ¸eIi øòÀ‚UL¶rK,|SnVšà;—¤‚¸ BÅbgµŠñ§u¸rûíF3à5Ÿ=þa^² 1VJ‘˜»˜ãhŠy“d•æ%]ß³@ã@w~)®*+·q‰Ó@¡ ÂìÉXj(4h|C¬V<|¯«Š"P˜Í(;N.C ÐàT‘£Ïb7i .×4Ë<ˆÒÚº²2Š@1.¢æEÙ‹2á3~MPE¸fÈ£J TÙ| ˜øDÛDû®\i"•ø›ð{wWÄf1ÜWSŤdÑŒò§€H ¥3°¾#¨«ãÔßQŠhv:QšÃæ~ì!Ü£ü0¥ðM."¬™R”¯K l[”Y áÀŽÀ ´ZFÏáOæj$¢ÿXÔÝlÊø·ù’˜&h¢hâÆeJª»mÌ w"ƒX¸“v—,2U>Pïz¯£Ådå(La˜™|æë7g >ÅŒL)ÔÇ·NîÎSžÅTyJx8æ7 æ‡ÛD•v+2÷〹cyߦ ß+ë—E[I‡kéì.} ²"P8lˈØ\DâígÌwDù@¡Ý‹";Ì%ˆ§«MJëÅf”1ç—åú@Õb|²ù°šPß1+´g©ŠyÀ8ð¥V±J¼?Š@1~â|kicže“E ÐšcÁpŸ# 0É_ Tå¨WiL7¤ ÂAÉ„]’KŽÐ0!𬯓䰋‹ ·É‚EÝõYHC ¸?N[€`ã»P½³Øø„;„ƒ‰˜f×á#P¼ƒðWÂÑ¥‹ «k>Bðà§„@–‹(»N—@YÇZ)Y”í±4v!dáEаÓÑFêcmý´‰›ê!L߹ޖ> ut5´ ¤Øu°µ‘4q 8É{Åû1Q¸fCÚtÓuM¿E¨¤ºC \ß!L„ÔGiòòÌxÓ›ÅAúDпúÇ%P,€uëoá¾³µK ÒŒ·Ÿq f‘E&¸ÂŸ±cSØyÊu0¾[’(ræ¬m»À3¯ì=Uy~6ŠÍÅÉœ/YT{A¼ …RCÃB ÄWÎÂCó€¦Tú­A0л¤(KE¿‘@¦*a~ñ6*QQxE;6@î8çož£ŸÝ¨¿4Š9‹æ’6’$ í+ã??7 ¯¶”Ýì1Ý1b£þкû¢ðÒ(;…2ÿbæó?. (pä “廨œ¹Š²Ž÷¶¸Æø£ïk@…c²š@ÍWUsuÖæé¨deÏÎcð"dðWÀ rv)ƒ™ðrîE3Åÿ 1u2‹4»¿8ÿ#bò™ï$Áa…ðãÝ,ŽnÌúËÊ‚ª*8y忥b§gEã€(%P %PІ "ƒ« ÔÉN°*DÏ,} F†8Ú€IÔ &©Ýäy:È …B¡h`°'Tcz&Ü¡±GáUßJ• GŠ»øúµÍR`ÄŒk µ˜ºæžFK Vïûؤ;iݾklN›&Mší»ô &.=P‘ßqÙüÝy}JúëŠJ”êùõ¿CôU•=ºÉú=-ÚöœºF…OƒáÓ®Ž%NÍ[´Vo 8êsƒAãW7Z5à²åE%2i}E~ùïÜz^:pŠŽuEEcM¸yiwIo;f_U•}¾'“]|ÀØ:à†Lº"FÐ4f‘kL»ü^mÌԪ[ÞzŸ“‡ëlí@žšH­¦1/4Q¥P”s®¾ËŽYbQ•šfUçKoxE[#È·¨aÆÅ¤‡ÏÓŠ›Þ ¦¯»?hß¹o¡&ªeÛ`éÎWµ-3 P Œ'IÌÇ/¹5X°õ‚ÙãµðºgŒ‰³ß¨Ef\)R(²E»N=í¸=¤*;uŠFíÜk¤²FfºëØm`)ªÒB~ê51‘‰^Þ?xÂåš×G̽欹6ûÊÓÆi¸kŸË‚Î=G˜gq(NSߪg¯ ú Ÿtê1Ä,d,¶£ç\Kül9_PŸå»ß4ß8aÉþ Ç€IÆç¦}ç>!Y|Û<³ìÆ/“W6ueñïÒ{tбësO·~ãÃ÷.6Uå'üð7ûžfÍ[æµõ–uá]Ö¹Z^›sÕ£ye/¾þ…‚{–ìxÉ\C[é ]»„s“Ù¼kOÕ®h!™ÿÝúŽ5ß y¶×äû¨gÑ‚û’^yí€]’£ùâíÏÇú“A꣞Ÿ´ü޼{Ç.¸I”ÿ‘¹‡±Ôµïe¦ÿ;tíoú¹×ÐYÁÈ™[ÂòåÆcÅ–Õªmç¼oaQ’m´ðº§½ó1E‘5ºC8žºö »Òœî 1uÍÝyåS¿ªqúZ8þw—štèÒÏ”K½Ýgn}:5{[Ð=·Ì™þ£—3ˆfSQ²µáJ ²#PÆyœ¨+d+OˆÏ· SÔ3Dâù´Pknýĉâ{£àÎo‚èøL5h>ãÆƒ3c}²Â:à ì#~>óÑ¢mÏz}r¨;ϰà¤òkÙÆ,ìy¡ÃÛž+Ê×gÁ–§Rû@A6å=’ÝF™Çâü¨ ´¿¯O cæí,¸F=‹k-ÂþqËÀÇXHÒTuï?!w¶–$bÌÜŒ©”¤ƒ37ë4>I³>KU&ª— 4·üh 5€×{ÔýM2!Pø4I"k¬¥¾ó7I! °K6pH‡ äk9&èò}ÒºމŠüÀ«+ƉÜg•‡²£”Ñ®ôOÂ϶DMŽ5;¸fý¾ÚtÈ7ùñ-* 5q$Wâ”O Ö˜]T¸;ÑÁÕ¸ µ7§¤ç’_ê9xšwjѪ]Þ}½†ÌÌ]ÇGúó`îbÁt1tÊÆ‚AB“‘náÿÐdÇfÑC3ƒ¿“ÍžÆôR §à!ÙkTþByé°ü¾0¹@ûçs÷™:K!P`åžwóxÚ|c¾1C¿J¿×A\úèU}ׇŽyõYï{HŽ9;$¡i² C ú ŸWàp¾b÷[cXjÖäøô(|ѹçðÂù¼.>C¤¥6LeaãAÕýÿ˜¨š¨u4&;\ >3&…¤çúŒ˜WøÜ’[c ”t´¾(ôGsà‹¦˜±+Šò;Éw.–@a*ŠûFHŽÙ¾]}1>\µA \ói9&z°­]Ž,OÙ˜rÍhLk>3¢‹ék%úîa¢²×ªZt{CÀˆ¸‹Í‚¶UÿË–²USEäf)ã“f’„W’5__IâöReaã&æêþB TÍ ÔBM¤ƒ«q'Ó¨è8töDa‰#P2ŒÜ‚ðëüq§XíGZ¸!î>%ÓÈ(®RÞí;?²6²˜]¦—@I?̹QQzY(Ih0íâÇ#ë€NÔî}˜{1¿Ê2|1DÄISVTY߆¢ETk©c]B³V`Z‘˜˜7“漨Æ6.Õý¿¨š¨v2­ÒC<ØÝûB©Ý”ìÊeØ;F®éërÂÈKÕ@áÔ ±I×‘ÜŸÆ ú¸¢É+î,ô% I#~^D}áëÄ¢_)Jú–Ui#æÆ(‰&M|øœ•“@E„Xç€&-MJ4O„ìç¥îƒ¢M³!Ù"NÔ´M”YÑD0Šàššh p•H;†Ý9åKcD Hí¡J!Ñá☼Q TÍ TÇ4‹Š¢áÂì3QZiöU˜ š¼D’…¦Éë%ò¡9‰ôjÖ"éVÓcDâÆº\ˆXd¤3±/¤ÞK D>¢¸4µI ˆxKúFˆ´±S#E¬Lx_ ¦"ÜßeWÕþOküçò#Jw§Ÿ á¸B“ç#Œ27“L&ËÜi} Ø0”2W•@)Êp: %PÙ¨¿1Ñ!+îÔÖÈ0ræVoòEiº`ÇŽ_ˆo—>Û‰‹Ncà‰Âó˜‡H…åKe#°ÈôìF–B $¹À¼-‹¥¯M’!ê¤KˆŠî«M…öÅG„íwÒ–>2Q*"s–ÍŽ.µœ«#Þ'#óRôÉ×Â8kûˆ’¯½\R/̓nŽ/_6¾ˆA_Ú[×Üí+ÈDœJ Y`ñõ/ºý?J T6ê]“&Ü-é k\@ðwŽÈŒÆ„±+Q¾¾óÊâò@áë¢(}tÌBÔº}ÁâL„™\€l(ŽËÀì 6ÿ¹ŒjB  iù‘BBÂñ¤ uŠ#P¾Tø¯eÅ2nZ†Ú$PøIrg4bÞk.4ƒY(¹p›£G8&§×(o¶lú:î(RnDÕ1*ÓùÒ¯åòq‘›ŠŒ!Ž;¡<Ÿ#6çõå§ñ(ÌúŽ G|úÔMÁü’f<æï6ï Ç0„Š,úòfÕó¬*«¾(E¶pýŸéQ.Ù¨«s;©ê<8ŠÆòùθKçÁùü¥|*.ò)—Kjî žÐ÷wR9üÊd5!PøyI-DArÂîƒR(™·HÂÍÃS› @ãˆ!îi"»J!PiÀbŸä€Þ¶ã¥Þg£´<–@¥ýã¦Äº¸T ˜HòœÖ7Ÿ+H¨š%PŠr€Ípuß_P•êâªB¢è@k”š¨«¿M5„#l\ºƒ¨³ðÆÌÝq^So ·<4W2Ù WËÖ³dM`DƒéÈ÷>eFåQ—  y‘ýÚ`ˆ¤ÔÆÑ&kJØdUi;§"W=MóÆ|¾M‡î¤§éç~otâìí‘$[(ÛÎh§R¥0h{I0mí}J ™¢*('C—*Ê–D½h…O)§­+ ˜ÄÐ.qR;˜˜0k8aémæhÞd™˜d¢Êâ,¶)«ï2$Ò}fü¢½y$V–9÷š³åÈ{|Ù²!î=qÕÂýÇ-ºÅÐ^ÖOKæ ‹‹lKƒù×>iL¸øÓX¤»õgL³_4¢iËÃñ\’~yl·/ÂöaÌ`J¶c‰oÃŒºUi°>MÔÜN]sOU‚Õé›smw06çôq/¹Ôøv|â˜[DòíU&ËÂ÷B¨Üþôù†‘4Ó½ÇGè™#î=næE£I ùëM•@eK ú„ø{÷M…¢TVJ‚NEíóhÜf‰³ÿ¤–…(ÌJú´vø”¹~S8Êjÿ*NŠp“êÌãU5â J˜"IÔ#V…îK@§P(j8˜µá„ñs6õ*ñh}›ÐΡ¡‘) 0CÎN©-¬  yCswæŸBÑØÁ¼®ÊufæÈwkÌ”,ÅúBýI•)¯[A9…B TÃ"P¾ðÿ(ßé[Vg™”Lôæ/Cû4Ëyí[… ´pνüÿBŒVU^5$Ä_›“Ê{ ‰t¢T(”@5<åé%ð§‰rÌ®mÈLàgîÒ~U(œÜjýF/¶óãw!.Ï„#(QJ$Q Bü£¸Rsž¢(•ñMo•± LBÚ6•ÖOo›(?‡¶ëÔ³:+|órDõ<ÍhV–éè–,‘fd÷&¸AûT¡¸¸‰uR€»2ãJ’R‘¨¹!þÊúD­QÊ1 …B¡P(jlVÜyÿbw¦Ü@ Rj50Äç¹ü?íº˜Pç¶#U( …¢Ñú:íû8˜¾î~™Wœù™ó%GE‘¨¶!Ž…ø[÷¬49Qÿ“ǰarØè@V( …¢œŽáŸKw¾jr†q~-¹ÚÜT!þ9ÄË!z—…(1*‰Hõ ý{ˆÿw€§B¡P(Šò€hÓˆ5âôÅcÊʔՈHµ±:Ä !þël®P( …¢Öð/!þ<ÄÇ!¶‡èZ+@‰P¦„ªIµvjRuôžB¡P(Šò`Fˆ~!š×Éš¯ÄG¡P( …B ”B¡P( …(…B¡P( %P …B¡P(J  …B¡P(”@) …B¡P(”@) …B¡P(R( …B¡P¥P( …BQðÿyr%Ù’ƒ IEND®B`‚stxxl-1.4.1/doc/images/vector_architecture_small.png000644 001411 000144 00000026464 12350112610 022454 0ustar00tbusers000000 000000 ‰PNG  IHDRX—ì,zHgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿ‡Ì¿,OIDATxÚíw@ïÀϽÝÒÞDFF-ù…È&{fd“=²÷—Œ!•ERQHÒPÑ•H{kïº÷¾ç÷GÊêÛÓ/ªï÷=ÿЭsŸçyßÏûœóœsžç¤…–ß @_ZZ'XÅQw\éëHK³ƒõn=ÿ&ú:ÒBVnL£¾"DÌ¾Ž´4,öáKúŠR4X´ÔVy„W4¹)_¾Æ§|ùZV~E|_L"'æUX"VÆ—'•pS28|¿!"–}ô -¥Á¢¥a°âOžÚ9`o!'`F×}9·‡ÛÄ-`L9ùsÌNÔZšZé¾xøýÑb'+Ÿ0›Øy|b¼Á×+²i°hi¬‚ð]Cô”žWxâ ©§üfÅÞg‚xµ£¿Týõ–.Ó[m ©"]çïY&ÐÎý`olGÕY ¬µtÀnã ?oíH&€ú’zz2GoÚSƒê°]ôÆL=»¹=0ê-Ÿ' 6}冑êÐ}‘‘¾¿,¨%Œ©KjÂ\­cG”¤¦ë­Wà±ÌpІ<´µqOÿšk¥aÝ¥,VŸ¥C–ž×ÑóÖŒãè8ß`õA†úÊý“XßgÞͪQ<`IŽÓÓþáë÷®º‘[r'+÷dÕ€Åè8kÒ8ã]¡¦·D_ @HwŦ1ýe ³Þî•’?|Gb5`‰Z8èàɿĦÌ[7ˆŒ!Ë gjtÖ°uû´|fq#D/ìcò€Å¯¼|àªóz‚ 8nÞZ&@—…kVV`üµzÿX>€áy3’Ô«b†ò+nÑW,ÒÔ,Â,µù\Dj˲.!bñi7,Ý!†É]Sße æ Õ·¼bñ—ÄËKt{¡¡Ù·Œþ:]^€Ø Ž_¶·ïuX}ÐÛsü u rú«Ì€î™Ç6»%ëÀ&7¹‰Áý`€ó_Ã>?›Ýnãœ^‡õ© ,~½Œ'[Ž$ßÔ ÒÃw°Nîr&ýd¯~U††嫾¸³½dkÀµg{ppO¾K¤7?—‚9·»/Ì´ÉZ§×ëhÄ÷À®ðØg‡„jÀßShmx!{cé‹®Ú!ƒ@ÂvIw瘭Òã¶)Î]P57líÝ-š,71FÛÃ%žn”¹lÉÇ4:Óå`æ9eñ}£•îzÈj¯s)v“«KþZ®±áýŒŒÓ7dVøÈ¢óˆþa~ú‚Ë—ôÜ=@äU²‘™Ÿ™t5X|ºñÞÛöÅ:‹÷ ßÉc30wšt1Ê2QU8¬1Àÿ¢@õØü±*TöŒ?°õõ'u8vKf±¿<Œ}Ð{rʃIÌ%Ë{n‹Zå‡>»½F˜×Ç’:ZtÅÐ2{#cÓŽ‚•AÖ~N¯g6ˆOÝÔkIì4œmrqf?™Bçö2;ã± ¬1DÄ›pðߨJÏxXÏ– Á̾š…˜Ýo/~ìtÔËËë•wf#L¡rº>ð]þ*?è† ËÉ´â5`v‚œ¸¤­íøU:ÅšÀòü!*³6Çu‡Î‘6ÌM{A)vö€ëaÀ«)Ú†uÒlÉ2üšx;³Ð%z.ôO&ç´G¸Eu¬ŽŸ_™Ãã¼ßô†¡ÙÛXgõ`r\æÃ‹ êÔ^MÓ^™¿£J¡Ç6¯ÂmŒSØ?s°®ÇÈ-?]¢È8X×]ø‘¡öÄHg`̛ݢy}¬«¾¢08k·Ì°'PT&|%(ÅÏÙn:ÚG 'V[éÕy›~˜Â…iê íï&tlŒHø Ö‡HÀ)ßv=½æh/úv @!ñº¸Àôœm5¦PÈß’ú…“Tm:òݵ©õÐãëB8pM[ûvRµÕ—pÁn0•唿‡Ö€V‚&\½ün– ðRO[/Ó¸JaÚû’K²µœ÷aYºÐî^¸ô†} ;tc{‚Á§Ž’O ´§ÄÞæQ×e ÝìC?;ïîÙý=«À žËADæ±ï`QÞ+rI„ ×TîRŽëÈp ’¸ÚèU¡rÊT€™ßƒÄ~Ö08c: èÞîù™IºÓ¦I€ÔûK°æ›JX†‘¬ü…™ò£ RæÀ¾(98f+ƒ¦éNž­Z'XR;Ô3¤Öìwæ ³:¿)ìsl’îô)bß=‚yëY<`ÝpaÈ›«Àßs¡$Uæ}W8íƒÅÁ3t'ÏV¯~Î¥ƒ$jÀRO —¥ÁÓY–¸†eLpùÞ×èêÎÍ€¿þîÓ];vE'f X–î ôú&ªL¶–” ? 2ÁSáŒË$Ý)³jœÞ{ÖŒ°æ'ô8ùQ†¥0nWª¬Nì›<ø§½Ÿ£«;{ @¯”í<ݪÁô=Ð=q ˆšîu‡!ùî,HÍâs3Ÿ¤;mºtµ%t“àë´Ÿð»9ññu·=mXø‹€•)Lú0OWwÖ`m©¼3ÇRˆ¬¡iÃV¥õav½:eŒÏÔ†)ïd4"—éêÎÁÐ׿þã9Sò±1óŒììbMÍ"Œëªžˆíî}ëƒâqÄã!ˆeû—™^ø€%;¿‹B¨Æ€5 `l¢šÜY})û tæÑ°ÛXbÛXÀrºÏØ)£VVT€s/´/j*}ž ½<Žj][ÞŠ“Q7X»ãft6™'årø6z[vJT:Ü º±RjÅíž™2÷¯ #߬{E Ö…Jð40+X–¬‰“úþuqd·«@ÄôÁÐ=†Lx->XòÚ'¨äã‘`y¿ë%Á»å·K1–ûMž^ ,xº‡¯X½v¿“˜~^eh‚È;™jž› £ä€Á€ö‘GÀæµ`ÍŒå{@.rµÌéeÒ7í™ }ÿisMe„ªVÕ=?Ÿ{›9ÅDmPÂà³uÄe ˜óI¾º¡§›€¹9cPm°´æÅ* »¨¥£â–·µ0†Ç«U€%+qÞO¦6XEg OtúZ:´!–¯n玈{û§âCþˆxq©­âaȽ}¹´±p”èm]toÔŒ5`ùk±½Âà` ̱+t”Àÿèµ €¦:À¦@ `Þ¹ÎTÿ>cµ¦ýqa¯“Ð-v6ˆ.=¼3ÀÌÜ2“¿{ R”jµ`x¸â /~xb0`í˜ øÜ]`x?¸D ·X| 0O!fŒMP†Nk'kILÎ 5¥]õ=ŸwS€¬ñkà†ƒóXÆÊ6‡ˆƒí]€¾ëƾ»r|·„OF×K„ž¯— Û‰ÃAzõ´¡í´²×ˆLÉwËzD® Ö0€®²ž§ ë—¹Àš²ddÅä£L˜\] 0ñ㔟âX%3lË+Ž/(á,µ¼ŸòYķß±.Xn J)|, qÍdŸQP¶êòÍF§½*ÑIZbÑ®9'ÊV¿ #ñ®·‚q¦žn€§Joï= gŽ…yùNK·ï•èòl.ß_ÞšßãX›ÒtDF;+I„¼mu´Óâç«çMSçëà·c™ñ÷¢Ö¦ÜL°>\—•6ÙËw)eÖ´P—~RΧõg•…e…öKví†æ+«sç.ªË:DIt¥­¸JÖU#9ˤOÜ]6W·Èz%ìZj< ‹Ñp‘®Ç4„Ô³¶‹+ØÏz8viêù®}^ïZ0S[„ϸÀLÿÈÒê©¡k­pÃå÷}E–Ü–œU²g´MüX{ÓųÇËj컵§æžï/6ö„X9ÓD?ÖPHº£³'wƒ´®÷Z½éùÅ]R÷/9=Ž <ޝߋ?|,_§Ž‡ùç,šøÚ§¿èí ‹gOèÓs]—m=ðÝŽz.Í Ö©Ï"³äºÄ;ŒÚ‘»Yv»óйSú2D¦\|j"øÎÞ‘æ›ò´/XZ9kÅèŠ{6È<.¯á³uÁŒaB—òŒõϯš±„g=YÄü ¬r“C7-O' úëÎtacüaCãSHùïÒßæZ‚E'F,z¢»8в:´_¯nÝlã:OsšMic|¬~©ÛöLgA¿»w§/vžÒþŽç›PcA%nî{å¾' ÷­Ø;ŽQ Vò¾m»Õ1÷©ÉÐÓ7z ùòmhÐP½úÒzDÕcÎpÐv™Xï¶íZ*·&¯r'xÞË/$ÌNVpÕÓ';e6ß±¿wÏ~üSx=tÇö-] Ýö§û5mNK/õö~ê5ú]yyc Ëm·í³ûð¤tÔÓÍ·í™ÊSÜ,´ßViÏ3 $ô„€Ì— ÖD¿Œ5jµsû¶n }Þe͘G†¢Ç^ù‡:È3tì_˜U­íÚ¶£°V»ŸÐ¼|±ãtïWoCýFAw“—·'W!"»{ïÊ=j9ïZ™g·ï™ÀdÌyj:ôÔMEy'Ï€ÐÐý,¹cžó˜ƒ6ìTý5WHQ…y\DÄÒªI¨(Ÿƒˆ.rÙ"³K+ñ㮯EÙiñ®»*Ù…vÞEøÛC¡¿LHLªË®N‚?BÖÒB5¹BÃÈNb|‚|À;4PHBzÈ&&0…kb™,&ƒÅWËyàF;³STTRnc€v‚¿FA¿›ÂÇÂb !ð)œè""!;}.C¸:<Á/)ÆàMB«§N¬† øË ‰KuÛÝ@H ¾$´¥»ˆ˜BíZû$D%;h°„«í ”p­éüxE1~!&ð³wëIH«m†ðŸ¬Œ O€TÐ×XP€Õ@•ì°R@àG!•«Vú´¯„³¦‘Ç{ KÈŒ];÷TI\JmQû†ª$÷äMuE/““Ÿ4šÑ@uƒ ÿyÞÏþ~Þ[\Je‘\CÕ g„y>Óý4JR¼Çõ¨nš©Íó±TÐÞŽbr:ÓX¬nÈMÌo¬åR»]_¹˜œM#HB‹¬J8Ú'¹±ÀÇËÞl¡D½e3ò–1³y~+q*è‰Íñ| €Å¯m݇gNÒyæëxiM§Êfý<ü‡ÿ˜/AÁ6Ðùº‘*£°D×&èÂ3ŒÅ¾/ïšÎk¨l¦[àHžúË!n~®lìÚPÙL{ãø¥<%±Bû‚<ìNh l†oDèUžÛ;ÐÅÿ¡¥¡BCe3=ÂÆñäiä.¹Ü84ˆÙ0Xâ[wÈó$Wû{Þ9?[¤qe3—kh‡I{5ÕuV9T7õUS­õɨö“j KFU­¯(oš­kÿžB Öc±ÕT»ò^±~ª Õc1:«©)òΙ½Ô»ò7X%ÔOM¥C­d¾Z?Ɇë±äÕÔ”xé«&Çh°KRUM™÷‘cÈ«+Š4XÅ×CMµ;ïS'ÒG­#³Áz,95µÞ¼”Õ½¿‚À?Õc‰(«©ÈÖÊYªõ‘hl=–ó„ì¨á& ÓRžË¦ ýèB?’B?¶Þ”JÌþçr¬g!4X4X` ˜ÞŸüý@Ïï I,¬À*yzÉøÂgÄ·¦ú_µËBD,÷°¸ìU‰áW‹±ÒçÜˆXé}éï«©øQ‡o‡M*rƒ¯ž?íÃF*ïµCê™S¹4X4XµÁÊݰõÃûµÊnøÙ¡ë§^ùˆˆÜwŠR¾\LcÇ.79ëo£ Ÿ‡Å‡÷†ªŒN7`¿Ìá\™åýå¢â±òôcí‡×”}GƒEƒU ,êTßDÄl­Þ±˜ÝwJ)<Éoøe]!ºMÍCj-ïþ†x¨}š¼Bôíú‘Ú%èÀ‰íÙ+8ë¸.°”I»¤2“øâ®”&Õ'¾B R•±ÄVÈ7¬.êBª1l©ßšv¤*ËsúOe#âY¸„™}&×ÄÒcºÌ,G++ä®pôèѹ]O0BÄì 2<'à‰Ô¹XD ]ùMy\yÝ>Váª1„²|Ù(R•s³ÇŒ5–DcþbÒ6¦˜Ž'UÙ¹‰TCÿ8©Æ˜S ´7ì$ÔÐÑ9?™´[[Ë£dgsñ ß–Z`qÖK—nŽÅ¢a‹cccã⳿ȟûþ«sžX>±S<"¦+©çg+O¬,ª¢ŒPŠmnä–“©T–—E\M#i©œ¸[åìrR•dûBR§(RJ‚n•——•%[†U\§ò²’G ˆÇ^€ˆQíG#âkÁµÀBoÑÃþ‡9˜ÿ× ""~ÞÉ Ö$‰PDÌÕø«ð[½`5A*_Ê%Vâø˜$·¶ƒ|8N)¤*/~g‡òò+ (’ü]:1¿¢©Çå ”ÿ‚ˆO„naf_°J&ªny…X®Ûé"V>‹Qú+Ó#ñœ€'â6Æ-DLW\ÆmV°?d6A)&¶ÕÅ!?6ŒýÛ:ÃŽ¾cA¦’õì⃊¦¶ˆHeY â¾!é˜Ù{Rå_Ú²¦!âyÆÜ8vî•ëëJbŽBsæ#,ö—™]Žø¢Û ÌVžP´´jɾ÷¶ˆLƒzþ$Âÿ,Ì]­êšüxº7?—ìþcu—Öÿ2"búT¦âä1»ò1f8³—ÎhG Ÿ 7õb_éa²Øœ]æÕ¾whi³^†ü¦ìò/÷/new³0x–Ë š½EïbH{ç÷ÿš ªiñƒCç­cclOßûÑ Ê©ÊqI·X¹Þ±έØH!–ZÜ.Aî›ã§/ùs0Ýö´±}R³^‹J—‹éä¦ÇýBFëëë5Ò™œ}=¦™ûPüØì¡³~ùr»YÀBDNÓ܈ßvOÞZä5AÉ<«UŽõšT%êbsº«Ä¢µ—B**îÿ¿¯­÷ «yMqÛ2‹Z× ².jP÷š«ñ²v„BêûfòiZõ«{++› Dµª!d”›®oÍõd^¿Jæ©RžžeRÿ°¯¥5áV:äÓkÀ‚ÄJ"_‚ÎnjȪíÅ}{&‚øb{š$¶¦ApˆG@ýŸçË}Ówd*¹^—î4ﺾ•¿Å>áa¹RÄÍ’V4„P_âð€ãÿwóíýI=;oç¤f~ D+«‰^kkzSFöyÒD÷¾GS+ ûH:=&û•þÇ´ €•Ñ” OiëIøÚ’š¶üó©M{ ÜÍm¿F7®ZøTþŽQ·°>Ÿ Wúpõ[kéåkbËöá+y3\ÄÒ`¢As)Ę˜ß”žl ¦0ÑÄ›xªæúŸýòZVDßõ!4™/nþƵ +?¢ NSÌ‹ÿÎ;ÉŠoY††ýÍÝR¸ÿu°Ú¼|&Î4D6ªlDv.É”ÎͬD,(û½Cn3`½}ß”hE+Éð¼½Aê¿™6ªp‘›àdFh |¯Øþþ!·°’MŸ‘¯^ü/¤µŠÎ³¯½#U ¾Ö˜áß÷Î!üâǯl¤Áú!…7_‘+…œi.|Ò]қɱÿýc8é’&Ý·ðåRÛÅnJ0©•¬ K‰ïgEƒ3VåË‹ÖÑd_{ý⋲?6à¶å¼S%H "±<˜¨’¢¿DþÉ qÛ‹ëäNž ­ôÎm }Ï'îyY;Öá9ÙôGåxÛfÿéá¶±pCÁMÛb}Lâ[A×I5nÖa Ëì¯øú¡f84Xÿàh=ÿH®i’Òò=O5!õ96~?AõBNIX“úVŽXØDÛ 6eU“ÛâYΤf<,R©®æ„6°8ðÚõ–rÚbä½²)‰SªÅìßÖ׳ÄÛ«ãÕb¹ñÎö²xЈ‰§ìÇtVag‘ØêoɿĦ™{’ºðTð™ÈêmM5äzû¡ù:@e³«ê¦êäÝ—¼?!–ýC20æB) VËJ¹—’x>¨™/ ¸v³î‹šj=N¬£Y#¸¦ýi°Úžä5WŸ}L£™5¶0ÓοQ%¡%ßh°Z\r¶ÀÚ*+¨øçÕ?HÙû¸¶ReEƒUe^^‹%vÎ<ÿ¯Y+Îæ‚ÑBœ¢'æ¶D{#) V‹K”%ñâ0ê컦Îâ'²“©( w \e;”Ð`µ¸”‘?ÞéMª…Ï÷»Kqe̽`Ì=Oºz¥>¡Ájl‘>àeM©X6süJqžõµÀ"Äâ7ƒš$Ó`µIº@ìh!—l›_b.Q¹NIbyÕ!Èlâc£bi°ZPÑgßf˜‹o6:Â]bmE¶øä&>2 þ·_õÿD+˃¸°7ö¼O#-Ô»[‘„ßžc÷*›¢ÁúJö­> /÷m>aȪ"2¦êU\µäñY|ù4X­DãI-è?“|Ûü1™›_öâ¢u]»mB¬Hý÷b³¬Ö! çü‰ÍOIýÛ,(ÄØ`¢­b‘_Ý/äX¿,̲‚«uȷˤde˜†ÕùyQÐmÂ÷§°¿:¾ià×)Äoöå:ÆÓ`µ)‹#ž²’Í^Öa¤>šÝýDB‘Ýß÷k”w­’MƒÕšB„_àýÓŠ/¿1Ÿèdª²ÄÊ,.þÇä?–ßÿ³Î/ogIf©T7s¿Æ­óˆsO¥E4X­F²®8W`ÆÅUÿïƒm8az¨ðvC'@òŠ q_âõ ¬V#åŽÞ¤*I&o‹ÃHOfä~%Ú«˜eJ|ôã]¬Väe•±?…Éûçëo\]yÀ›LËmëú;$njFÖ@Xø‹Ù4X­ÉyD&Î÷W_¾ýØ™HÇl÷M§ÇDvëœHûu „«Iú b+b?9ÜT£ÀЏ[O‚i°ZXÖ¤ò:ÔǤ9W‰C.4X4X4X4X4X4X¿H)§eÁ*çÔVY!Õœ`±i°þ p‚/Jm>°*ÃM²ÈÀŠ¿nuÞƒSX”ÿÍÕÇËš¬ò+{ŒvÒ`ýAaûõï˜Ðl`UÌíšHVÖÂÇÅ‘“^ÖÖ—9ñÏv6XoÆNÖ]žJƒõGE_¾ùÀâ–_R ë•J¢þ©:Àº¥UôO¥¦«ò¨?÷ûéô4X­,*?,ó̓/ˆT¬÷‹tDüöÊãs~ bÚ3ךӌ®uç«4*!Ú1ˆ˜é÷ì…ȉððË*`c¡ß£¨*ÿ)´“1'uìÓ_ÀâFö}Y‘•ì–YÝÀ·—Î ¿‚E†g¼}ˆY~Ï¢(IJ ÷ÜB.ù?úXÕHx/Ãïiçý÷H”­¹u""&9ܸôŽBÄâçWLŸ•!êwŽõx«òW°–ô¸åë¥=åPŸûÁ¦OwŒŽÆÈóOŽ ü¬œC [ G*¹ã§yO<ÇùbéA‹ç£ÆŸÉÍ0q±䀈ˆ‘û'I)<¢h°š_¸vk_¼=0߸gÑÇ1Õð|èõû+P¿ÃÁ «eGg!>Ýóäþ_£ãê‹ã$ù 2~À´²-[ñS<>™S>^¿4‘kš¿ì.ª°¨X…ÝÅ…«Æ°GþÉê®øDîûÈ©Ôø‘C«ü§’¥¬êZ(ÀÒ©ZÉѱ‹ÜÒüew—¯±K‹è¹˜ó3XÜ’6©Ú£JœFåRc o¿Ïxªw÷ıÔıƒ¾ä²×Ãú%Ð`5¿ø C´¹…§„ƒ1¹ó.¬Ü´¶ u4²q±ˆM9e$ðã'„"ZÂÁz}¬çòáˆG{¤|KýdÕÉ µKqÛ|§rÎÊÒè&·°0£·%âK©§%I‰ªûñSç»ÛÛ¯xäV+«c'ò¹÷ŽßTÒøPX§ä"Î×£ðò9+K#Û/êÇ­®¸Xñ‹ØÉÑ\þKIRÒs]èÒ1 Ÿ*§6´²:~¬¦?°‹ VóOX«g° <³1âZûÜ*Œì‘A\Ô—OD´cÝA«n‡OZßa~yý`…!Z+¦ÄŸrõíîŽÏUSVÞC÷¾ñ<›³jƒ•Þû â‡.OŠ®XGéìÃ’sœõsÒTœjêRCµ"1Hi §n°(½ù\|Ú7¢ƒCÞVV@'_DÇnŸ‹®ZEÝ)£w§™ì¬ÌT¿W«øµPǫ٥`ЪšÀ•¯ÕÅö+ÑQÊŸ×y¿Ëo‡†ýƒ"">DÆsœ±ÎŽÿ6çÆöðÀŠ ß÷å`@D .¨ËyOïm‰ø¦_ô™IÅœ±û?ì°y˜Š¹›)ÄÏ ˆˆ÷4r*m¬€ŽˆÜõ""¾ÿVX~ˆ×†˜Œ+¤&íDôØ}ǵ ¯ã"ÆÆWÿmö¤`¬f—låß7¤n:œ”¢°o¶sý¬•½³7½¾r1?ºúm*7#3³„,ó/_*¹ññ܂؊HÓ¡oVÄV7’\}Ø; VóJÚdF¯ÉcOW”ë1?¤ÐçBŽCÁá“§Qi#„<*ŠöÃ’’õ,…¹3õ¢ê5…/äßó|08‘ð¢î«ÀÊìmÉ»„Xµƒ˜c_Ï®¦V7vâÝT‘?æ"&Þ«'ËHƒÕÜd›·Ô±1zÓ‚ëÙ×:°¹Þg튧‡Œ®¦„ž62ŽÄük ç'Õëc•ZHð¾3"nÜðMû;–4Û·ãNžCb¨‹Êz»œûˆÍ V¹­øužI¶l‹úò½.×÷6'¬fÎ÷;ÌÅêã^~-—©ïuÏéÖˆøÍÙÆ•×ÿÊtµv©w§4Ç2±ÔËÆá#Ï"“jwëM½yå&‚•ëjãÏóa©ÏÍ{áõž’DƒÕª„.ô£Á¢Á¢Á¢Á¢Á¢Á¢Á¢Á¢Á¢Áj+`‘oX½VHªîJªAoXmë’qò ¡ø='U±=Kªñl¯?©Š)½Å¾5I¹á œHUxôÀñ4âÜŠùÃ#¡fa%tEXtdate:create2013-09-24T20:24:35+02:00[ŽË%tEXtdate:modify2013-09-24T20:24:35+02:00*E6wIEND®B`‚stxxl-1.4.1/doc/images/san00b_pqueue.svg000644 001411 000144 00000101223 12350112610 017665 0ustar00tbusers000000 000000 image/svg+xml R-merge k-merge k-merge k-merge k-merge Insert PQ deletionbuffer int groupbuffer 1 int groupbuffer 2 ext groupbuffer 1 ext groupbuffer 2 ... ... ... B B B ... B B B G 1 G G 2 R stxxl-1.4.1/doc/images/layer_diagram.svg000644 001411 000144 00000050160 12405117057 020034 0ustar00tbusers000000 000000 image/svg+xml TXXL S files, I/O requests, disk queues, completion handlers block prefetcher, buffered block writer Asynchronous I/O Primitives Block Management typed block, block manager, buffered streams, Containers: STL Interface vector, stack, set, map priority_queue, matrix sort, for_each, merge Pipelined sorting, zero-I/O scanning Pipelining Algorithms: Operating System Applications stxxl-1.4.1/doc/images/overlapping_merging.pdf000644 001411 000144 00000020364 12350112610 021234 0ustar00tbusers000000 000000 %PDF-1.4 %Çì¢ 5 0 obj <> stream xœWKoÜ6¾ëWðV'Ū|?®EŠEÂÙrhzHÖk;õÊIÖIó÷;Ž$J‹À¯†Ão¾y"?+Ý¥ñWŸû¡ûåuRwOiÕé®ûÜ–ëc?¨_w`•AÑgµ»íxªQ&Ù>¨¤Õnè®ú»;ã@±ûß~Ä¢v7uÄÏ~Ûu×`ùuQw‰àŒªhü?ºÛ—4ã[¥õú÷sF[V–¤›‰ke5Ì.¬ e5´åŒa£¬†ÞqÝ(·]ÁyàŽÎŒXžÑn;—)}˜Y®•à<èµáZ¹í Ã*ÂKÈAÙ$–g´Àæm‚H,zÄgµï“v”Ð=÷]С·%Ñ›“5ʲïXt¥'bntdö€Ó+à #™}w_¹¹,Ô\l©aЊω¥"NÖ$gaæ5ú±#3,bàsâ…oèQ¬…ÇÄ*A™UŠ¡a•@‡ˆøœXáúk’‹®¬²ñ +lNÄÀçÄ ß¢Ÿ¬…³2¸2q!µÀ›Z”†aAÞv’¤fÊZɆèseØ*·ÓŽPbgq AÇCL³Ñïî:e¾å”ˆœq/-çtxw£Þ½½=œžp¡}…:·‰¡vt†òÕtÚ¬›Îä”ð’§v²±m'ãj#fŒ¯ÌÁNÌ8÷5o•ÞX€£¬¾9í™Þ'QûÐv~Ûµó^¦Sç#FíüÒv~°ŒÏ)|#eê|Æ(˜•ôÆšë\›³¬¨|AÛáó¾§õ¦U‚AÑí*)–1ð9E€o´‘éi•0†DÀ¬¤i×\Eûü/"Ðý‘âØæÞöE™Ø[²{øù¯·W¯Þ¾@óBÆÿ;œŽï>M_¿¥ŸÚ¸€¼‘DZ³FÏ:BØ.¹>kXÿÆùÞƒµÃÅjB„QçLƒŠ[TÄÔƒ<Ž›ÐGmá5¥>œI¥w%³ÅЙ"Ü ó îê«mU¤u©N¨.ï»7/Õ#iÇK«§%a1'™rï¼²¾ôÑàjƒ²Å­ …}ç¬î]NüêÀ}2eœä’ïKœ`÷PÚëe\Ã*îe^¶KÞÃ*®eÜÛçWP„Æz¹é9UÁAðS‰`ÆŒ©-†ÊB%ª5A´Æ¢‰—© Ó<‰‡q%ö¹¨‘ƒïu nV#gL!R#[Kd› ÙYd†H0ka ˰›”lºÃ2”U¨x–*|¸ªH¥üЈ…w³‚̈u$‰vi«³ˆà>‹µfv°7ó0I¤Í©‘"ï:&›Ú×ËLšNGÚÇžÛXÏU‡dSm.«ÆcÀe$[WÛÂxnùä\e2ñ*kWÛqjæ„ùöBDc=ˆs wØ#WÙ«¼ÄÎø_s|~üú̦f:{Å{ij?Vuh7ÆVà‚Gpî`ôØ ×4&†ˆ´ãE­mÌB†¥±Içf»%‘#|"t%'†ÃrrÌ3Y2g­d®Jˆ­%h´k‰é1k:̈1`;™ŸO,))Jœïh%ó1I5P×”Ï \ÎXæåÃa1ùÒR1Êí*£í°Fˆ—Q,h%¬JYó†hªTÆã"êÐnd—­ÜñÚ-gÊëÑpXLfÀ ‰Qˆ$QX„MRñ5éæi#b”5"6ÛŠ%g‹É?±˜%cÉHÆX"l-A£]KLÓóŒUÀf2~ïœÞÜ!á+Y:£‡Ó݇Ç;<`o œ86|^¿éþ¾Ú|ÂM ùêæÃÓƒzÚßn¾QùÏîè!º*ö|¢7„'µÛ£çÓ;ê4@*tlr„ïÞèôÀ.ÑÛÆD>îÛWêÛé×ÃüJ+œ!ppâkï+õþøqÿðÔâcPã͘\(ü;ƒ§Re«a½PlˆNÁ;‰±‘Øއáðø¥õçQ<öâ-góõD}:n_ö÷«»øu÷?Â-Îendstream endobj 6 0 obj 1551 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj <> endobj 11 0 obj <> endobj 8 0 obj <> endobj 13 0 obj <> endobj 9 0 obj <> endobj 12 0 obj <>stream xœeVyTSw¾¿1’{ÄÒŒÄ%—×Eí¸m;¢µUÔ:Rd“EÙÙØ!d% Ë7 „-,a ;a-‚¥*Zi«µv\¨ÓA_m§µÎtœé;¿ë»¾wÞÎ;ïwÎ=9÷æ÷]>ßßwû°°¥K0‹å!§åm —Š“%žïÍÔµv µŽ ´äióÓ=^ë°ÐVö ðaƒÏÒöµ+®û¡‡¿F=+‘þW›Å*¶vKe%¹ÂŒÌü€‘áѯoÚ´ùÿþÙ¶sç΀”’ÿ= IËfHÖ3/…i"©Lœ&É' ˜‘‰„§2D%²Ì¼€äÔÔ´TZT²(-;à€P$”ɤ…ƒ_ظm óóö¡8¥ /à9ò€#Ò¡ái¢äÜÿ‚a)‘ÊBróò ‹’SN¥¦MÏÈf‹ÄÛ¶lÜòú¦­v;†…a¯aDZ,‹Â¢±ì÷X0‚íÇ`ØAì}ìö,û;‚ùa\Œ…-Ã\ØrÌ[‰0׉-Å ±‹¬ÖGKÖ-9Í^Å®`ÿ¸Tíõo^J¯[œÎþn"N Ëv,»¾Ü{ù´7Ç;Þ;Ñ»Û{Òû?}HŸ]T…/UjõEÖ½E²Èn¦^æÕZêLõ@ôÚ±$mÂ!\£Œ)'(ÓÇT+ÀM a|úÃŽ†! fºD‡H: ‡P•ü„G(ÕÇÖ¨`†@%ø‰—ö%ˆä‡ ¾ÆõæØ¡&¢”ÏxÔöRÛ{Y}ß¡¦ïØÈB%ñèßn¢ôšÖ#?ä÷Ó ZõÆ#ú%R%å-^ÙD¯£½NÜ›šÞ5QHJgK¿„›Ä£só7Œ-ZÜ"o-Abí åû‰ßíETð`×h§Öñì&+ضjãÉg™8Dk4Ç w½qîÉV|¯IUŸh>’Õ¥-ÿ ZŠ^C¿Úÿð·Çs#É»¸ÁY˜©ŠóÑo÷òú/¹§nÍì¡ š»78>ÜÝ/ð}šmÅ£ÔýÑâ6¿>¡8ˆµŠkú'bñ X­Úc$¸¢b4Š#~š¿ôù•чð¾/D\/N "O¿ 4ôò”»âc$aê·€8¥¶’¾è“¹w¬;‹l+µš×d±›€è¶—&’´‡zýa5“½?h‘;`Ž@øà™ó®Z»Q[/hVÖèr´ô:…É’üXò/¸ÑU¡:V̤‰¯íP;©­NU‡ßGóhç—«¸›Ñè:¯­õÒp*+ËôUUåÀ7€Ö¬©!¸-¶‚‚êœ5»Ãc‚žºG^Î)jÉ?1[#5v »Ë:UsD)ØÈ±ÖU›¼ÜP;4Uv2‰i1ôºÊ[×Üýìò—ç󆎺IûLÚ¬èb wj®_¸½Sà‹´³”Ï _#2Ò,®âÞG6*ŠÇ½±œº*‹A /3”éµiIq¥å÷¾FoÐWC…©ÒRIpo|L_àìèOº23Ö5;$PÕæÊµÀOW¸>%Ñ8νÿwާ@/‡;©]-Ê&¿›‹è㥅Ê;Ï3r4õS$ú‰ÞŽs‡äø|EƒN´©Q©ÔÈ Dj1©ÙÓ$·}M¯WâÓM H è|÷tÌGÇ›].Á™3^A87´ºò¼Ó]ÿQ“§#”Nj·“Eå0¡Ð¤‚3XáÐB1h+µåÚtƒÿkÈnhªh;Zë­#ÂAÇ(ð±J«vêUäCOø› ½EW[R««…Z°6:FÑKè–ßµÖ~ Áé¿|Á8PàÃú:=ÈAaÈÓȶѹþ;P¾| ùÐQo=geÌ'*ðÑò3ÒZl-®- ¤ãü7¢¦òºÊ:¨ãC{ƒm¸š‘z_wVÚ´1h ýÈ¿AlSÖB Øz˜FÒûOkšÒ« µÀ·B½y‚ië¿ù·NZjœŒ Ü—:¦ ¿Lùwù]ú3Úø§UÜ©#×xr¥Ú¨"_ßt–DÑ8Ìû]c@lÙšï,êìjwvTWÕTYÉJ{• ¬„k°cbÆ%‰Áé­ÈuIiEJ!dܘг‰WÏžnŸ½*à6F×´ž^3ÌàcŽè7þÌ‘±¬P§”–‘)>CVãpå´­8Kx*aòþ”uc}ËF[ž¾Ë3š“2]¼Š/Ñy•ⵦj°1a/K&Ÿ5ã¢ûY÷ÿ´½‚VîùqѰŒØ"AÔÒñsîÙ…óïÒ¾4;îðÎè“ÝOô_Q^}¬î»lêeȋ۟‘Dàá{ÿÇÅ›r7jÒëÈÚ›´µ¤ø=m—öŸÛS”‘BžH”„Ýý›¯·"ö‡Ín««ÍÕå9æYh¦–÷²\PÑ"=ÖòІY¯f¼¤ª*€ÈÐ1EL}Æ´zœ×öåŠÝ íÑpâKÒ„ñ)²ØIЂ¯ÞFø£ÛW6) Ðf^ýÂðèU¸ ]ÒÆ·žW²›úo7kq¨÷‡þëi>N¯¥_¥¶ ^#/‡>Lý–™æ'ÐZ€þ&¹ñ zc‡{ûÒ)˜*qg_ˆØ´7S(Š.Ë•çæçgfž,‰‚8Ùš9=•õw@lxÒûxò1~f¦ã"/î†A”ÜËús¯óžf]ÉŒËmcíMZI« 9§AÛDg[k§;±çèQ²„2'Q—Qõ±3ûú:~¯Q{e£ßiÑÔÃm]G¾¸g“IæÐ›N´ªe×ô²z¿D¥w]½l*‹ò翇ÂRd-°ùËêd59@lÝêJ»–E—”È Æ,‰ªŠ °A}º($Z–éDÔBâ£Û =“³‚±–AƒùøÉ}šWëeεC38‡F\6{•µÊm•Uµ0 Ž×P¯cÎCŸ±S9HÐúg«yCú{† ¾Re„íI¥½Þ ë;Ûimœ+dxFEE°gñªx‘é”YÑ#|Š£ú¨¥Ü\a®°TðmZ‹ŒDY™A-(V ‰ƒ“hµ%DoAQþ}®›×æ§úl|Gu“™)'›ƒNRrÆ«êËA º2¥RÍ"_*/7§©%º°R¾Xçõ+0ì‚è endstream endobj 14 0 obj <>stream 2013-09-23T21:10:15+02:00 2013-09-23T21:10:15+02:00 fig2dev Version 3.2 Patchlevel 3a buffers.epssanders@mpino1200 (Peter Sanders,308,108,) endstream endobj 2 0 obj <>endobj xref 0 15 0000000000 65535 f 0000001866 00000 n 0000007742 00000 n 0000001807 00000 n 0000001656 00000 n 0000000015 00000 n 0000001636 00000 n 0000001931 00000 n 0000002032 00000 n 0000002503 00000 n 0000001972 00000 n 0000002002 00000 n 0000002816 00000 n 0000002416 00000 n 0000006200 00000 n trailer << /Size 15 /Root 1 0 R /Info 2 0 R /ID [<1EEFFEBFD5D9BC8EDC7F25CD6968EE27><1EEFFEBFD5D9BC8EDC7F25CD6968EE27>] >> startxref 7983 %%EOF stxxl-1.4.1/doc/images/san00b_pqueue_small.png000644 001411 000144 00000115620 12350112610 021050 0ustar00tbusers000000 000000 ‰PNG  IHDRô*xpÃgAMA† 1è–_ pHYs. . ÕbKGDÿ‡Ì¿šIDATxÚì]wx”Uó=[Ò $„BOB—ÞEŠ ]PŠ€  €Š`C)ŠJï½÷*Ez‘Þ{ïÒ{ïÙìüþø"ÂÏܻٛÝHœ÷y¾'‰äýö-'÷Ι33ƒÁÈ>lQ#°×‚XÄ"×ðF¡ œøæ0ùTÇ<<…!0‚@0" »Ñ ù&1yzôÆCq8™޾èAøÛð"Ѐoƒ‘·Wóˆ!{ÐÎÐ<÷ßìPŸá<¢ðß(#/£ @0`ŠþEq G5¾Q FÞ…6@ø ’¥…–oƒ‘wÑ ¤ ;ß #ÿâ·P‚oƒ‘_¡Ã À†oƒ‘_Qû@ ÌyAkÿ_ìîüÂáÈ7‹ÁÈ«pÄq“ÿõ_Fà Ne'q³`Ë·‹ÁÈ›(ˆC füë¿ÌÍôÈý}=ß.#oÂ@ ¬ƒþÿý—føÃ1ŸcŒ üÅDg0ò.&€@¸ 4ËÁDg0ò6Ú"„X´þ‹™è F^‡+Ž@XÙ"º3*¡2*ÃÎ|󌼃ˆ!ùô·î}„û¸ƒ»8…¬Ä3yv˜Œtbñ+ÊþËÓ®ÅW/ˆqŸ"=Ñmñ¢Ðšoƒ‘WPSBnc&z¢1ªá5ÔB Â*@Ø‘¹zŠ TÔ@8~æ›Ç`ä8 'Î"‚ ˆD8b #ðÅìgeªÿ½*B1oƒ‘·àîXŒs@ ââÖ` *>—eŒ0´CyÔÃb¡ß6#ïA Wø ÞÀ¨îÿý‹AHÃcÜE(¢ñ W©3ùƒÞhŒqkQ˜oƒ‘ñOŒÞ¡È7„ÁȯD¯ °Ã\Dq¾% FþÃgx aøœo ƒ‘ÿÐKQ*ók{ŒÅ8öÀ3ùZØ<×FÛõ¦a0 ƒÁ`0 ƒÁ`0 ƒÁ`0 ƒÁ`0^2ØÂvаì`@ÿÜWÜ ŽÁÈÛp€=&a7v»¹àV`vc·ãwO‹8}…]ØeÿóÞ¢†`:øN1yŽå®•#–zí£Xò¡!цk_Dù}t­nΣß'#/Cy¥ ')€¢ÉHáäG~IDQäG~AQÔˆ˜è F^‡ 6”¡G$B,½—¡ÛŽ‚|£Œ¼Môue%D7R˜ßâšÄ÷‰ÁÈÓÐz¼³èQœè©´=¼^ŸÍVe0y „ÅB/„D¦× gáÄwŠÁÈËÐcÚë©þB¢³Ç`ä}CiIŒCíH³Ž|£Œ|Lô z0ª1ß&#oCëüþ¯~ÑB¢§Ññ6ïò#Oƒ°Ö%ñ¬Q"ƽA8Íbƒ‘·¡×þÒ:1PBtãŒ|£ÇPKÂ^ÑŒ|Mt] êÓ’{µ3yº‚ý¾ŽýF`ÿæLt#OƒðWá”3FÉÖ½9áoÝŒ<¾¢ë¿ëÂbƒñ*Çèœ^c0^¢§ÑÑÐÖÙ0Ã`äñ­»Ýý#Ã$X_ÿŸòmb0ò4÷œ gH"Ƶ7jörQ ƒ‘×Wô¡#¸L•Áx•côhjL8ÇDg0ò5ÑSi[DݾÜJŠÁxY …Zh¡…&ó'šÌŸˆ¼m:ý]â‚%Í!ß.çæ ÆK‚b˜Œ5úYßV*ýVcuÙ#+ëgbí”_Êï‡uø6ëÁJ„ÝÎÉ’2ÕXêfÐÿÁ힌—ƒÐ¸r³§¨cšïíyÁ¨Í~r§cZCz?%èÖ¤0ÂaÈzàØgTP$‹q ÆËB½w¶EReP ÅP ¥PÅQ Å“‘iUlÕaЩÄèle0^è°ámJÆÙÁA³ âlDO¡¥Ñå?‡ ßbãe ú–f” ´±ö'lLDÕk'¾(ã|Uf1ŽÁx uú~TBŠ0EÖ…p(ë0É-ú\†èqôQšÝZA|Ï`0r7Fßæ{ß(\чdè×À>ë?®§>a1ŽÁÈ +ºí¯CR’Åt ÜRG£Ë¦©2ÑŒ<£hQBéQw› 1.‰¦Æ•ø1ë<ƒÁÈm¢¯n+TÝe1:ô˜Ñ8ÍO\¾FéO‚½•Å8tÀ»¨G´Fg4€š¢3šÁuñ.Þ…?:ÃŒýƒ†×|3”ĸO<.Ч©ÆÓg) {ðB‘®Î ®iýOŸZæéœÞüîãùµüÓ«ÞYØæ¦kºk°® o 3P»ÃŸÁéÂæ»¢ë÷f4ž-V>Œ·NŒ^D¿w] J¦+t–îSݤ³tƒRé!¡ýÚ»|ÍŽÁ0cë¾¹¹Ä0ó(dÔ[B1n½Õĸ¸3Yxæ0êŠ.ÜÑ‚Á0‡èRÃLÂvÁöÛ„êžH£‹L¤æLÁ½àñEÂ3?%oÂüèŒìCã2bB\ª,FµØ`aõ _¹ç£&Æþê'rñP ÕOD.tg0Ì Ôfï¡a&Fì–Â.ëßm_îÞå4ÉŠþc¢ëdŽp•{„°étoÿÛEFñÃc0² ­nFßt±a&&pg-Áª¬ñiðç$«Åè“$göȦ¹;?;Ã21º¦'yLT Ù`•=¶Ñ‹é.ÍäÁ° ÑWtP3̘ãâiXŠãBµ<:´3‰„ò£ªéø”'C0fÄè#j<~,6̼O8 $úòJôD"Æž„©:ã ´žtÖ ®t?r¬B©NüðŒì£R· ¡bÃÌáÈf=c•4u^;}#EâuŸWb´¢×]£ÇÓŒ«…jUäGÇ`X&F7ÒÍÐOZ ~SSûµS¢ç(F÷ÄÍÉ£3¹Cô4êEøSh˜YZE²uÏÑmŒº.&zy#†ñ£c0²M±Á3¢ 3Ò2Õ8ú(Ínb‡›á_=¦èhá%—jÕøÙ1ÙaQ…ˆbÃÌiŽs†é5#%úž®¤(ÆyâúTᙓiç©R^Íøá1flÝçõÈÛ^b÷ 3Þ wÞI”Ðqq´ÏpÅ.°R1Î*𙆟ƒa™=&¦¸N)çm¼î\I³N_÷‚oÌ´\xæ Þ3 ÐB~v Fö×Îѯù=f&¤8O¤×LŠqŒš=Pëá(уéí(|`ÏÁ0cë¾è}£Ì0³·¦ÚØä zê?®¢çær`qŒÎ`äJŒžNcRf Vt­kç)’!‹it4´U(õ‡7‡ ¢¦±èmÇÏŽÁÈ6´š9½ I E-„Eb%c“£éuÂY51Î^¶ Uw=Þ×Áu6ù@ᔳFÉÖ½9á˜âÖ]£ÇÒø[–áGÇ`d„ëÞy’!Ü~OMrÿYÐ}¤šçüìŒì£v‡]Á*†èl¾í"©Gñ›SÛÍ!ÿgåùè F¶Ax¯ñå§bÃ̼Ē?fm˜!+œzÆ(ñ¯uÉÐíTl÷\¸ÊŠ=£3–Ûº¯j'1ÌÄîftŒ ‰°ŠG8ô¸ñD}äÏeª †yD—vša³ ö*1zŽ,°î/’dèŸìëäò3?;#ÛÐÚý249Ù b\ ­Žªò©5†,r+)ÃìMòŸ¨ûbÃL?ÂVa‡™ m%“Zâ}WQãŠxìZÇÍ! Ë­èŽ?ŒJH’50xZcü‰XìN,ÆÅQÏt›MjbáB×Än÷Ì`äJŒn Í±Õ>äµÎ]õ¶Ž3ε؎õ&8,àgÇ`˜Aôõ­¬b˜‰²ZsHÉÄ`˜½I~ãí¿ *Õk&ˆžL3cKô„7w‡“ 9Fg0,GôVoò7·î+ã}F &¢ê1­Iª¿¸|Òž<)OŠŸêÞÂÖ´@õxl2ƒaæÖ}]káÖÝH‘ëêŠÄ¸ï<ÂÏ‹8ÄÓ§©—{S(Tsñ qK‹_wÊÏŽÁ0ƒèRÃÌ'FÍFaFëñ΢ÇqÿAŒM#Ø6-ÆÏŽÁÈ6´Æ|%Ìd™ãÖ—±–WצqsHÃr1úï‰af Q»AÍ›HcŠþ 5›î—!þÂæ~TÉ€Ï4üðŒì¯èöã¿Hf"×Ôv]P+ã©DŒKâ­èŒ³ï2árºXÏßyª”w3~v †ebt­ˆ÷þJ º£s™‡b6RJr™5›´g\Í¿ìRý5~v †D_óŽšaFS®ñ¶»‰Ö‰Ñ=¹9$ƒaѽc“ ~jbÜjzl¢Û}ðÓµtã ‹½a›ý¡«ü˜šƒ^wl©î 4<Ùižb]?f˜oš8C?ãj¡Zùá1flÝ%Í!ä<±‰PŒ[Z•|%bœáI¨ªç‰S$•/ÝŸƒaÑ¥†™~„?D«rý*g¯§Hæ©ü_lœbzMj˜ñ£*éÂé5#ûÐúvt¼’aFS£Æñ›)Ö‰ÑêÏû‹›C2–‹ÑwyÅI 3Ÿgجf–W¢'Ö!:Ö÷‰LŸyØ#›îüðŒlCk3y`j²ØW¸µ¶xl²LŒ‹§ÓV v¦àawzždNë½-ݾågÇ`X&F—õu‡ VV¬èFJò=WÉ©A"úÙð³c0Ì º¤¯»À+ýsÚ{-ò[ú)Ã7lIÒpÚ”p'ü{ãÆÇaŸ¥`—¹¦Sïž«ÂÓ„«ò±ÈVݳ6Ì~)%™ÔMMg 6 Q£ÇÑï7ëzóûɰÄÚYÄÖ~ιnߨ»Äzœ«={æ»ÃšSƒ¿z Î8íùq€óLäú»VEÞœë'î¼z2µÉ37ï& 3×Âú´œQ¯ù­ER€uTwOÜšÌ1:Ãúðl&ΛMŒQÉÛ2æÐ‘ô”eÆùt?õZÚ\Zkü3­H(ªæö§j‹šUܧ´n'õíkvŒ.5Ìô$ìf¤1z µ$Q\Ñm{WÒ²¼ÃøeXZ Â·Å¯ñ9*óåÿƒ6ô'Ã~'ì4së®)ññÜÈ4+ŒM6Ðý€/ßP¼J›a_ßO·\rÑõµü’2,‚zÅâ¯Jzœî Ç\þH6À€niâ6sÉn-̲Œ&W~(6ÌŒIuš)øÓ¡uêýc`”¤=Äùà÷ÚBM—6‡L¦m§Kú4å7”‘sè²]ÎŠí ·éÍS(™Û ‡¡S¹k—ÅXP”Žf&´tXÐ=Cf˜ÙSK46y‹KÒY£$FoN8f 1Žg¯1,Àaß"1Ž“K8r}*Pèßý*JÜqc†ç43Ýå& 3Rœ§ Vt½nô»qAÖã 6ŸzÊÀb#7PÓ+ì¦ð5¾EoœÎýÀç­I<é7Ÿf6X–®è9‰Ñ£é- Å]³¬_XªøÌßݳ£¿¡ ‹ n±¸«’¡Àû'å~Œ^Noÿ$îêr0­ÆæEÅò=zö¨©îét6¸s;Ž˜îÒL‰çú¡FîCø eä—É›£dªû£ÜWÝ{áÍ §¯‰côlj#»›£kC%žâ¿Ñý…‰ôÚ.³ÓkKºU 3ÐÙ|Ý+:DRä7½Žb=º›ë¾U\Ô’Q ç@ <ÂHø¼ðªÚ£úá0Rñ}.&©6޶L‡C.$Ø·-N0ÓNƒÏ/f®¡& 3£Ò ÎõŒ»T(íŒQÒa¦“r‡±î¯¼¢çáMû.—ÑPð/œ1=ryE÷iyí±ð5~@¡Xnw9Œv^W®ˆcô€„ Í\CµÚ™}Ò• 3:‡_„FX§gœK™Í[M4‡ü™y“ç0i „ám©:f››©`w\£ÿ7X0è]‰3}:é6›wŸó*†?fzþT3ÌD[­9d8õжsfÞä1ÆQ‹Õz€[ zÅ Û—í:Õ¹„2¹mÕŸvDÔÓH»ÅÜ2U·a“„}Ýt+tPkÁoš z ­¬6Pñ™³97‡cô|††ˆ!_²Ï%Í£'ѱߠÏí¬_4¨tô†AâŒÑÝìê5©aæpd³ž«^7®}BdRKœïžªŠb6ô‰”4‡¬žŠAìŒËkè#~ðy™>”=àþÝ.±ÁóU¼ ïÜ^ÑË¢P‹)b¯ûþôׯ˜é.×aSsIØ÷ ²ŽÑ ›\%eªqÔÃ`³Eqöšcƒ¹G2ĉ»K‡ëëÏÌÉcøÂý×+èðÂa\Ô¾—離_2 ,LW¯™›^Ãk]¶ §©J 3ÚB=ÆDÿbhô‡Æ¥˜9y ?ƒ@8 —ÿ÷ó7ð'ö>;öa=ÊäêçªS<öš$NühÜr{ûè ]ç‘’zôÍ%§˜k˜ù°þMß ¡¾=3©Ø8µ9"zq\›Æ1z>ÃO ÎÃõÿý¼Ò^ðÇ…æ²Ê]«Lø ‰¦¼hr;—;]ÊÞ¸$N¯«t˜YÑA²Gˆ Ø[“”ĸdšSî+Å\‰þ»áÓÄf*d`(w˜Ékø Â}xþ¿ŸWÅŒÃ8ü„ùˆÏm¢»þ\"&ÀIr BåܾU6@YϸÙd»Î¼žq¦ 3_ì– ‚½fê)þ1.Ù÷JEE1ή÷èéb!tËÏò¯3sòÚ „(4‡‘Îm¢ë2]ψ_ãSä˜ûDןµ±l™ªÖfÒÀÔd…0A:À!ž¦:,Ä÷¦à‰[“$M?Öœw¯T™“ÇPwA |û2&,°Ñ´xnîoÝ[£Úk{o‹Ókw’|hÞJX^>Rb˜éCØ.0Ìh‹¶›û$ÖZbÜíI’݇r·òa h1#Ó[æå!º¨ÔðÞáËæKîDÑܶÀVBÑ7æ?§×Ž¥6úÚÌÜ„Æùë±B[¹‘ž„Œn*ŒÑ×—±–çÐñ—‹Ü2ß¡`Ä~Yˆ^°=;7EnÍé5†™1u?èø&ĸ$ú%Þc‚bëݯCüÓÄå2o¬_Žy“Ñ1 ¤a%ªdñZ5@Ø¿ˆ®ƒ=¬Ø÷AT­á{Wø_¦Ê7á•ÛʯÐýSqëZc,:ÛìvÏZªMÖcv½tqç{¢ô'A>Šb\qíå$ž¦zâh•’]™5y6ø a ñóÐuQ^¨€h‹18~¨”ù¯Ë`(Vb7Öã#Å&9ŽÑ“èø`ýÝËÜ’¤×Bâgµ77F¯ßv_A…èè^êÉ¥tÉ,›‘É…gÃ^1F—fþgåùèy:´ÂA$@HG üñ~ˆD*„xF÷gÛÀïq‹0›…IÖ©j³< ¾lרæ”ÍzE×ht¯ã[ýÐA^…ºbT¡ƒËé‡à»JoÔ­ƒ¯µ_v¯P¤†þË”-Ø}ú$‹Ók ©À sG2ulrÁOl˜Y”Pæ;ÁASªé†û Ö‰Ñ ÕX²?ƒcôü tÂB\@â‘‚T$!¾8‚Éh…ç«+¢4´l1ÁÖièä 8­Z§£—´9_º¥Þ»ò}d ú:êþ•RZÒšÇ{îµ1v2\»þe˜6?ذ֖²¨Ãš6’¨?&pGm¡·Æ‹[©9äÞž±Â%‚<Õµve¾äihQ•ñ&Ú¡#Z£>ÊÁIÒ¼¢…Í*rŠÚ%£® _ã‡ôîaã‰òüP:)’)•Œ”BÉd %S ݧ®Û ôž6†wí-÷ÅÛåëÉݘ½“’ŽMþÔ¨['Ø~›ãè›$çŠ[w‡“ $Í!ïëà:ž¹òÊ@¸ÿÑãh³¸•”ã׋ƒ$"U4-™;!¯.JÕ_óPLôs)o3sd‚Ö~ÂÉ*†Ø`Qu£¯TŒ ñVã¤1z5‰C[~ÿ_hÐAd­(NkVÆ _¶ÓTÌÿ™8ø/¢%%úqr{ oÅö¹lëþáOs;Ìlð‰¾/6Ìô'lfФâåk©ÿÚø„¢Ók®Åwlµ g¼*x¾øVêÏh x<,ñº ªå ø‘ý¹ú«UßzõO‹qËÉy¡™é5mÁ1_'Š 3aAKê VeMÕZ‡o%[)F?Ý-A£‡P›t+À x%Ð1Öœr(M¯…ÓoKá(¸ËyÜ>%!zÍœ{•­û7èQæ¶$½?¯%;Ìh]\åÏ:l°ª¼•Ä8qßµNÒH:dÏÀBK˜¯Zã>fY1‡ Pµæ“»’AÃ6 G2éwLˆ—Ý—>Ú¥Þ†>/u´Œœ™m˜Y×JÍ0cƒ 2 l< J-°LqÇ%ÑCéÝPtqdä{4ÅcìCx£<|›™@9Àöüœµ3MÜ’ýVN¯™žnfŒÞ¢ù ƒÑWT¤'Ò2ÕkªeªîN,äý•Çà‡»¸û¸Ž·¬ñaª¨åhŠ·27Q俈ÑéÀ$ضÅë½.!z"˜Š…ó-T¬±ã®¸LõZr×Í<¥ëßîŒ4§¡hU~³Â•«©VrÆU[¶O(E¤ÓƒMŠc0r=Pºûiqã‰[ôúY”,žºc%D¿BµÎ*gÿ‡¾cigœÔ03€°Y8ÀaIUIÝzÍ!£è‹G¶Í<ø%eä¦ZII'µ”õ¼qFBô¿¨°¢Öø¨gŠÌë^ÐL¯;´Ž?~+L¯åDŒ‹£^é¶”â¸ÊÍ!¹ƒše%Í!ïP³(¡QŠÑïRëýÿjn-ŒDW‹W¯ýé'1Ì|jÔ­Z`ו•ÝH1OwªpÐN”*nYـϸ9$Ã2¶{N ÝS`#X® ¼ZV§K‡Ê‹ZÚ÷‡ÅHëÑç˜k˜±›ø™p`¤ÈÀõuE†™Òon¼— É´N}$“C'I‡™Úw¢\ÙÖü†2rGÀyæÆ‰r^ê±pûíðÛ¬p ÑORñGðRü`¹ÚaÆ@KÊ~+"ki=ºõ†,ÆÓÜ+Î5ªðKÊÈ9Ld:!Ë£û|zTšGw¦æ¤(צ³ýÄ=㎧6þÊì‘L’2USfêK:ÌäHŒóÄ­Éò›C2,éÅ úzœK—냢‡ÐèE(¨²uo‡ZUÞ§×î&}lvØn®>ÍPãdeª±ÔɨݭhR¶ëûãÍtãV‡¨^)ð¶Ä†9v% ˆn»ò÷ ÑhøJ8iÔ>Õvò¾îÛÌuÆÕm¿;(]±9¤LuÏ P¿yµÅ8ý·’I-¼ugX ¥»“óRd[÷PáÖÝ­Ýöûr ¬âÖÝøøýtqzmŽÊ¤–ÍÍ%†™Ç!?ŠÚ=kÝÚÍ‘ôuO£=a¯µñtž¸1E"ó<î]¦-¿¤ŒœCø´¼&®Í:K%ŸÅ8éµÓTò”º[eöšÔ0Ó—°-kà á{÷pɤ–hjB8£Xx$ãü¨§×ƒÔOÛ§ÁVP]²ÒÙK¢ÇÓΉЫX`Ý`ÓñÛˆTÙ4Õ©f®¡ç¯ÆÅ¥ÊbôÃ"1N3©Yr€uTwÇÆ³rsHF®¬è^ï\×fÝ£wŽÀS@V푟’$D¿IoA1Åö…eÓk„^±BÃŒtÈ¢ 1.†Z)Šqš•…§Šõü/Ú¾UŒ_RFÎQ°?² YÍ[Êëâ«X`KÁ±ÕÄ`qzmzµÑf¦×´ºéý$óY£ÿ¨­66Ù@F6Q¼ý¶gçŠ{ÑÑ̓oå%ah€åƒÅUå7¨Á”V³Àޠש­è=ФÂÉëâ¸øQâ—=,ÛaffR±qyO[èƒqQ:žéØZÅ¿³’=€ê%q™*ÃRæÑ“èÈoÐ ”&×.[Iˆž@{~V‹ÑuÀàNâ°`i·š]½¶²½’a†°Î%ñ¬Q£7#Wã\ÊlÞjäau8ncvD_ã‹äsWØàÑnÉäh ÑÏPé»(«ò©´ÀàŽ¢Ï Ýfsóè_Ô|øXl˜yŸp@$ÆéƵO²RsÈs]…%uF]B4¹•Ã(¸þ¾%J­•”w¡ÇÇLµ’RÊ£@+ï‹WÅ[÷€„_Þ5×¢RùýM¡bÃÌÁ¨¦½Ûo1z45'S\ÑÝ\¬”Dÿ~{»;Oå—”aý­»}òŠè”ˆ@ÃV¡JØmÆ…ŠÓk; >?›™^Óa³8F7ÒíÐÁ­ $Æ¥Ó…à®m¬£s»g†%Q³\ØMáËIs €`—l?}F„„è¡4a (>¾ÈÍêµ4êMØ)è0£søEh„dݽ8ø-¨]fч—qã †õá8ì[”$à ïèüá:Ù¨¢£ä¨6ÀÁví'˜i‡Á{¢¹†O§E«fœ ’F:1ÔŠð—Zp¬{|Џ9äëñèË#™€(ûî9?‰ÕC<’ÉDzí${¬V>m¼._Çè~ ?u1·zmq…ˆbÃÌ÷iç ö:›o>ˆ ±Ž3ÎÙkýv£x¯ðd_'—Ÿù%eXR l ­˜ {Á¨ÐÒe®œ“v˜Y¯ØaF|ü^ºxŸ1“ôÍL¯i5s{ÄgŒ Ü]KÍ0MoNYÃëAžêÞá±É¹ 'TG ÉQÉÌjª—Z B“;…/Ûcê±î¥IwPÚöµÛ£Ö3N|ÖÆ¢eªò=~K.ò»àŒ&ˆžFC›uU¬^+¦??›cô— -ta.Qn‚£PŒæ"ŠçÅË* Øž™›"·ÀŠÒk%^;uÅ*Ø÷аұâÆ¿êf¶3nIc’Jã íˆ>Q¡’zô ¿iuëÑ5 ˆ“ þT=ƒ´¯½´p‚ 9Z¢)j£$ à¿©Û{§dú6:Oç²8.Òd²{`µùåV…xí5¿;’Þì¯]G95 ìeªqNí®x¡p³ib¯ûôê?š—Ð"Œ~Í l7Â>‘w®PÚ£$¸iGš}ŠE-ßš~Rø÷,•N­R¢k¾'y4Çd‡/¢„$ ÷±ð–5ÇŠˆî“.®ñÚAò(ÑMÅèItü7èJSÑ{îH'µ¯6©Ù™½f^zMSºïâˆ4áª|*¢Ýû‚%Dç0xhXø0À!–&Þ,Xß+_“\ƒúØ„XFÄ#A<Œ b±o¾LDßžg‰nv D²¢W»&\Ñm6NŒ“ý^®>·ù”讀ãÆå 2g\€Ðçôù² ÑQ?x«|ª·QµÚîÛâôÚ¤~}ÌÞº/ì*)| ØWSÔ֩טÀ¨ÿ@Œó£Šš_›CÅE¿½dŸ+ŸÝ(ùÑÑ ‰Õ-•¿l"½¦Nô*po²Ð7]2©åõ‘fOj‘fƦš¡f˜ÉÑ ´žtÖðjŠqMB"Z1Ñs ÒôZM[Œ‚‚]rÙb·N[këþykùÖýOKZ`¥]`M=™E{©è‹ÔÎÿ8$Uœ¡sÇ¡q©|Jô>0€ðT­aÝ\h€*uÝ#±;kðV¸ 2Yúâ­"Æ9=ŠËºhµ±È\ótn´J!ņ™»ª»^ó{sI»g#%ùž«¤(Æ×^ž!1×^>\Ïãã|Jô/A ܆ûÿûyaÔE½çŽZŠòGî½0š£5Z Öhb¦ùãØ£°ô0ÙµÀ °=?'U­ÃL±†‡oZ%½6}KÝ»(ÀÂâç·3“ZšbƒgËTt%¬w»¬ó脟‹FI8ÄÓ€4û•Pë!Ñý©F êò)Ñ¿pÿ¿h§B™yD Q'½®.¨TŠO²w–‡Or±TÍ5x(~ª¡š Ú3¢Cs?™2 kÊõˆ+] Ÿ{šr1ººaÆèóa²8½¶ ®0{$“$FÏ mº ¢~m‘Ž3|c­£®¼r÷+Úr„»ÿ*iŽ0D#ш…„(4È#D¯ïµ›žÐ£,_šM¶÷”ÇLªM³h®àhJX“ÊiŒžH‡&ÁFà8.Vÿ¯R ì!u ì°ÖM¯ý¯•T¢p“Ü•°_£¯/c%1ް·g¬0B §>þÚ¶Îù”è]‘ BªýkÜ ÑõÑ¡y‹ènQ×%tØDv9 z7—~ #¬6Et Tï²¾îç…}Ýu{Æ'H‹ZªŸGI•Ëj€²u7<«îW’;4·¸©Bµaé*†b\ý_|¬bðåîxt‰¤I•ï¾..¿æS¢×@(iè.üåà—·ˆ^$êšÕˆÞ5‡Dw þ±4AmR‹ Õ]=F¯‡2õÖIˆ~>¥õgæ0¨Î½'ÂôÚ¤d·_{=f6Ló“\gÚ_E1N£QÓXô¶Ë§DwÄ~«„[³rðg¢[Šèj•Žï8îR«£ðT+S½GíöÀM5„³øÖ}Ù»’z¸Ø€ýà áÃ’O/$c*†';Í(ö¦àVdߪW¶¨¥RAˆ@&z®½ŽgÌ5‰¦¼cªpšj¡!+üIÖ6qÍdتäÑ€>}$bÜrZjI1.¾IwX(Ø~kJ4[ó ÁJ1ú©n Â=˜ZD£§}¾%º ¶ƒ@¸ŠF¼u·6Ñ€Âó׋5å³TÒ¿\`üÜPÉ• ÅæcÑ¿äƒ âôZDü²6fn–µºiý…CMfÖ•¥ÇÖQÝ]Kþ±‰dÍ!ßuù%ÿ.éx çA <Âp”}Á¡ÞC$QhÈDÏ9Ñí{Ã$du V˜Øº%ç@5ãSA çÇ⥎–QáEæfV`TéënBŒK¤ï]§Bmá•ÆèáôA¶C!ägTÃn¤ƒŽûX1Œþˆ¯1'‚Ÿ“yáƒVxOMá}å·îÒI-!ôÃjpÊ«Èý“’+ËÑK[`5®Ã'ÆŠ 3÷B‡·&&ÖÈõ¯7< ÷Vã<ìÎÌ{Å›CºaÎ# ô¯#çñ=¼Ÿ½Â6˜Œ'F<Þg¢›Kt P­Šÿ Ñ¿Ýfݯ±’+ó§!kÔF2U…Ç닞ˆU÷3)Í¿03½fÂ0s êEe2ÍË_¿š&YÑÇ$ù]mE'lï-iY3;㞇;Úb6ã.á*.à/¬Äwh ·² º¢ú#\’’c¢ P°;-ï+¶Àmõç=É•©YlŽÊÕÿ¼#Vº¯'w`æ)uØØBil24•ê¸dÝ©î‚ÃBwm:]?ØØý3¼*У Š (\à éÖ!Ltó‰®Ê¿uCž£Ò…¹p1ºúØd˜h<1‰°ÃÌ­;jvÚ¬h˜Yím-1N£ÇÐ÷wí›”ƒ‰n‰]jM ]S`+¨*QõôeÉ•%Ðî_ W±ÀÚº¥‰ÓkóÈ~µyé5B÷FWŸfõí9‰%ÆÎhƒ 2 l< Mq\¤˜G/®¹:ýÑ™è¹BtP®ÃŧÃLËc(!²ÀþI6©å:5:®6Öâ;ô,}çb†¤zmQ[³;̬j/3Ìî­)É´¢"=‘\gê“ûåÅ8ý˜a¾iâ3• øLÃÜf¢çœèÅûC “Ô,°¥Ë^>/·À*Njq†ö½/bÄÊÁZcÑÙfö]5ÑvX†ÍJ ¦©Yýĉ×}zl©ï¡6ôÔ¾Û¸«éâ–ÛO—ônÊÜf¢[Du¯á*®*¿IÏ¢”šö½ù—r®éfv˜±ýíÓ”d• ªœ¿žjÝ·'IBŸ¥‹T­ÉÜf¢[?žH‡~–©ºôØøXreñô§bŒî çfÓýÅWöWZÝïÌÉ´¢|¤Ä0Ó—°]h˜YR•|­%ÆIˆþ”|Œøœ©ýü¶L‡7Š^Ð*6Òe‰^(òó¶HIã ¯ûÂÆvs§FI®ì4•¼‡r*—õ.êUþë¦8½v?ip/s-°N£~zíŒüûëâ±É21.Žú¤Û®Wì0cÿþø+é,Æe}°§ŠË؃o¡e¢›Gô ¢Kóè>NOŽÉóèAjyt 0¤m.ŽMΠ­15 ’·6Xë%I¯)Ñ÷¤jÏ8Ýø¡~iâ?!“¯;Õõa~?C LÀXŒÁXL@W+M[}E·îôÙfasHïÂŽK®,F.‡“ÊãПt1ˆ‰>‹l6˜]½¶þmEÃŒWãíwÅzA ­ˆª4T± ¬4½–BGŽU(Õ‰ù«ÈÏb\ˆXŒ“¶{¶›3-Rre!4z1 ª}0Úy]¾bÉÙk„×[ 4¨T¯¡C¹{—ÅvÜh«füéµ4 Ö2÷˜èÖO¯ "!Âê5—îR1î9+nÝÝa×þûp±Ò½5£ô$ó¦©Ú4=ãgf–Ç{%Ðwô˜[ÇðÔJØÚ‹fHcôáL=&º… 3í/úJµâ~‘L&Òk'©øC¨Ž÷¶ô|tÖ¾#<£‘"×׋q²2ÕXz/C·C­ù8aGoñtèHè«oU„¹ÇD·¾6–ÖŠ‡,–*wIf˜‰£-¿ÁFeëYÞ/aB{Ó«ŒµäH¦4dÔnfl°®¬„èF º¢†5ÊT ôh;×±Ì=&ºeŠZšÞ¿Æè½}±ɺCceØ;Ôjÿ¿qd ýñ¶÷…«âÝ7qÔûæ¦× Œ‘”¢"Æi=Z/z'6ÀÒöðz}ÓºÒ=%à#æ=çD/ Øš—¢f-Yùìe«X`µÀàF±r0•4[Í›ü‡wì}±aæ£fcÖ+:áób! 1îuÂY8)=?×Û6qQ =WT÷j•n Ïq•j\EY5 ìUª}žªDï(rÚ¥n°éôM¤xEÿÃPöws 32gœ6žöYÖg$lpIº‚¡zzÖ‡W†öI6£È|Jt T/ÉØä“T4HHtéµãäöÞ*—¥†´#iýsÓk²zôœŒMN§ËA½Z)ÖL×^žÁ1ºeˆî„B((‹#Œ6’£Ÿp²Ø«²u—Z`#iÎpª¬çÍ3’+‹¦%Ó`§Â€v¨Uõà-±IåAâžæV¯í/Ol˜–a³J`Õè?2$BbT½øI3E¢kç|"N.øQe†psÈl½x¼¸ó 9ù½Ú+º¨TÿÁ}ɪÒܺ—~÷øÉ•=¢nÛàªrYåáÚt–¸#J«=ÊL[kóû TqéÈÀuDc“ÏN;c”lÝ[Ž)nÝ ´ùíœA\þ×± ¥;¾t”ÒÀnÒÃŽ‰þ²Ý °=?'U-½V¼î‘ër ìK“^3e˜YPê{AóÍwÝcBþƒzôxšqµP­Š/ѰQwÛæFÖ‡þ¦æ:2Ñ_B l•ÚÄþ¶‹Tá¼Õ,°¨Ò”R£¡é:Tœw¢uFYf·{^ÝVÙ0#‹Ñ£éMÂ)ÅÝ7'ç¹ÝÐZZ•űšæQ9ú2ÑóZŒžHý½à-öh|ð–Ô{XÑû=z—¾+à° ­¹1úGõn?Éý=ÂA5¢§ÑÑÐV 3¶º-oùvuÖ\\*¼áT×€>Lô—Pu/ÑïhðשîE”œD¿sB¼äÊ.QÕKj£¬m~¤JG2­1·K›löZí‰nØO@Víð~‘¡ ìSÿq ëÑm†| |€ ´è’kµê/#Ñ—0ÑóÑ]ÇËÔÊTËyÜ>e­ÝÒCuØÜLb˜y2²¹HŒ»](]’\ˆ¡v¤Ù§XÔâ‰S„gN¦Ý'Ë”kÉDg¢[dë^»T”XR»Om£¸V)F@ïþ 7•Ëzjm»'N¯ÝLêÙÏÌSš0ÌôpÐÙ68<Ì:Î8©çG20TÃDg¢[†è%¢ÅŸ/޶N‡ &ÚiøÒ@i}ùµW²X´ðáe’º8ÿ½=§ç#¢Û`fc¦ä˜Ž:Lôÿ"FO ½“a#pƒxV?qUre ´ïgèU¼¡áU{ó}1Ñ/'wd&ÑuØ,ŽÑt=¬ÁõÚ‰o'Hĸxß#•Å8iŒL­¢ÐÃ!½öÔ¤¾ÔGpt%z2Ñ­At=P¶ó9?á9nÓ['QRpÝA餖kTï”Z_÷llÝÿ´l‡™^„?³6Ì&¹EŸË8 >J³[µöën.VÈ‹Z^Â!‹êDwÀîñDdÌò ¢Lt+½àp`Q’š¶Lé«çäXÅI-ö@Ÿ>É21ÎÉ\1NãùÉì¨4Ì֥ó”§1VãÇ%‚èÍX|h›ÿˆ.ÞÂ0Ñ­¸u¯évS²¢¿yJ¸¢›ˆÑoSóƒj³×¾Å¥oKÒk¡ñ³Û›[½6µrÈC±aftšÓl±aFÖ×=Gª»K¹bÕÝ@þ{{8Oe¢3Ñ-3©¥x¬¬Luÿ$aŒîüá:_É•ÅÓ¶_Õ¦©º ‰çV‘ë|³ëÑçõ yŠ ÜSKmlrM+ñ#Ô^iŒA}ýum]˜èLôœ½ à:iK”šên?}z„äÊNQ‰(§xe¦;ÌX0FO§_R\¦ΨÇôÆ©²n·iO|}”qçæ¼z1:=÷‰î¸Þ¡–G71ÀáRË£WFÑ7æ?M“µ¼ù¥Ùªû®Â]£>ñ ¼hì[†¦8.„š8®YÑ/\èu÷§)¨c¢3Ñ­?d1¾ÜÁËæíüð¸ô¹Z G ì;¨þÚ¾ÛbrÝJê󑹲׸ªÄ]`»ö Ä8g‹Uâ­£K«×RéÌ‘êž=™èLt‹X`«—¾%yn¿.…£@à¶[49ZreAô­"Ña¢ñ„ùÎ8hJ˜)6ÌœèÜYp¯¬)ÆIcôXw»@Ã2Lt&zΉ^°?:?Y­z͵˖‡VÙºÛý»KZIÍ'‡Õf{Ý%1º)¢ËĸD“Xd’ '¼)äÍæLô¼GtàÝúŠøÊ¤õè&Òk§ÈSQŒ†Nå®]§×‚ã§u°dõštënƒ53žJ®Óð$Ê[QŒÓ7üqš˜èåÆDg¢ç‚6Ž6Í€@i*åsá¢4½¶}¢šÖºÎ#£Äéµ ŧ›™^Ó”ûpYxšJõÚxݹ’&YÑ¿Ot¢¸¢ÛõÿþVº8q·þœGÅLt&ºEVtŸ× Ïñ€:D1Yu‡¤ØÛÔü<¯ì éµ]æÎGÿ¦ú“GF•ôšÆ§Á®;IÖ‰Ñ=q{’äOÈ:&:Ý2D/ؘ—¢f-YáüE«X`Ë¢P‹)²‘Lµ¾7³“º‹ß3Šép¨¦Ð0³Ú›ÿb\þkÉDÿOU÷ŠA·$…)µ/ ûº›ˆÑ¯Qý“jE-ÝñzÅ×Å1úÃÄ/zX2FO§Óç¨Y`èó§ùŠyt‡¶¿…šC2Ñ_Ú¾šˆÑ¥b\,u3(Ï^sõܾQ£ûííæ<…‰ÎD·ˆ¶j ß»ÃLå[ðR³À^¢ª—PRå²ÊÀ©åïAâ+Û›^å'ó¼î„¥"$†™ ;†¬-'ã"ž®®aaÔ=HÓ±‰ný=‰Žü½`þ—{Ó}·¥ØÃãÕ,°VH¯i\FŽf‡üÐT@VMñ–ËŠ[I¥Ò–ˆZýç£{8œ\À1:ÝúD·<–¦Ôº‚²‚“èÿø9^re¨Ò”RÒ¨ ïôu¤8½öGF™I–5Ìì‰nØ/ë=aHñ  ‰÷:á,”f!þì+Œ›©^úë™èLôœÝpZ³2^£‡ côrEïž²JŒNx½êÑ«OÉOp\KÑËlÃ̆–jc“õ˜Ù0ÍÏ:Î8·+gœ¥ÓYÇiÆåBoAÇDg¢[bë^»dôuá9Ò»„'|}e^÷GÔm\•®K¯]æ–áE¢ÃÓ¨BKóþtÔk·/È ŒÑß'‹qe¤b\G£f•®³¢ ’›ð(dÐìW33Ñ™èÿŠÑ]ãÒa:˜å±Ÿ~^¤¨×‰±´ˆ Á«à¬dµÅŽ ô¡ÑY?Q²MC'óˆ^«óÔù4/Ëc6Õ%ìŠqœ¨ §/²<†Qû×ñŠën#]Ìç´™6Ž–„3pf¢3Ñ-AôÆšò ÷,W²‰Á›‚ß,®¹S@²9"Q jDßÞ‘R%uqŽfzì´!wò„ýB¿úhùP ÁQ Û f`kd³^¢çb¢3Ñ-Gt›„±t€öfq  d—€f‚ß,¿è.ÝÎò¸OrDô–&ú®Út‰dyܣބ3BA­¯‹a/ERXG8ùÒ›„sjbÙĬc¢3Ñs‡èv ;Åk* &º7|GJ®lÉËDt67¦¡êÞ›p^@V¸’Øê›@-ÕÅ8&:=Ý·Fä¢k0OFô^â™ 6˜$ú^Ñ™èù—è¥ñpDÞYÑ7š ºDu7IôÍŠ­¤˜èLô<@ôòðS"º=ŠeŠW…QZZ}á…¶JŒn‚èÇ~u ~6Iô¬Jl4ð‚´lQ5ó ;£Ú k?=[D×Â)ó…±S¦ ÑŽ/¼Üy—èÞhgZ4DSØ(€6ÿϼòß½,)ý\@cÀOøE8c~yΙf ¢ï4At± ö¡«ÁѳÚöÛ`+ö¢ €2x„ï#ø…»ÉDÏÑKa+úZaê¾ÃšnMÞ%úP„£[lÁ P¾˜øRÅè7•ˆÞgÐè‰+l‡€Âø¬Jt –*]‡YJbœc0ö<° ½o`#j0ÑÍ%ºÎc 3nâuÀyÁ•w‰þ:Æ£úâ3ØpÁhûÝ ¾Š[÷¢™[]'Éܺy*Ö úÅ­{vbô¬ÿHhŸýéÒf>ͳ¯˜èfÝå2Ií¯L_“ʼàRâýå‹ÑÍãÒé]¢Kt‰nP0eXsëž1nkNÅ8?ÚDëim }ô”ŒLtã^"¢—À=k=‚š’U  äMÕèгž'J‘i1ùo¢§S2%S2¥PÆ‹DߤØ3îÑ7‘U¡T‡¼¨2­$#‰n-¢_¦%´˜Ñ :DÙ!z1ÜùÑCh}IÃéKú‘6Q¸‰FÕ©3]£ët‚†PAZ™ãôZ–JÐC""Š£ÎT™Bþ!ºSΉþ'ÅR$Ý¡ŽÔ€B˜èLtë½dÖX×¥MÝ·ÿ!ú”Ìÿ×cäDË-HôšT–ºQwjM¥iEæ8F%oú.ó³n¥"ô×ßD?nZuÿ‡èéÔ‡ªQØßDß¡^½ö7ÑõTÞ¤†T‚*Óã˜èÖ$º]¡Pò§µTŠ˜&z)<øgëþ%]§+tˆºRÌm¿eˆ^ƒªÑw4ІQSj‘I¸œ¨î¡äCýè]¡ýÔŽZRˆé<ú̶î.4›¶ÒZúš*Ñ 2þMô 9ã6=¢Õ´œ&RcêJLt&ºµˆ>šl©Õ£*Tˆd®ÙK¯Ý"r§ªT™Š‘;Í'ƒUbô@zZSœjŒ¾øo¢W$WªJ•ÉJÑ&³œqãIOÕ©>Õ$wjDgþãN*v˜É2F?B®4‰ÎD·Ñ$OZLÛh¢×hM½?¿¢ß «t‚¾%/ÚbQ¢x–Ìú”JÑC5¢k±ìÅý*§¡T…NýMô£B¯û/ÿlÝ‹ÓyФ0ºNݨ&=°Š÷ŠÓ&:Ýú1z ¥ m ìó1z 5 ¶”d¢PcªFÁª[÷?ÿ£ûSEö7ÑeOŒû{ªÍr¤¥%ú!Ê 4Š£idOÓ˜èLtë½b&щ&’ Ý0½¢?ù‡è“33žšP#бX½U¥‘ô}Jõs¢ºg)ÆSêI†l×£?Oô­dGóÿ&ú.Øç”è6Ô˜:Q{j@E©;1Ñ™èÖ#zºO‰G7© U§`3bô¢4šB)ˆîÓr¢á”a!¢ÇÑ÷ÔºQ7êE£èhæÕ*¥×6ü³uJaDwé'*H³Lft˜÷ÑÝh#¦c´šj“]û›èë8<#ú-ú™ÆÑXKSi¦‡‰ÎD·’ç@oR+z‹|¨"m6-Æ•ü[u¿C¥É“ªR*Oeiù½Œf2 3aTÜ©*U!ò¢¯(êo¢Ÿ j}ÿ㦓'U¢ªT…ªPG:øê~<§bµ0Ñsè§iM¡)4ÖÑÃÌŠìåÑãé/ÚM»i7¦;”òrv˜Ùú?¢§ÒñgŸõ~æÝË ^„Â.°?ý½¢‡ÓmºE·è6ùR²©2U&:=Ÿµà¼0P{‰Þ·PV‘èk1ç¥&º °Rñw+àÞz‰‰þö+öÀ5|Gˆ^„š‘)ôÅE+EÒy’è-q^Šã•XøR˜ƒµŠ¿é»hùý{RìiZ—s,™åÑ‹à,>V¦ãålJ¸¯ÑßÂ}¡^kjc¼Tycœ[DŸŽÊ’Õü{g˜UõÆñÏ6z#FîîA:¤AìDRE B@TDÐŸŠ‚Ò!Ý-=Fw3ÆzûþþØá²fŒmlxŸç¶½ï{yϽçsÎs¾ç9ç4KÁ amçn³²“WR èîl¢W?Û™½±fMüA¯Í1J&00þ9)ôÏø=s§ž¤u }˜ç•…m †'¹Awco%ð³Ï²Ÿ|6è÷¬Ç(›ÀÏNJp`œ\ faG²¹“L›NÐßd3YôÉŒlN0<É zfÖ20Ÿmà ؠ߳*£b?ûe‚ãäýc'0íуýtNÁ ¿Á6ÜôÉôlL0<É zFVóN?Û‚C²A¿gåñ¢Z?û9¿'pŠ'¹@ÿ Á;feo‚'v’ô—Ù‘ÀÉ#WÖ%žä==+x?ŸmÊŠØ ß³ÒxñT?;†?xâtrþ.+¸! »¹Ò&.èϳ+“G.¬fX*Ý•¥|”ÀÏ6äX<—Bý'@/α'†ŒàïÆÉú`Ö$X²Úžà‰¸­H¢€žðÉ£´,O0<ý…D½bZ1"Ÿ­‹W<—B=¬uK åH¬ëždÃYšÀ¹ÜÄ݃—èg-шè½ù /ÞæÍ_ËßämNòs„¥‰åá‹J&Çxå~¼­yRªÑ¼ïq”æT‰áµyM62Ê ødÜ^…&âc*ÅòzuJÆ:Ðs hŒå¬É"¾MÐw­ÄËœ¥K”³"Ã8J“Xï}u ?¾ÕlŽd'79£yªsNdáµy6>e-ðÉy.æó#¹by5w”¥ ®ôå ʬÊ—J½€2 q…>Ñ"¤J¬áŽÃm×Û.‘ÝÏ%ÄåŽËíy°K@?·û¸„¸øEÿ{ºÛŽ·ñå(ân›²_ÇÛé¢~Ú?¦+ÆË}]B]|“¤œ~.!.>ÑÿîzÛá6¾ìH¢ŒÊx‚Þmâ@4?Âu¼cø{|üwb¼æ£û nÄòÊaVG»‘ŽtÀ»„è¬N§B?«ßULxólŒ=^i–:†uÔoúSXþg¤ßÎå³{å?µPï(£8BËÌÐÔe§‹ÞˆrG+gr•2üÿzYiÄ–$Z󸲯›\I¯5Z#5Âò‘é÷‡ñ„òá¯Qæ@¦Ä£½Ì§®¾ýt-Ök½®gz¨:†)$J“CX¢Póîàh hL»oWô¦\|ù4ž©¬ŽtäDÝ8"²”úE½ª´·ëö̱Ïó÷u¼ÚAÞ©ô£j%Ç‹¼–ÀTë$´ÜLË4*Žðh­òicй‘—õ†\|ø8ží¥+o9]í¤“=H¿é ]Žñ¯úŸÖª¯žÓ ùéˆ>P7ÒEóúMÍÐËê®±:+I:­ Ú­oÕM³%Ö{êªáÚ©oµÓD!+Õ_]5H[c„Ý[ät…¾•‰P]e´<–Æã>èÚ¨m ŽÖ¯Ó9íÕ÷š« ’Nj¶~ÐAëJ>Z©o5]‡Í_Žj›®k¡&뜤š©ï´KçµÞÄKÁÚ£©úVëKõo-Ç‹¼š „*':à]ÇÜǸ@÷ÑýénÜÐZ]ÑFMÒﺩ0ÐTÍÔ)ëõkZ¤¯5W§MC¼WûuAó5]7$ý«4MGå­Í¦+ô×&}§ïµ+Òݼo[TC¥M¥‚?¢ef¤Ë>±†G÷AÔ ]Šlž’¿Nh*XÒe­×¶†¿þÕJí6Õ!LtU¾Ú¡M tIkµY·tMçÍã Õ)ý£ ±~—cj+§K¼ñí¥NÔ‹¥ç{]Ï(@þ«Rš)® V7Qs Ò Ê¥÷ÔYo¨¿<ÕGA’néE•T SU5×I•CµÕVô?y©º*뽡ºÊ¦)’Âô «‡>Q•Ðê*k]áMLJN!.Ͳؔˆ{ ûêUÒÂHx«Œºª±š¨Zë/µP•UYí$Sw•R3ÕR-$}¨òꪚj¢mÚ¦Ê*«j¨N*«ã’5N%Ô@MTTïÇ nV q„V0Ÿ\—¥µ4Æ&íèWÔKu¢ÄžTH=Õ@å©×õ‹©©Šª¾Aý€žQ9µPÕÐfIÁz^õÔJµÔNÞZ¨âªªæjª–ª§’ni Š«‰¨¸&E‹æBµPÅãÖ»¹ÐÇñò³:'è¾úDõµ.ÒkËUE«žÊ«°¾Ñ:5SeåW/ùH’Né%•VU•RO‘¤çÔS/¨¬šê¼Öë)•Tuu׋ê,I~¯òª¬rª«51|“­zJ£íC·—±ö|¯«™.j°Êëï(¯«»<´QR€z(½f(LÒg*¢’f¨ ¹UR_IÚ¨Œên´ŒTTÿJ ÕX9jª¤Ã*®1 •ä«.ji¸¦¥*-v&0#Ñ“Ù1+çUR“t[}U)ÚüÃqåQUí’Ÿ–)·ÊëOÝÕaUPI¡¨rÚª]×UÔIÃ䤡º(ÝPkÕÓ1h»Ê)ŸŽIZ$OMÕùëgåןѪ1±ù‘§™J³4oجš´Ê«•:§Žª¯ÝQ^[§tj­ãòÓteQ]m”Ÿ6(Ÿ¾–ä«j¬# Ô9uT3ù(XÝ”N_ëºîè´*«›.È_‹•[Õu]Ò×*¨¿å§;š ÂQ"Œ@ý œÖR€ßžãuL‹ôMº¥7c¨0 •NMµAÞ¤jª_uJSä¡?$ù©§ªjµÎêUV…(HÍ•Qè Nè´jª…öè”Æ+‹ÊOÒTåÓ÷:¥ÃzAUMH|¿Âü¥b[3ñK³Ì3lv´€ë UVWÕÒ Ž: ƒº£uÕ3¦wúT…L°\y´]aꡪZ©uZ§5ª£ž’6)«~5QLõ4#õýÊ©)’f)»fiƒÖj£ªh„²k–ò†±ô:{\æÆX׻ѕˆó*¥wõ‚êš§¦ùË_’Ž+¯ÆH’n«Š:˜ûò²š*PçTRïéš.ëšV+¯Iz_åe¶¼šnþ‡áÊ'/…éEÕÑI]ÑR½½úÿ™(ÓLy™åôi´AæNyêsµP;3Ž5å ’´NYô›$é„òj¨$ÉO ÕKÒNåÖÝÐe]×yj¯ÂÔEÕuÜv“Sÿ˜'ÔCUuCwÕ@ÝtI—uUûUL_E?V¦@¦$øPdµ:ì(¥%Ñú½µÊ¯ùzNuc%-”›~7·;«†™éŽJúPÒVåÕ/’B%MR Qˆšª†¹‘‹”ÓôÚ>j úò—êêU#WíWý©ÂLUî0þz„leOf»ÒóõVeÑ0†…j¸JªŒ*h½ÂÔE] Ÿ«¼¿¯UnmUž‘‡¯©¡ ÓFåÖZƒN- 4×?¡"úAÒçrQmóþ:jmî«Oå̬GÜûÄ•~Ñ•ˆó*¥ìÊ«õ–Ö]õU_¯ÉGÞ*¨Íø½®ú™wôS}j·rªŒ«‘«®òjž¤¡ªf¢• Ê¥•æý3TH^òW#åTcSºzÃjN}ô‘2òC¢M3¹1ÆÕ·o”iÅÊ%•Õ1óûaµPÕÓ0…jriƒŽ7ŠE ZéEI‹”AUM9k¨€6+LÕÑÄ ³•WG͇©ªnê¼J©)gå1 e¸ÎþºÒÞ‰·n”¬Kò†ÍŒ­ST%6¿T?õÒëš!éOåÑ>#·Ô£Š7Ð Ióäªfꦮê¦zÊ­} QS=o’¯UDg¬ºžtRUQÝÔE]ÕNnZÌ;¡ÌAL}Ä=¿Ü›.Šÿ†é Ñ— ’&omÕVmÓ-…¨‹ºÆú…ª“é‚n躮ë†|%mTƒT€šYŸÜ­š"iŠri³n麮ëºn™þþªúÊÕ—1‰°ß¸‰¢DœW)½¬Úê¢ó¦š¥/õ¥æ)@ÇUH?Y 0Ÿè¯ú Ð~åÑ{ÚªMÚ¤MÚª+’†ê)ÓDnWN-4ïÿZä¥5U#m0ïß,/ó„/éu¥½ÈÓL.¼åt5ò´âNyj€Êk iŠ.é;}¥/õ—¤uÊ£Môšl@o­%-‘»¾±Ê¹]> Vgu3þ/Ê¥}VXU7uIeõ¼¶˜÷o±ëcj#LJÓR€åaFÔ…uÊ£gUL³Ì-8¨7õš^Ñ4I*¯IÊ8ªBšoz©†(i®2éÍÐtMÓtÍ×u«©z™«~«BVôŠê)@Þ* .š©iš¦éši%{\V/¹ÜaDϾŒÖóuŽÐó½®gä«é*¨Ï¢èÅÁq€.}£è+”S‹L¿V]õå¯[ª©—£i™^j'§ËôN$œèÌÉû¢MxÂL¨~T!ˆÖG Ý/EýŠ:¨˜ÞÕ§ŽúEÒ嶄ʳj¢zMÔD95MRˆÆ)¯ž×$}£W5TÚ¥zâDœ ;ÞʲÂ3lŽùÖçUBßJ:¨ºjEh=®Bšk@¯cÞOõå'išri°6é_-ÒH“ô®º4Q9õ¶æªJª¼$íSiµÐbý«õ§5I_ý뱫´–™&ížê¾Våôr”ùšµÊm^\“L‰ÃC÷}¨¼£Ú§Ÿ5Vw¬NèAê¯|¥Ùê©‚ª¡ë’–)Ÿ^ÐÐJÔ¿R¸n´5Á;8’n”4øxßß¶¨Š¶K ÑTUÔÜHbÝ2U6’…·ªëS:è#ó·ž*¦*ª©Š,««Þ±>{@UF54XL"ª¯Æ¨´Ê©–*©…Žk‰JˆíÔNôò•eùýž/âD×Ãf¦û›¦ jv\xpª Êoô¤Pù+X!š+Ï0–S&Iž;ãÂ5ø0ݲ†#¾º©´¡ºi‚ó0ݶ"™»æ{‡'>{븮˜jï§ÛÖ+a’ä§ ½ªúºceLœÖ1]oüÒ“CƒŸí4Fw¢›V÷㣑žH°nZ¦ûwãŽÕè…éš¼tB×MéîXå /§Ÿt[Mô¼uU_—.ë–F(S SS‡ÎþÀ…e’îꘕÿ䥑n¤Ž™Û¨cfüªSVZŸöj±Vë°ü%…é´.Dø´¿¼uV·ÕD/›«†È[+´T»uEÓ”;ŒÅ Ü|:^=߸8óàn7õ²zë#uWn ‹ÒœÜÕX¹3;‰öW"8]ëk6à£ÚnÒ_ZªAòÐ÷1¥'%Ó4S,ÓŠ‰e+5NË´P/(–DËÏì%—;|’ztöx¤('ÑÜ¢%:¥Ã®œ&ˆ˜q7J™ƒ˜åÐó¥»Û?IªI¨¿ž×›ú5ʬï5õ“ë]Æ&ѹž÷•ˆ.§¢§$ŽRwÕU 5ÑQ²àLzò'‰ ›Æ·IëW‚ó£ÙzµUÕPký…/µ•ãez%ÑÆiÁò2Ë-hÌ– &Ôæ©²*ª´*é«(Š÷eõ–ËF%p§ò‡¨&ÎIØóE·ê$§«ôK¢ ·"[ö$ÕŠü@]Ó¥AîýôädŸfŠ"®&®ùëª.GKéÝžŸÙ.õéìqkðc¼ò9a¬3Ú§}ºeœë¥vrŒi¥$éù8Y_{’óݪ/NÒ9‰¶ÄŽnåX“‘4¶MO ¯¤''Ò´bòl2ª¿Ãó3kñÄ™Yÿu"™z½íªû6J‰o4`O9­Lâj¦*#vS?YŸ]>æ&‘‚ª¤iŤ³ MSîPþNÍ:{Ü|'b]ÿ•¸f±JƽRU“|a?&a5 Öy†±"Á‡]=Š1>߀$¬¬êö˜«¿'sÜ“¸I»£ÊÈ´$:“5…Xœ+Ÿ«ÂLWî0–P*ÙK—¹îÁŸ%Q5¹«qrfžåÉ¥c óõ¤S"Lzòã¯þnŒsM"q5‚n42Ù„ÆÇfebž{N,3:ûÌ$ÔÙãîù>Kww€®'z¹®«¿ÒÝe\OFK s¦§$‰q%¼úHÕß•N׺DØV"ñìx¸nÔûÉÑÙE_ÿ•X¦\|Ä:û{¾Ä®&§ÔEÎ×èŸ,:{\JD#ö&¾‘Èéɉ3­x2ñ›´íª-¼hŸl2êc77ÆÆ½[BÛËö¿RRT“®§jo¢•kˆ“tI¤<«W‰Øžéɉ4­˜xMšÑ’"?3…kðý;ëjG¸ÎÞ!Eàнå´*ªI˜Vª¬Øó8ÏéˆbùùÉ=ø³DÙ>ì^õO‰ÓLåX™/ln¢4iF7JªüÌmNtvH´…0-Q©„o£”4ÕdUþ°Ÿ±šk®ò==n%âót~Y‰0Õÿï[ý¸ú¨MÚR– ¦?Ù:{\VŸÝ‰‘u¤ÊóhÛ(%Y5y”“Pü4^îÁüH¾÷äÒ1Èùz×GR"|SCõwg|º»Ö¤]×F=ù:{\V–åž8âóÕhe zäm”’ªšø J`5¹®Jw—Ï£Îþ@ >áJÄõIÓL® |”&Í[í“+?3åkð2÷üÇ<ŽžïF7³ß÷ÃØiu•ó5.Å>9±¯|‚”o=+§ÔRýA\Ý¡:âx²åg¦psg\Bçž½Õ!|%×[:gº9œna5y|l¯Ê!¥èìqYù„(;T'|š)õ,çH€¸jt£äÎÏLááQç¤(ìT„Wü=ßÞòZÏj¦U*—²tö¸,?ó܃?·aªê›f*ǪüŠ“tï˜ËR6ޑã.'븂ԉƒ3ÝÎ46[G´}j$‡StÅ9?» ¬.£±SuŸœi&±·B âj*ÓR@¿7ÀùZ×Ã#s\AÇT\ahÌ¾Š‘Ny ÓjU{h˜êŸ] J„9åõÉZÎQžÕù5/Ò 3޹üÏ™]¢§(„iYRWü=ßšaóMϬy©Mg ñ¶¥DÙôÉ›fÊϼ¬!÷›4_‘ÛãÛ!÷{ Øqî9X³“ò¸‚ä¶ÌÏü…ü寉ÊÌO©Lg;"{ÛùF‘t7¼ú?™ÓLY™Þ4iWõ–\Sn~f ·²¬¸—u嫱r JÒã ’¿šLLï7@”Þñd}¢žœ3=Î4Ñzõ—«o*×Ùã²ô v¾ñœ6©³œ®$â1—ÿ9ËÇîÁtVýÃuv·'¬š q¼íx“A)8Ÿý”‡}Ù”æIŸfr¦»ÓélrLݺQ 0w>ËèWVi®%ÓqÉk.üÆü'¶‚Ôã"qxâëh7®ÒÑFõQÍ•‘øðÒ“už…1Gf2ý ŽÇÑê?PC«q”Š6¨nM9œägjÚ '¾åç0­mÐm‹?è‡lÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmГӜp¶Ü‰´ÌfŽþò$™-ÆÙö=-C˜Ï\Ë›ãüh~›ÇäHÅ¥óàUÞ´¼p‰ïém~‹ÆOÈT[!úD(go>ã2c¬rö¥ªlü,7 hhy=†pŠžÔ·þ’z³ãÒ³¤–¨ >@íät‚B©ø¹UIs£’êªN ^W¹ÅÌ'$bi—>¤z,å¬%w1ÜF8~Ö3í­<>ž÷Ý/o¨§oøÏy}\|y-Õ–,K>uý2¹¤rгÝX/ÝŠÁoë•'ô’!‡u;Ær^RôxÛËeC7ëˆGó#Ú¡ªâMô” zö±KØû ½LȹX·knƒЫ†ÄvüâmÕ·AOÁ ïþO€^Ú=±@¿+èõlÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐmÐt‡›G—T ;Ø ?èiRÌfF6èñµLŒ¦Å ôšLÄÃýQ@‘‰¤ñ'ÜHkƒžAÏÎ~ÞþOÞ“Iޝ_&ÅìDšØ dc|¥Ëhlƒþ@ÏFwÆ2’&¤rГò8Òˆö¸ñ<çù×y…\‘þôg^‘€k”wÙ ‡›'ð/søoÞÁ‘ŒÌa#ù€ºxñ6™Ë-v2›)Œp+Æz2‹E,á"ÏEðf%kù¡dg:§ùrŽÑ¸ð2ëÝÙÊ0 3ëÙÆ*æ°œs¼’l ?Ë!V1ƒMì *Pƒ£ ÇgÆp€rTâîðsy;Róó2gYÁ,V°™mdq†¥,c>uxŠýì`6Û8HCÀ™ß™nvWlà J͹ÈR–0‹l  Œ§N,{udNÅ ·e®@s†QŸXÉT*9˜‚/+™Ê8rFjEÛñKÅÓŒ¥ PŒ±Ôg+y¨Ì·,g!}Í©§Ï0ÜD ÙEM +#hÅ þb!/FØÌÙ=¼ÁÇ^ÊNôâ•€¢ìà{бžŸÉ x°!€S¤æv¼Yù™)Ð/°’8áÈK\¥+ޤeWitÂÛ};YØÄ%™Äa & èØËD2,f>i×8MK:q–tçÅqˆ´ j~2™Œ@U¼Ùe@Ÿ'²°˜ò±†UdƉ?˜a®Ð–S”šãÏÿÈTà³!7G<ÈHzÊRÁÀ 졞ÑvUÏF%JâŠÙÌÍÌFŠP‘t€+%¨J~ópñ “ù\FP’“´‹òb¦r‡ª@]|™Œ àÀ$Q€~\¦ð{¢€žo6à¤á[ö[Ù á ÇYÇÖ³ /º ÿCV‹ tñ¾¥(£P”Sô4Ÿ_Å|ënbz¬ O0ï*Èiz% èñgëÙÀzv±‰ÂTå*'¬¡IL ÷䜵åÊÇô·9hâÏÚ\³f&šr•Ʊ€~êæ]†;ô²ñ/gy›r´å|ÏêQ9Òé`ùØÉßÔ¤<ñåw€)ø3‹zT$ó9D[J1„+ôÜÙÃ{泃ÙC /A¬ !e˜Ày%9èïs”Ü`aŒ^”ÖãïÈuúu³)R¶ð½ 'Kp–~±€~œÌ5šsÅÙ ‡×/ÆS€Â¦0MäØoްÊ4Š1îÎ>7?×áŠý$ÝŒª²¿L_íÆ¾:pÒîèÁ¿èSÌó+ÍyžOÐßà"ML‰ áIÀ•Ixsš!̘@oËE Òo,Ð÷™}ˆªr‰®÷¾רüÎs—»s–R@ ®Xl}Àð1 bž¹]_³žŒÀ‡l‹6FïÎj‹ü0•“–¦xÙ´Ói™Çf2ãÆbEý.-ÍcÝÎwÉún3È(Š7]c½,g­ÛלK&t¿``õ`?£Ì«y9ÂðX@?F_ó®Z\¢™ zÐ]ùƒÕæI€N@1v2‰Šìá\7¶ñq”ë;ò=»)83ÖŒÑïƒrÂÁÙ˜+¼4âMÌ_|xߌÑ÷áið;Ÿ mŸô\°ê†®×9EKzqŠgŒxv:Ú¢ÅðfN@þe·º‡©‹{˜FZÀ™I$/0•µd2¹bÄ8?Fàda9îƒ~€Ìó>;È èZÿ¿ó'ÀtÖ˜Ô”n\ Š.œ¤Ybý-N˜ Ƭ±D«äýd´QZ>ŽZßôUnQ¨Çysºwf6ZºfÎÓÄLUà<Ï™ý^sІ+Ê}RN¯e¢ËØÂæÓ1ÞˆÛ˜BwÀ‰–Lã>ˆ4%›žžüĺð%È ´ã ëtÚt¼Ê ¶±Š^†žô f›˜@G¾ÇhÁY^f6[XEœãý}vDSÜï‡î¹­Ðý>è ¸jd/G&³‡ldb+ã¬l¯ Ýoš€8#k˜k)ï‰ ú§FuŸ=ôbt¦q”ùLŒôÈ˳•ù••,â+ü¹‘ gIƒЂs”' 9î%Ý㢂þ<§x†ü‘2çò±“¿¨NY>·Ä¸û ge-k(EFÚp–Q€³ØAÜhÇ)vÐØK~2ñ—éœdcô< P›WÌðÀ>”6ãœî c€5b¼7ïùïPŸžœ¢$Ÿ~f\®¿Ïü@'k°QŽÏø‘w)C/ÊÐûуü@ËÕÎýÑ,_ñ¯ð%ç“ $®,leÌ#~‹ä\½ÖŠI¼N_–âM½]¡%çLÖ¡±3¼¸³ÑšâÀÜO~c?ÛŸub;ß°‚ÿá|Í/ÖͯÍf²oæ˜QÖSìã(›ù›ÙüCv3FŸÃ&¶pšq²Ñ÷êµtäÀÈÆï¬ŽÔZÞ7§(§»:DªÄù9Ê«1¼ËýÑ,#Cø•ÅüÊVøúp–o˜÷x@¯ÀwüÉb¾§nÏ®ÁÏF ³Æ79gÊ[³¾y(gÀÍDjE~;àFEŠ‘ƒML 0%#Z¹iÅsÔ‹e^„ŽrSŽ4@?þ¥õèAÙcôR¬âG¦°…c 梙 xXëÇHšjJ=øq ¢|o‘;ïXóŸ6èöÆ©Úú³ÍìåØFöagŒº§½ñ„ º zбô”¡!õ)àÇ—§bœÇ´AÒAÏJžxÅJiÈEa²˜ŸŠ$ëÒªÄÝ Ïxj ™ÈOÒX?=œà˜6 o“Ù ?é ¿Ç‚X¶‰hYøŒ]ì¤#™™À.vDz¤8&ËFSóÏ=Úø4‘@÷`/ÅãÊ5YÌ^’›š,e/ ã}èf:ò- YÀ»‘RÕÿS —¥V¼¢Ž4 ùÐî¡n˜ ¹)ðÀÇ=ÜÆ²1–­@"ZGNÓ…Òd¦3§èôP[£tå$¸ÃÜ$ØIááAÏÍ(KÂbîa Õ(F~ç/ªR<Þ=ôÓâWFñçY£òõ}KãÑf9{YE ±’=¬àéxL/ñ7»9Àz^4`ƒ;è™ÉIQkypÜ ’d&žQì£n¸3†Ý ‹éÝ(JAë>§# Žd§("|—¼T¢ÿ0?Å€þi)Dk°šÑš¸ /»…ÙËh2ãNqöñYL­u&7ÅÉiJï@Ò‘–üä­ä¡ŒQÊzà!E+û–šñêõz1‰dÀƒ>Lfx,óÌ1‘ƒ Ôx`¿—Ø ÍêH• 6ið0eȈý8Hi2Æ»5ÍÆÏ|CWže·â<ëÕ=ÜÆ°ƒ¡læëh@ þ2É®®LfÎŒæ4>¬g5«9ÉmÖó?ráÄs¬å ™o–€¼È/ôeÌò•û–U)ôý|ÁTöq„)äøñ¦†Ua1¥(ÅR|9Î*V³;x±–A@^&²›ƒìâ³à{ÃùŠü-_ 4×׿…щ/8Èçô%3_s€Ïé„0pû9Ï9+™4ù@_E.êÓ²¦ªe¡¬é)N(Î,öQ›*Tf»¨kZH*ÐÖ™ì¹)NFjÒ>ž4Îd7mkV6°4Ž>Ý=ÜFÀ"ž¦ÿã(e€¦œ6Ë Ò³†/ÌàÍ©Mmfs¦T#-9Í'T¡+XNVà]üYF'jG XSèÿr‘O¨Lw¼ùŽ480ƒ?LÜ€³T'=M9Î÷Ô¢6m8Î7Ô¦˜Ë.:RÞœ¤/•m\b" ©£”ý¼Ù]ç‘@ÏÎn³J8;û¢dÊ=XˆÊ`Fp‹úÉ ú—ìáG¶²ozã4ã_³N9Kù€4|ÃE|ØÂF6rž[lãgrâÆa+ûÙnù¡la*{ØcB¢ ²*Ža‚ ú=Ðoš¥(Åðæ}  §©l@_Í—|ÄÚd+Œ¬f¦µ?Ìišïr;–AVJý‹M—ØŸ³”†h CVö™u“ì3½r#Î[›KŒg™qg«bŠ+p€q â :ò_Ò'ÀvÖQ„^d£Ÿr•¼ËPÆpßJC -ÏL†›à+=/S“–LæH3ì@sn&;è åKò“qœ¡К3”5#¥Œ2ó-ÛÉG²3™MÂ'†ãE+ÜÉÇt¶‘øˆP~ 1F258cíbƒWè~oó„´,cþAÅV2E8ÉZ>g"ø›ôÞã°•Ë™rA?h­®Š+´ˆzýh ç´ºÑ~ø0 LäsVqœü¸±ƒÏbùf¥ØÄÒ‰ÈAle Ÿ±„³´œøƒÙ&¥µ%§)C1¦q“5|Å×Ìä«ù†682oæ3޵ì àÁA¶³š©¼­miñ@ÿ’sfCŒüæ£A‡ÏØd¾íç¬%#á›LŒ%-®¤¡&gh |ÌÅhÛܳ¬aUœŠ§ úý1º»¢ñ»½ŠÁsm¬ —â ñ!ó11œêÀ{ì´ã_JýÞU¹DÛH 7â\¬ ¿Ãu&ò‘)ñ[dÁ±¤äc«£%ªG3±‚@~e#î8² è᫲Žò:ž7ÙÜU8Á[8¬a xpŒ}”€æ0è_±Õ„:éYÁ¬‚>Á€^†+`ËYÎ:|xøÄZÓÕòòÛÌUmÐãýSΘ ³±‹/€zœ3õ"‡ù"Ð=ØgíøvÏâ}^Š Ý§:p™šÀwÖ ï9®Ç zW.YÛ–`t ˜A/Â*þ1»>ô{»œu6*D=_  äí¨NMª1Ýd!Çb .èÛM^RV1hÅYÊYnk¬ —æ_ÑœV´¢-ðF°%F­3 ØE¥ê6èáà†2w2ÒŸK<Æ›±d!+ïd`þˆQ@w`$§iG&\ÈF-²Å º ù)Í&Q”|‰œ/–°ý Mp¥ ËXг4 ¥XÕb=»øƒâ¸’ž"¦9ˆ tO–s”æä§…biøŒ…ò†ù©hQB÷Ø@‹ëXÍjV³…¸“ƒÃ± ü't¿l°.ÊqÞqшi¥9ËÈX@ÏÎn&GIµÁÖ@ÏÉÏì2cLôƒþ ûøÕ¬â,#qœÆÖ³–ŸÙa¶(yŸ ¦Ê~hžHøV¥Þ¬aëYC1àí›Þòìæ×¹Ê1Ö'ò]}xÐs±?YÃßìá_˜Þj¼YÁZfáEUÀmfC(¶Y 6 ÙÂþb» ¸³ÁÜ·öør™ÃáÇâž^“µáD.ðð;ó èÝLèÃ8E%<ÈE.r‘ <8kP‹Ç"Æ3›ò”f^” pˆ9”¥3 ˆtèÇE†RŠ‚Ôà òÄzzfp‡Ñ´¤ m©ÇtLƒ[:Ò—F¸Z÷¦1oÒžT2A¢š ¼‹PÍúl:jñ½iK!œ€‚Ôˆ!<ÏB#šð4OÓ„zñžN*Ð]¨A>Êð:/SÜZÊíIzSìÔ&3†fkµ´Ô „x =oñuqÒP-†Z’“ƵvßÌÄK,`=‹ù’Z@fFÆpF©/3‡™Lc&s¬½¸“ô”c6è6èÝqÀW\"lå€ ®æ•ûïŠú@ÒGؽÓ1ÚI¤áWs´ÜÁÝÝý¿m6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è6è¶Ù Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û Û ?èÉ_wbpý¡´©ô=ÿ ÐË„œô6èúµXA¯ŸÊA/¤†jƒ7Ty9¦nЫ¦»ñ¼ÞÑÛÆ‡è ±~®øä€Þ>[H/«dË9DTÈ=þ  ¬ÏÑšký6E%S3èδe ý-‚ yËü6€—xeJ¶ÂLãgæŸÇJ|XÊ<óû/ôJ‚³ÊKƒÆOV)ç3-ÜdUΟâ÷ÿ¸µsØí¼/ÍÞ{î|ÖùnšÍÏ{öÓå‰)iÖ2ä‰)Τ±Üú¥2ŽÖ_œž˜r¦‰TÎØOnœ¬¿8ÚÇ7ÀÍÁ³ñ;)Їõ—tOèï<±Ï±6G(ÿ¨¯ÏY'ÉÙöHÖ‹Í©:¤ý/ƒ^Áݶøƒ¾ÅÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ=‰AòDƒþ_PÝ{°ì6¦kޏDr'Þb+9Ìo®ôt1xzã,Ïhy&Ë3Ïb¹›åî–g5žÍòì–ç0îaùýiÂ\ÆsGð"¬d8¹Èk¹§ñ|–ç·¼€ñ‚–²¼°åEŒµ¼˜åÅ—°¼¤å¥,/m¼Œåe-/g¼¼å,¯hy%*Qž—8E*R™ÊT±¼ªåÕŒW·¼†å5?ey-Ëk[^‡:Ô¡.u©K=êQúÔ§> h@ÒF4¢iLcž6§«†Ÿ8Ú”¦<Ã3×ú\rmȵ!ׯÜ÷|SîMyÂ}sžÍyÃ}KÞ-y·xnñÜzÏóm»çù·ß‘GpßY`gwÜYpW¸ÚmùžB{ ‡ûÞÂ{ ï-²·ÈÞ"û½è~Ëÿ-vÏ;Pì@ñŹRôLñƒÅ–8dùá‡K.y¸ä‘p/u¤ÔQË•¾ç^¥½J{•ñ*sÜrï²÷üDÙeO”;Qî¤å§ÊßóÓNW8]átÅ3ÏTM}šú4½óŒñf¾áÞÜ·ùÝæw›ßmq·…_ ¿~-ýZúµôkå×ʯ•_k¿Ö~­ý[û·ñoã߯¿­[ÿ¶þ팷÷oïßÞÿÙpèÐ! C@Ç€Ž:t èÐÙx—€.]ºv ìØÍx÷ÀîÝ{.ð¹Àç{ö êô¼ñ‚^z!ä…°—ƒ_ z%è…à6!ÕBÝEw9ÃÞpqp°‰ŽÅrÑ“q,ä€Ó|!Ϩ¿†h¨–h½6jƒÖkñõZ¯õÚ`|£ñMÆ7ßb|«ñmƷߡڡÆwßm|ñ½Æ÷iŸöi¿ñÐÐA㇌6~ÄøQãÇtLÇäeÜ[Þ:®ãò6~B'tB'Ÿ2~ÚøãguVguÎøyãŒ_ÔE]Ô%ã—-¿ͯFók1øõh~#šßŒÁoé¶nEòÛÑÜ'šG_îãƒßÁýbpÿ< ŒÁƒbô`ËC*I ÒUí×VÀšWòìrÏ0ÚQ÷Îm·-Š¥Áƒ tt–y^Áí5/ôðÿ(ìgíÐ%Ë6Û¿…ÊGG´DŸëÕàú× íÍøщòd·ñN˜9‘•Ò´äí4Ósm¬xºíÝ!aÓ´^'uWav}³-Y-Lwä¥úJ½BÞ(z0㟣éFerÚx'&ò™)ÆÓ¼™fr¶Ue¼›ß~+t’VÈK·Le›mIc¾:©Õ𬾡Mo•8šåo§ñô¤¹p±±LJs #ù©Ç+ŽÜ;ÔøækÁŸëoÐ ;´·-Ñzo?ÑzMÕ€Ð>¥¼²-wú‚—©MÞ'hYU*2òRç•aA¡}µ¯<4J¿i.+È®«¶%ÀuN[4SCÂÚÜ-w"ûÇoéE= Øx§K‹•èà0<ÝOž;j\è0\sµMçåÿÄVÊÓú\#ô‰Æh¾ÎÚŒ>‚é¢vh®Þ׳~•Îx¬OûoјÂd°§ÂS®9“•Ò´b°ëŒ\*žiçûvØt­Ó)ù>aÞZeQµRCyª†öÚ¼>4ÞWµK¿èCuö¯z>÷&—™ ¤Ű3]RÝh> Eyš7&e_]Ú«ù·B¿ÕJ“Ï!à­Uvý¦0ùk‰rèÍ(¯†(@aòÓù˜j}I×"”;L·uN7­Æ/PA Õu]T¨éã®*DA ´>ᯠºœêu]×>-ÐHõ¬qÑs»Ë¼C+Jâfãý$ Ÿž‚ÔåU§ Y—8ÜèÆë¡ô—éºBR5è]ø)5‹¢KlPÍSGUP#­ÔI½¡*ª¢ÉÔÛúBõTYµô…|%jˆFkœª«¹®Ê[/«‚jé}ª! ”ªÅj£*ª¢Þ:‘ ïU¨nêj¬^®}¹Àžtó†Ó޲d}b6¹²- òéÈC z8~šiAÁ}u¯<8Úx©ôÃ*¬W¢ L(“jk²þR •VG}¤¥ê«ÜÚ()XÃTDµJTPŸKòSeWý¬¥º v*§µD/+»šÊ_ÒbR-Ó/zJíu+Õà}KGµXôJpýk…ögúÍá:Q‘줱Qøo xéÈéæyn¯~¡‹ÿa?i«ÎË?UŒæ×Ê]ÏiœÞW-UÔ–(¯þ¡ôš&IúGõ¦B$U }&é_Ò󾪬« ÐÓ*«3’¤•òÐIÒ9•ÕÓ ŸZª«‘5×+¿V¤p¼ïè¸Vê+õ i|³èÌ‹FÓ*ä$­]éÿëé8áÞ4Ósmªxºß°iÚÂ3ðÖÊM•Ô\U•IÃ,鬿h†¦k½¤Ê«]¦¿Ï§™’¤»zJC$ÍWõѧ¥1ê.Oý«@5ROSÖoUÄ„ç¡ê©Æ Ô)T3Ñ(Ö;ʦïRèÌ÷Iý£Éz+¬éí’G³,qÏ T'·m1!ž×7Íäl«Êz7¿ýVXJÍÀ[«ìúEAº©±Ê£¿$mVcÕVM’ô» è€$é¨ ê#§ÕÒÛ’¦)“º©z«·zk¨Î*@ÕÇ\w¢J˜¾=L¯ª±å¥\ª¯7Õ[½ÕG}´6Ý?ÕzMÕÀ°>e¼ÝW8Å«ÔÁWìÅc¶Åk4Ÿ‘ÔãljîKÂ3ð&¤¨ ¼ûcôÛzFOë–‚uS7tC¾Ñ@ÿ9è‹å¡•Qpiléö¿*—ÜQ5V .©Œ†¦¨F.@´Y³4$¬­o¹“9Ö8O¦ (@oÛn.ä¥úý ¼RDÞ}Ð¥?”]ó"½è—TC­tJ! Ñ5Pp$ÐO«¼ºê¤®k’2«©ü¦Á*¬5 R¨îê n>¶™ïKá‰-aü+žÉ¹Áy ýxšÂd´ñ¶-ñ¼Jtpø0ݼð ¼5WÛtNôèwÔB"¸@-Ð Y¡{m½-IZ£**§nzNµõªüäti¡Ê©ŒjéY=­f ”tNUHmô¢žQCLÖrëŠöèg}¬ÎÕÎçÞâ2“Á4£8™ì™oÛ’ÞÒ•2´âm×™97T²2ðN'[Þ-Òý“=jQ¤i¯sZ¬Û¦XbÆÜ!Z£}æõSš¥5N¿êŒ¤­SÄcOj‘–ë‚Úª»)Í-ý­Ñ¡éÚ›,C—`]×~-Ð(õ¨y)ïötsJkJáfÏ|ÛöxÌ‘,¥ }&e_]úxÄ ¼Ô™ŽsBg,ý¢\fŠ.ùòÖné þÒX½TûjÝéឥ,Ùl¼mKIžÉÀsœeIñà o¼ú¹þÒÁT–7I•ôŒêª€^×d™óÑQ-Õ½RÿZá3üî0‚ÎT$‡½¥ƒm)ù(x=ƒFëWíNx×´R³4Su7Iñ¾#o­Ð7êÒøFÑC™9Ž¥;ÕìÄÛR¯€ž7ßs{µ‹]ü‡…ÍÕ–T“—ØxßÕ)ý£ïôVH3ŸGÝ—:~΋Ô$®vU±íɰ{xo§™žkS…ÓmýÞ›úÙ/@g´QS5(¬¥o™ãYW9~ÅkÔ!Ÿ·mO6òV^ÖUe¼›Ýîö­–?q{àê‚¶j¶ÞUÛ»Neÿ'Íwô¡IoÏ|Ûö_Í[xn‹‹jtóÕàñZ¤u=Õnˆ¨ËÚ¡yÖѯòÙœÒN£M(B&oÛl×ûx÷ÖºÒ3h¤ðRòÁºª=úŸ>V×ÀªçólM;‹·iNq²ØxÛf[Ì–*ÒÁa¸ëOywT;ß)àÍÑVKq{à…êºh>ÕsAO]Ê·Óõ'‡÷iMi;±Å6Ûy÷ð <—*œiã;8lªþÑIÝyŒ^¨nê°þÒgz1¨îÕ{2þÏáCž¥L¾òÖ*}£>¡o?œé/‡±¬F¼ÞõÿöÎ5ªÊ*ÃÏᢠ(ˆ¢a bj-KM1/¹JcCñRJÓ¨ Ö˜cQÆZŽÓXšé¬LÇ[š¦L5å%—Ù¨jj¢•YŠ¥ *Šh˜«TÔ¼ð›íND—3sâдŸýå=ïÞ‡÷åÃóž}{ÿÿ«‰þs-¯Íªïu©” J³¢[<Ž/ËɺŠèSØD@µ5Ép*:‘Ü«´ªŽPzO<ñôf*ÛÀ=æó=4q§èÃjIÊŠ =lE·üBEÆ>"Íq[züA›úøžŒ8]^µ4/÷=E_+ºÅânÁ»1†‡ˆb™Sô†ôá Òhe>ÿ$º?=Í(:â„ñ2%Œ /qK'#z0}H'ÕùýhºÓˆûx‚d‚ÌuûEûP…*¨T µE1—`E·X܉7é³µ¬f‹p-XÉnVñŸ“XIô†Ìæ+þE.ûx¸“œ!‡ÕŒÃ‹çÈ&hÆRö³†OÈ7¿ÍcøŠ×É%‡Rfá@ߨsÅÕèpTm¬è‹›éÄa&Ѐ@Ò¹ÀBø²€<ÚQ‡FÌ#¦.¢gPHY@7N±“rÉa Å$:E§€ý¦f#%dT#º?áñ´9ËF즩­ïchÍŠ>\甫ÏÜ”ùåä åi™–j¹rT⌛sI{´H/j®>61ó¬èÏ1…ÍV•zä°ˆã2¹8âˆãv:E¿½, “©éL³jEc/6gÚrŒ@_˜%³(Oˆ^¦®í–ØóÙJRñ¡§‡(HÕAѺUËU!鼦©¥Ú+A]¥ }cE·x”a”g¤<Â" Œ/˜Qe™ì‡1z]Þe­ùýÿ‘Áë"z~¬æ}Î×ôð´è©*Óíé"ú%¯”@ò¢Îëb¥ßèïu¡Êƒá²ÎKZ¢Hí»BôduW±Ž*OÝÕZ$½ª†§R]ÐIÍS¨žÑE+ºÅƒÜHÙÜMOþA9¯á£”Étý!Ž+M©l&H ˜Åô¢ ü…¶@gŽðéâ27˜cL¥3)ìåm3‰·ËˆíYÑßÔ\e+U¿ÑX4¡¤³”¢= i:%IÊW¦ú¨Ÿ¦é„$i½&k“S’f©¯êë!Ò,—Ö—•¬^&'Ý*[eºMÉ:í¬«0í¬èÇj¾dÏ2q8?ÒØÀnv°‘‰£˜ƒ?àMV³‹lf>Í:<Ì?YÏsx1Ò´ªC[Èç æšÍ4)üÝìŸ c ž=MaJг¯(ýNç$ÍW´2õª&i´J$}¦ŽºG/i‚Úè•4C ÕMÔ-ÕP+C/èm—d“®¢ÏQ 6hƒµØåÚ¬ ͱ¢[ft½IÚ,é„^P7EëVMÓyT„nQ?%)IIŠ×2IÓÕVG¯)zuP®6i§¾•$­R€–»´Ø¡Mµ¢[¬èž]ªÐ·úLϨ‰Ö¨L­õ´ÎéŒNëŒÊuQÒtݬRswËUèÉêí2f— ÕB¸¬·OSˆ6XÑ-Vtωþc È|E(KÒµÓó8©³UD_§p}$UZzsŒûqyn¼ë ]tY£!*·¢[¬è5'zb%Ñ/)U5A“¯8HÊWwµÓSš¢ÑJÐçUD?¬;ÔY¿×tw½ŸzUÉ[¦45Óý§4Åè>í³f,Å›†Î¹ñkãGÁfù-„ ÿ9aq îŒ;£yZ%IZ©7M—ú¦©XRŽ2•¢MЗæé%=¨ÁJ×r–´U³kâÒ—š­ñZì"v…ÞRÖcOk…Fi€b­v ¬ÅÃD³–^×Ñî–°•EøÓ¥ležÙûö ý¿{©¥ÂM¯²lS; Ö×Vt‹GiÃáëÐ*µ¼Ë½t „lVp/\ÖÑÿ/Ewk¶íº[]èFÛy‹u¼Ç^b€X œ“qEÜ€7#)d#¯ñ:[y ˆ €a×}å~‘ç²µG»+•|­W ·oµ¢[~íÑŸ–$òЀdëµ>ý¹Ö¼iO:/I¡@ÉÄ\÷•}އ}~E ûÎç$Iî=^k´®”µJ´¢[~u„ÒËdO­ZÜœMÕGµ¤øZÑ ÿU.(-f`%tEXtdate:create2013-09-23T17:44:21+02:00w£»ó%tEXtdate:modify2013-09-23T17:44:21+02:00þOtEXtSoftwarewww.inkscape.org›î<IEND®B`‚stxxl-1.4.1/doc/images/pipeline_randomgraph.png000644 001411 000144 00000052455 12350112610 021406 0ustar00tbusers000000 000000 ‰PNG  IHDR0ËCþŽvgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿ‡Ì¿THIDATxÚíy`SÅÚÆŸ.T(вD\ª (¸¡AÔ ¢ˆÊõªE…ë‡àwEq©(îá¢× µnõªHUTP)*âE£ÈZ-M›÷ûãdOºd=sNžçŸžNr’ɼ3ïïÌö„¢(Š¢š °(Š¢(ƒ¢(Š"0(Š¢(ƒJ¾™­@QE`E`Pæs@QAEAQAEA`PJ¬´`­#0(ƒ"0 ƒ"0(V:Ö:ƒ"0(V:Ö:ƒbÛeÓ¥ ƒbÛeÓ¥ Ê NM±í²éRE`PE`PÉÍʶKSE`PE`PE`P¬t¬uE`P¬t¬uÍJ`P¬t¬uÍʶKSA³²íÒÆA`Й°íÒÆAAA+káñÍʶK³Ò1 F´c(ÍʶK³Ò1 1Ƚürñç¶ôãƒm—6f¥cbëhÀ®o꾾鼗z`ÔmYù¡ãEŸæ]ÛŠÀ`Û¥Y阅ô¬Õ1ïLÞ@ê±òâáÑÊ ¶]Ú˜•ŽYˆAv¨Ð3×J0ÊŠ<Þ‚À`Û¥Y阅ôKKà¸Z]³àî’z`¼í<$ƒm—6f¥3C<¿¥w<¹Gçò:%õÀ8@³Á#'NöiÒøC ¶]Ú˜•ÎÀ(¹1ƒL6:õÀh\õW“¯ 0ØvicV:dá›}fÉFúGŒ»Ul»´1+ñ³°¥Œ¤ã\‘¶w Û.mÌJgô,Ôž#¹À˜„èLØvicÊ|Àð\#ÉÀXŒó#·Ò™°íÒÆ¬tFËBí{÷L¼ðúwvºWŠˆì¹&”•••UŠˆHÝ'—V‰lœ5úÒ½7T¿ÿ¯sΘüèúOùªäÚÑc¯X°=(iÃãSEDVß2~ÌôﵤÝ/]=zü݃ù´üº5þVÝñ¸ˆìzê¢3&- ôßùò£Æ^ùÒ¦zEý·V8ãÂᦿµ;ìŽo»høÄ§«Â±õßÿ3êŸ/ïL&0<'·­ OÛs" Û.mÌJg°,|9 û¼9³m99–á"RuD`£ÀÛ"²jfW`—”¶Ða»ˆˆgîþÚË9SýNÕóŸ£1ä¦{ÎÏE›g½I²¨z .€‹óptÜ7@ÛJ‘¿;€?õLq_‰ìŽ€Sýy¬›ˆVï‹Hi+/ÊZ†ü„†n]ÓÝ?öˆÔ.(º,÷§ÿ»9&l‘Ÿgãç¾ÍìÕß:À¥µÉ†¼“Wš°yÁ¶K³Ò+ »,¸R»zC´‹ýsëozèhø¡ÝeOõ°L¤æTÜ¥9ñS מÂïöÓ®®ö­‘½¿m¿@«s§þ.Rw-€~?v9n©GdÓqfŠˆTÜúÈH?0vmøs€ ÎÌ{`‡ì¼?À—¾LÞè‹PøŒ7.tF [é€öNíò‡ ÅÞô—³1IËñÞ#ƒBƒ¬Û?{‰ˆˆlkàÞ$CžÅñ#:¥snÜcÛ¥Yé ŒÀSÞqú‡CD~€¢y"®«ûþË#2Ení•eP*"âétÒ_õŽd‰H%­Ô>û@-Î×î\  ·÷ã«Zú€!"/Àþ_‹ˆÈÕ.ðyý\@óúux#ÊϨçÖ݇ÁË7ù€ý´Áªÿåc?ß”Æ`ÔðÑSîÐ|KÒ€±÷Öö ¶KSÆõ€oW÷Óý¢Cò %>¿åâ ßug‰ˆl¥Áà+wz”À7Ÿ=ÀÁÕÞ°Ã{]Œà~¨0Л~ëºÀ„(?£ž[ï ÿö½Ç3À?EDœ,`TYKŠm—6¦ÌŒ}½Žö¯ƒ¢£€¯üÿ]ü仞 ‹ˆˆ§?0LKû€oCZßÚ §ŒðÝx:€ÕÞëc‚€ñEuhï½>há^  ”ŸýÖÝ€‘7ý@³u"RS}¤žƒÐÉ7ÓíÙ/” ã!dÛ¥)ãã‡ü ]ÿ û𯆪넬˧x56‰ˆTÞrÏŽ0&yß\ŒEÁÀ˜àSïõ±AÀø2Ø‹¶P oz} s”ŸýÖ·\x“«9€ÇEä\ ] Ù¿JêG`?ß›²€¾ÉÆÇÞýÜ;ïùTº`fÁ¶K³Ò+ [ÚȾ#()»|ÿ|d·ÑGÁæùtDÐsy00Þ Æd FÐÝí|çMî˜ÿ¨þ[¯0'è]'#RÛ8< 0ö ùqw' …Èy-Á¶K³Ò, ,/xšŒùÑŸï½ðsívISñb Àè`~ÀçŸÞd`œàÉ w8Ld9BFªüÀ¸°E/ÑÄ£ÕŽŽHÛ@`°íÒÆ¬tF˽ڈú1ß6wÙîè³fZ›“߬ý&À¸ ÀQÞ䃀’&ãP³ƒÞU £È¿\§Õ7¯00ŽKÊó Û.mÌJg´,ü[[ñ™{‡»Q`L°>ÚgüiËÚÿ-Ñæ0’ŒÊ6´æ¢Ûž&£€Aïz€EãÆyQ€qdôá®$c&Ó™°íÒÆ” €!•´NÆwcÀ˜à³(Ÿ°°%NÝ)©†|ÕèµNDj' ÍwÒd` ô-üõê5‡‹Üà”(À´¨N 0jú÷ôD$® 3aÛ¥Yé ˜…O\Û0îp]äí÷=´Ø«©†üt"ÐñA«¥éÀ8ÀAAïš«Mz? `¿(À8À[)†¬Ì{3 {€Gµ5¾Aû0üÀ¸$hw¹ˆˆì¸Î“$`ÈÝ=ÂGÒ–l»´1+±²0íqïÅš.¾¨úb=EãðrÐíóú׉L°8¥ÀØÜÿšªFô[WÀ3w]dý*²!ËmýÀx @‹ß‚h8áÚdõ0Ä}´íïª ¹~8ŽÀ`Û¥Yé Œ3}WZØGG†§€Nù_ü¦Å{¢…úðFõûoJ€QÙïòF~F=·žÀx×@`¼ˆÈzî F©ˆÈopJ`²áñ–›“ŒÁ}úìËÞl»´1et`äû¼²§¥7nàË!‹ˆBvzËÝ0ÀwŒÑG­Ž÷ˆHKh)8Ãûjphw´â’ ` À&$¾‡äîÞq$~kägÔsëÇPáKÿ È[éëJà6ßï>Üßk: .®ñ¦ß‹›$IÀÏÐ l»´1e`ànïUm>Z¹DD>ÐÑ-"¯8½nßéûö.°ÿã»Eß@O­H]ßÀýíû[Ð@Qoz¯ßp‚÷º¼Ý ŒY¸l›§ŸQß­×8ÃèÙh\«ê ëY wÀe‘¥Ùpô;u"uY1`w²€±À`Û¥)3£Å¯ÚÕ§À}Ú}+S69oé¾Õ¤<( ø‡9€ìòµÀ SàÀG_¹±gÁ rž~søÿDþox*y@{_wãøÀóýß-½y\£ˆhé¾¾Á0ÍÁfµÞï #‡^úh´ÞF}·ÖŒp§FŒWï$¶|›gò÷öÅ£Zö€#F¼"Z0\ûôéôòÏ‹' ŒûhQPÁ¶KSº}""²â\ê]‚t¥÷Ò¯E~. Üðfsÿ#roò6‹7¡ÿj9pnµì¾®Òö5üÒþU#"âyz¬ÙqôÒÍø½/Ì­Ù: ÎÒÆÂæf…<™çͨ ÿõÞ*µÓ³ «E6ÞÝâaÿ åm}Ÿ¶Oéh¹g¼´KD<7¾æ¤Àùá‰OzÊ]ºJªfÙ‘Û.mÌJg0`6êÀ¬.#/9>ë€wýÏçÀ±«ey7Ÿ÷ìrŒÿX‰ßÎÑÎkÝ×îŸ ÿé(èòˆ[ä•l7Ô­8º—:ƒWÈs}µn :³M¦jÇn£Ù€ž?Ô{~vÖ3díáÍ´:Œ±z©”¯í¯[Z:˜3#ô74t«ÈÊq-€ö³ '¯ ºgóeÚwS!£qìc¾£’äSíÔXö|ÐÞÄñüáIŸl»´1+±²°nÈžß~øñÏ‚bD¹—>þì·õß³ã¹÷?»¼6(ÅýÍSO|¬%¬x쉟’ý‹Võ}lùë^ÿÏ‹Î>ýøþíüÓí®²gxañÞ°ÔÝïÍãøEDÊœ!éþ§ä¡¿†L˜$Œm½"sE`°íÒÆ¬tÌB²õvë ƒü·ç—³ñBšK4ñBÝ™ÄÐ l»´1+³d½™ÛsWH‚»Ðn8`„ëÏÞŸÓ™°íÒÆ¬tÌBrõsnKêö¼á!wXCg¶K³Ò1 IÕÙ¡gZˆHy‹mÆÆ4ÿt&l»´1+k]rÔè·58aå~§»D“Q¨ÎOßxæ)¯æÝ6qŸÀ&:¶]Ú˜•޵.)  Ç»¾­µŸü£ÙõãcÑ1‘[½¿§3aÛ¥Y阅dê¿¹ÐæìkïzhÖegvB»—`,¦Ò¬¦i»‰OQÑÆ¬t¡©Æ×ŠcÐf5KÛMÂmÌJG`šZ7Û*¡ÀØ`Íj’¶›„)*Ú˜•ŽÀ 04ö“0`,°‚f5GÛMÆmÌJG`šúŒëxâžyÚn2¦¨hcV:ƒÀÐtYO(0Ö·°•f5GÛMÆmœ©•NÑ Šã|Œí‡èCgb`$cŠŠ6&0 C“gЃ<Ê'À,:“#ST´1E^U´(˜³Aó([çŸ è°ÎÄ$ÀHÆmLQ†O¯çåw;¤{ŽÖ Ì-¥31 0’1EESá×â!£†mÑ™˜ɘ¢¢)ŠÀhÇmmý¸hqé:ó#ST´1EÁªYró¹ÃŽ2üš—·Ó™˜ ɘ¢¢)ŠÀÐTyù K¶%š :e‘„)*Ú˜¢ McÌ 0Ì ŒÄ§¨hcŠ"04˜I`˜ OQÑÆE`hêtÚ”h&èLTF¢ST´1Eš®^ OózÎMµt&&Æ•ÈÙI`P‡fÁ–RÙ{rŒ«hèL  ÏÀJƒ¢Œ¤Cæà:³Ãs7€? Š"0’ŒEè[ƒÎDY`üøFˆ^Ä  ÷aP‘`ÔÄžŽÎDY`DÝémÓµCQ”á,++++›€÷Ë‚ôÁË÷PAgb^`tüÀ (#6­< õ)·ŠÎÄ´À±&­9 (Ê À=“êÆyt&¦FÇÓ'œÐU~‘æPe `ˆgn^T^ŒÚMgb` q霊¢Ì ‘¯:g†ÉvÎÔû~¦310^Ñ;E™òó¾‘ËjéLÌŒ·þ 0(ŠJ0¤”À030‘‹6¦(#-ñ$ž :ƒãpÚ˜¢ s¸3*Å%ü;hcŠ"0âÕ æ.®=t&ªcÛ–úµqÙIEqjÓÐã]Ã4À8 ‹6¦(£éZpÚУï3TDd}o͉d½¨&0*š*«ØÖ°ìŒÏ Š¢’Œ€ì oU‹HÝÉ€v9(ܨ$0lH¢ŒEŒøKøbƒ¢¨dã]ùvUC’=Oç_¤,0Šl«) <3€q5€ûuñj¿<4ó][r Š"0bÒ´®Û´‹MV‰ˆ”äW« GÒ>Ë 83•-Ú—lZ8]÷Ú>ïø®k?îN`PƒŽzÊ{1 @Î""R³ÚìÀp[a©Ì`ÜÜ,ü8Ö+Z,÷_¿l>`¸‡Ýf³Ùll6›ÍVâp8t•`ì¿Lû»®ö&öTò<ŒdC*-°ºcæ¸WI]ž²GúNW™ ng©½¡i.[I)±A‰5å®Ú FÝ@í¶zÇ-Zï2=0ÄßÉs†åш¤ž¬‚ëj`¸ÊíÖlv‡Ãáp:N§wÅ\5*Üô¯N›'""sÀ7:µºSšÜ™žÀr-•Š·„k­ö œî¿Þb`8K¼°(*vTÔ׉pV8lEÚÛ¬ŽJ:ŠÀˆKÓ»n‘¥ùNòMŽÞ<$€!vã,•Š»„;ΉH:9{Ó˜ƒ«ÂnѺgã]wE‰ÖÙ(*!3(#­Íï|óÓ—çhý?oRi³K3b‹õðrããè“ÂSvåŵ@LI`Tj´°–Äòƒ*JŠ4f¸è>(#VÝëÝm¾XûãÅÀœÌ†q–JÅ]Âwჰ”©¾70ÜåV°–Æîø]Ž"°•ÓPFlòÜ™}>ùãŽc²\–ÀJ Š 1w ÿÞ¬Ã×! ÏdøËøÀp•X•ÆÛM¨,±°88N±iÝ£×Üújˆˆ,™6mÚ´iÓfe0Ä XM ™…}nÜæÿoù9pdZs\ØÀîLèC*ŠX왢 ƒ¸3½a”¥Rñ—𞣀¼‘w¼þÉ¥Ï^å]&äPׯMÇE2:.»‘AFÓdJÍ Ùzxø¶s=êÚ¸©¸(*OÎX’ÛaáÀE`$®9=ž À0ÆR©DJØui(/.®Qø¡ i¾¨<Éháô7E`$¤87#€aˆ¥R‰•ð§Ã¸8¢T$ME€%Ù‹å¬N:ŠÀ 07ÀR©DKØùÔC>è¨q÷~§W’cªb%nÃ|.Ef†Tª¿T*cæ©T©°¥¦7è,Š*„¢ £©¿TŠÀqÛKê(8ØÉ  £)*Q}©TRK¸j¸QaŠS¹¶Ò 1ÄE`Iõ#[“ZÂ?5 0JRÚ½t2J…¢ £A©¾T*©%|ñ€á¶ÖÔÈYØ9,EFÃrY`QØSÄX•>Wå}0Ÿªûn¿þX•–4Í2¹m€•¿)ƒÀhÄ'Aå#[c+áÊý€ã´sXOvX©Ñ€QžÆuê°wŠ"0ô†ÚK¥b+á‡à+yÑÀ(M«¯°pß7E`©©ýüôãIX!""»ò  {šû~•ãœÄHQ†NÀPy©Tl%¼û(àb¯ÐòÇ_7˯™×Nêi,`ØâôŽº­\,EFãŽBÕáëK¸fÉ·¾ˆ´ïà°ë& vF ÝVc½§(C?`ˆ[Ù¥Rñ—pM‡áIË  ».®›Ä  £QUZ]*•@ _ùaxÊãî—ã¶“A`4"U—J%PÂÕ{#’¾7 0ttÛvÎ|Sшj.•ÊÌàƒz>æ»­$E`†|²Lj »Fúv÷ÜV#ÅHQ†žÀp[õ–J%µ„?j`”6=ËÜG1R¡'0ÄmE¹hB –°§Ê¯=[>9ØÀÐB¥ÚñÅ(ŠÀÐJ.•J „kžyhKãíôvéÏíJ‹òG1R¡/0¤(6 0–ö5fh› #ƒå@ } E` É¡œŸˆ»„ÿ(0f,)EÎ@äâZŠÀh@ž4?ÿª õüDÜ%|¹†ˆ‚BŸò  §*ûa¬ Nh¥±Öé+ºw…¡5‹a%Õ†‚K¥â.án@‡ÙΠ½{ž]‹º) w‘*ÓH®LžÆ 0Œ¨ZeÕeœˆˆ¬îßýªÎ$}ÀPbÊ5)%ܹKÃÓJ•F±:¼®P8è}êk]©S7•ŠcÛDЀ!Õ`bµ¢Î$ÀPm©TÜ%ÜGE¤íR}ã^…JáÅí*îËIW­sê÷åNCM`lï `HÍHŒuÇ\±L ©Pj©TÜ%lÃ9:ç ŽîEß{ÍL”"0ŒHC0dGÜJ`ˆ”ª´T*îþƒ  ; Òp 3c¥ #Bå€~ù~`È,4û•ÀPk©Tü%|–‡'UŸ§40”sÐÅ™ºRŠÀ 0"48dáVéÆ`*!"V]L’Jx÷‘n KZ1Ti`(7ä¶(·‘“À tFŒ¯ Æ: ;!ÚR©JUšnÜ·nì1¼6ôg] 40JÕÁ´ÂY"0R#×Òmú—»ç‡:uѺÙV Æ› Qi©TÜ%|Í”)Vô¤ãöSzãž’óÖÌœ÷Î<`¬´ •Î ôªÞûgWT© ŒÂ~Œ%V""Pd½NÜ%|2 ġ֌·ßyeb„ÌÆX½t,ñ=óÇäP} Æu~ 0|ƒv5šnœ7>a0`¸,J.I²¡ˆÀÈ`œ Ž%¾é_Wî¯80.ëà ÆúV¶šY*w oÊ20ìjAQ™‘]ŒÌÆ3õ-ôUŠã|Œí‡裤3ÑŠ,•Š¿„‡âÂ×PùÛö¶êÃ¥êž{&v12¿.?½å ¾…îÉSžAl Æ'À,Ã'5ꌿ„KzE4‡ºÀ(QõŒ;W&v12ˇ)Uê…jC*ZÌÙ cëüS¶ÅZ±Ì 5–JÅ_ÂëúE$mUn‹²›ª‹3°‹‘ÀØ~à±F,z=8(¿ûØ!Ýs´!îÜÒ˜+–‰!N–J%PÂ_G&­U@ÕC´¸##€Qw&ŒØ´¸CÈ”hÛEjWè )שT2Kø©zç ^)rlRôÚg“ SFãv±jÇmmý¸hqé5‰~ÀP`©T2KxÔcª£BÅ=A ™Q*€ñZ‡j–Ü|î°#† ¿æåíª>}ê ±fÆú‚“TF±ÒñÕz'0Bžz_¾ù¼Kn+¯Ùú{ ͳüŽÉ#ξêÙÍ!ïüqÆ×"UŸc{[<ósRVVV¶Úë ß½ÂÿÆÚ%S?‘uwž7âê%¡ß¶âö Ã'Þöe½¨ Üê|ôŠ³Î¼üÕawÔ}|ý¹#¯(õ„ÃóÍí ¿àöÉZ»…tŸ¶œ‘ÀÐ}©TÜ%üÒèP:!Ù›Õ´±â+‘J`É8`èØ§Š ž,W=vË0´*¼×ŸöJoXÆN8È:÷¾ÄÊGo˯À’F⧉ˆgéÕÑÒëò?¹Òü[Üö\Àä 8üv:>«°¿¹låÊ•[ÂhÑÀ­ß Cþð‰'äG}|Ï{=ÑaÔ9=qÆÚ`||”7{ÃרŒÍßlÖÍRiA‘[צç³¢íôž«¦j{d—ÞÝL³>&3Ñs“ˆxžÎòOƒ¹Æ ù“nùê` ïy}T.¼½¶+à†¯Ëžô0~»ù@^`,ìø÷–c±O{À]þ/[ÚgíÙ1Æ× f‡ä¥[=wWî‘?Îpµß¥x®AÎ=Õ"òî~­sÀðÜ’5àýMU?\  ýrE€Q¥§½åžñÖkW!Õ×jĦV5G‘âc>6…§äUÒñÛ¿þ£]ý7h{ŽÞÖ.ÿàa‘Û¶ÀkƒOzþx÷‹|ÿÆ›w=xˆ›·}ÓÀ½»¼á–çh¿Ëûeë:¡Ív‘ªC0nܸÐeB Ü:UëɈHÝeÆz£Òz®ž÷þì‚ Þ·aÄ^¹@ç¤ÄÑM 0¾è‚ÂóÇ4¿Ý£Ž3Ñ:/•J.0r¶¥1MïÅ©~xv¹º‹~3—ÞA§í­¯Õ..Nõ½ük+ çSÍ7÷Pt¡[<%‡Ù Íľ!)‘ÉpÂ&Ïažö¦_\¤]=àðhù©çÖ—€v;½o©îàNíúßÀß­€ñZx'd\ûø—:ÀðlY½%@×þÀ§"?tž¥Œ3Ñbô |œ0.[øa™_ï<ÖgÂîª*OsCET|kœ;Ãv{Gy//Ð&­¿°Øÿú ‡hógèµÛÿJ(0¶ãfcj´ëGL×.·æ·i—»Zø%J~¢ßº³]ðSçûòÖŠˆüÕXæKþÝ ÷A8×—z1€vɘøNb‚­€Îž绻€ƒED>ÍúÀþzýưãÆ™a ;÷ŸT—Ö˜fDJ¤8³NÞ30NööÖ$Zì¦l¯ˆˆÈ9î•z€Q Œ»Üâ½~€7zÒ«A`mÛZô[ïCÄs€ŠˆÜèÏ«§•àq_ê<À߉Ò7øG*:~«¥œ \,""'tßE`0u\*?0OYè{RR .ÕG¤DÊ3k”€1ÀX-ÄvÍz‘mÙÀaAo8À9""r.€·ê†;wyýe†h—s\†hKH¢ß: 4øMö­©í Œ $ûWIFMñj,T'ƶۯ‘}뽩 Œ —¦ßR©¸KxîÃS<Ýð©ŠÀ(Uß»2+<ˆ¾À¨ŒåÛ?€öóg¿`TÐìÚz|Àx;f`|àèÀÓhÿÓWôðÊQoÝ’ +-€ïE–˜ ŒÚ¶@ÛöÁJÆ$jÂÀØ”ýF_{Ë…µ™7|#mëЫN g¢0¤R·¥RÉ,á±j®’2Âx5£öîé ŒØjÜ9€#¿ÄyA¯¿ßÓ}ÂÀX {]Àç/m*0!EÛà5‘´õZáÀøÀêä—j¢MùIÀ2c•ˆˆìœ„Ed{ ÿmƒæ —J%³„¯DÎNa„å’Œ:ÛÛHÀ¨öh“öÿUF½¾~L 0¶µð®v½èTÛT`¼üá""í<&2À ‘ÀøÀ'êã,œµÃw½·ï"²À“ZBߦùÇŒ†”ètÀOKØs°R×ÔçÔÕą̈…µF†¸gj±¶;¼)"Úª¢ÁA/¯€Ÿ“ ¹ÀiÚÐÒ?€¨‘Ù¢ÞúT½«§æiÇn00ÞF*œMÂÀ8ð  Ÿp•MD~ð¢öÿ‰¦‚3QR¬ÏƒpòJØs7€?ÕF©ΛpgÔ$†¡€!òý@­“q¿ˆÈtÿœ…¦­°=9À¨µ¸Ï#"+š!úšÃ¨·¾+‚Þu€7DNlÈÆ;ð/ßU -ï úgú(ùJûZ÷£ê2ôY*w ÿøFˆ^Äç‚îTÛ¸ØÛ¨­ÊðD`Hí¼N€7E›@PÜíÕÚ$gÒ[Ä=§pæÞ»5Ÿ{£OòF½u9,zWgmÒû<"ñ9€ê£]Ъ0Ï¡“Dä¿ðï‚9-< 8…€!.‹§ˆ&w§·-9h¢,:n‹lºJ2éP cæ¦v\“ HD¾€—oø/€³%IÀyj×vàMëëÉOÔ[k báJMбNäZ==ÀX±õâ¹ÏÆ€Ë×w`†ˆ,ðö‰ôö¡ª„G¶&WØ…!’a;1œúŽÆ2üwäOÞ‹·sµ¡§Úö&Þð€çêÆ1±cn÷¯ÎOô[ÏÐ-@†µÐ6¼½ßFó_(_dEÛ I(ÕD›òÌ\¶~™ ª¹ À©n‰¶’Šté=I&0F¬Ik ñ4ÃÓB朢äÔ·;0ð]Ý`•hÁ9ÚöOºTGF…·K 0mÝØr×è·~ O¾mùIe.€ C€±[D›¸G SñW¿ÿSaHÊÙ,çâ—¿úÌq÷):¹Á#´×ÞEÓž12 z,•JOŸp~@W=úEšsÐ49 ²`5“f½ÆÉ¾«¯¬¿Edk °k¦ö`žø´Ó{3‹ÿ©7€<ß?Áñ=¾ÆKÁ]—èŠ~«gh Àˆˆ\䛺8@–o7me€Í"ÚºZ´÷móø£OÁV€!·=|¾¼­è¬ù-œ9ñœØÒH`)ýG¶ÆŒ!.sÐ4$tx&Íz øÜ{õ% (¹À~¾°Ì/c½TàÙÀu¹ÐbÄ}W&"ëÀ4Pf¸Ù{ý~ .mUk´àÃß«¯çVg[ Ë÷Äök3 oºª9€öZ”©=£á[Në -fl‘J{»¤DI0ê.ðóâBl<ì=Fmyþ(íF°Ò¿T*~`¼¢w fÛFŸ2çL C£È»;àVd©]ÝàL-Nøê¶êÝmæ944‡ pôê­/¶ÿRD>!FÏ0Å{}/€ŽÚ½Ï¼ÞrŸ=ræ‹¢mÜ«çVù¤5Ðs­Ö™„.?zßò$äM_±gý›ýúµsü«EÖ[´oÚ·¨#€»“Sª‰7eÏ€¬ÖŠÈ_ƒ€£j¿m^0ú8­\ 8õœŠ;ÝK¥â.á·þ00Œrš#ƒ–I 8qˆx^kÖl¾/qA!0è“:Ù=¿-&y$Ú:ò¼ü4§œ5OD~ #µWç(Ôè³r¸«VDdgßÐ9Á#¾ŠÈN}·Š|×èüü^©ûðP ´ÍG³}Ÿ¶¿³@Á¤²ZYÝÍÿ--ç'©T“Ñ”·ÎŸtÆ„½9ÕoܾIäuí4Z» ÎDÁ§ÐJKz—J™þÜv£Ì ”aaÃjµdõ9ïüÃp⪠V:µ#ß-·ÙÞ«Úò¼þ·íßl²çF8à‘©½´ÍâÈë÷²¼ÕËûÎw‹ó`¯?ï0KDdÏÌÜb´ÛÛÀ­"{ìävËÇÀ‚·p|jäžÿ—î3öUß™Þ;ÿ¯- ù¥ÉjHUS^Tà¬:•Ü™JÃi^*ev`&æ†AVse0ÖxD¶/eÎüB÷Õ~é˜óÔ{Û¾yÕóO|TCÆêìÿôßgn½æRÛ¨“î“ \Û[õê#¾±´}Ók%¼ºEDþ’ÝšOŸ½Þ÷$¯TSÖ”7Þ:ááj¥Ü™RãÜ$¹´´2–Æc``4á‡Æ»2h]­€‘Ö*0²Ep,¶] Úõ4QaЧÏââ¦xOµ&F^*Xìå®”—pµ§±„Tç°4f_ãŒôdкZ#*/NÄ¡)³³ëŒcÔT㧴à¨Qï©0ÜÖ7'kc›Ög*Kø¯3sö{7¤ãxZv·ÿ¦@#Ð0Î\2‘á%}š‡­¨}æ€ ïatسi“ Í{V¸ q[`q%Ô |j¼w _ dÃÏíwÅ“Û$üÐz¡a`X ²œ‹ÀHV†œ."â±ÎÈl`ìÍz!ÝÀ( ŽŒçp‰.• YiQd/w§¢„÷G ”¤ˆˆ´A=‡„¥õBÃa€ãöÔ¬„Fz5’r_×ýoÓÃ]U¯vý1§¥h‚OQ°­–#?äÉZ/*ã/árƒ—ý °ÏÕÀÆqÃFfã}(^ëûwãcEûÿl$£ÆŒGÑ r6é DŸÒP¯­&´T*ZÉ[ŠK]I-áßûcŸ§ƒ~9ù/ŕۤþÐ0hJC×]íj£îdÀ Ëg=x÷5¶ÁY³ÁPFkF¬^0Àˆ2¥¡`[µåâv%óG£È9‘ {ÖîO¨Š/·É¶n04 %¡ëOUtÒ{×…Á5øÈÿxŒeÔ8ç0ú5 ŒÁ±šÖªG¨*œ¡r7Ù¥Oi¨ØV‹—3<Ë!²‡E Ð௮L.’“Ð|S ?4 þTƒCäçÿÛ_‹&rÄÔ/[µÓˆFŸa¸oâóMôfðºË}Æí‰Ý´a] GX—Ö¨K)²—+ØVýë¤B;På •EQ,¿º<#€asŒ"0øS  ã*N`¸Û½ê½ÚÞùÔ°!øŸšôÄjÚ˜3êau)Ö’ —zmÕ]”@˜sWãn40—aÞ!)mÈ‘CRü©†ÂÀ |Û§·‰8È|*žN›3i²KQ±­&´×ÛÙ`ª4æ0óS*¡20þò Àõ™ñÚK 0ŠBÖë«ÖV‹&ålBÇÂÄÀÝŸiO4’#a²Ÿê.&0T†_ùOD$½ lÓÖ’ðеÕÒÄâÕ:›Ð±0)0"7ó'4ˆ¥Fêå²"ƒÎ640:>‘ô °R_`ØN·êwH,œ…³ ‹d”° ÑjüÂàƒJ#ÍTiAæ€ÙXÀ8*<Ås$ð£nÀ(ª?ŸRÀHøÌ=g:‰—°:Ñj F`(¨r ,åöÍFcšå;3ݯGlב£PÊÃ]”X¨Z0l ÆçM¼„•‰V[?,¼…A`(§J tirè×·!0ÑšìÎËBúe#ÖÓl’Œè£PÊ#ÑÃ0DœEťΔ—°Ñj†…¡€‘IG´jg¾ØÓ~v® vÞ©Cþ…¼éþà‹5ï€{Ò Œ¦Vª0>n/]îZ…hµM+aþ)!Fú·Û “êcÏQÚ®/yáÑGµtß­’3Q¥Hó2PCG«mÚÇ; ÑÞŒ3Ù’´œÞá¡Ê"f‰ufCþ¶F©ÕWj9們è©tºk¢Õ6EEY“qÀHót…%Í}w#vUÏÌæE×Ïs&ª#áRʹkýs`3ÈceíÛ ªîiûÑå€Å)”ÚÀY5¥…mgl=ŒÄH†}r·dàó¯+mSßvÀÊåQ†Èžn¹dÔˆ oúpw<™È(`Xu˜ Mn ? žËa1Bssgd¼ wqZ¦¾Ý6ÀÆéncÃÏ¿jÃ®Ç kRKxÇPõlìŒà*2l‘”_%i˜úf4#¾¤2 ¥º,âHj ¿¢ 0¨0@ssÀš™~¦°¤Ö@Œb,`h£òÃJª Œ†”þR‰—pÝ–•:^ôiÞµ­T†Õ³ÉÅû œjžz"QIÆ*û¨.ãDDduÿî/ÄÞËÈ`è°@*Ñ^yq‡ðØŽ*£Ä³Þ–Ì]ó™Ú#F10¶M R}&ÆÜÉÈ`è±@*Á~(+2ŒºŠÀ¨0Âì@¥Q6¤§¤ò§nêÛÍh ÆÆöþ†ÔŒÄØXM—1À°êå0â.á÷£5¢"0\FXTjŒµ\©ë¢(õ_ŸUF|‡`ÈŽ6¸•Àˆ*»n»Pã.áS4`,¦QѪÛóeÜ%\€þµ-TI`T.Å›Sý,¦ZÉŸú.çt·¡€ÑãkE‚±èN`D{¸Òo^.î>WD¤í¦"0 ðø^’©»0‚ä¶%w~šÑ@ ŒÖͶJ(06ØD`„7‹N ¤+áI¸Aç4YêOq¨]sñI›úf4ã°Ÿ„c €FXͶ꺢2î^Œó#·* ŒrÕ£ƒpDÊo¨$M}W2ˆá€Ñ÷Øp`\à#â±JÏ“¸KØsrÛˆã/öœ¨$0”ßg爔—œIšúf4㲞P`¬o ¶gPóáó¿øKø×üˆ1©o‡ª Õ²…Þ-©=F1"0>Àg!ÀØ~€>Ê9}¡ç©DKø¼0Ôm¦(0*ÔÞG]Î)¿’1÷ªm€TJátÀÆ `|r Ì"0ºÎ:.H¤„ŸÅñ#:¥s57î‰ê“ʶ ßµÞLlu£RÑ¢`Î [çŸ è°À®Ûº.J°„÷ÞÚÞ¡ADD*GÞpq{Yx‡+ÓT İÀ×ó€ƒò»Ò=Gó'¹±Õš:/J¬„«¬‰%¥¼O¶gv©H9Ø¡Îs©$C‡Æ¿n»HEg¢0ì ¸±¸Kø!bG‘ª Ím¡‡ ÷úqO}3ˆ¡!;nkëw&-.Ý ¦3Ñ "£Å]Â{÷sï¼çSé‚™Êéî¹{cœ!›^ˆÆ9õ]Êh ƆHÍ’›ÏvÄá×¼¼]Ug¢0t_ •X "çµð´ùÊClÊ£TÄfÑ{ß±O}Û+×›Q4ç¢Ç H%VÂí1:"mƒºÀ¨PõTŒR®©­ïy*Æ©oF1)0Fà\CR‰•ðñQbIyžS·iU³‹á¶°ƒQâcœŽ`4üÀPaTb%<“uÎAlrªÙÅp°ƒQ/Šb!€“Ó݆yaW¥vÇ]Â5ý{z"W¤31YÁø ì`4òTÕÔ1&F!0L ‡2GGÆ_Â+óÞ OÚ=Ta`8U|-a£‘窦M}3ab`¨±@*Ѿ»Çž°”¥*CŠaQmJÔÅ]f «iëdÝÅŒB`˜Š,J´„ÝGÛþ® ’ë‡ã”†‚ÞÙ¦ÃSS¦¾Ýœî&0L UH%VƒûôÙ×8;½EDÄ¡ZÐÚ ÎÓ6þtUÔØð-£f†ÛªÒnÔ¸Kx¼¡Bƒh%_¤Ö¼·Û¢ìnB•JÉ 7Ð+·ÀB즆]©ÇʸKx¡ñ€!NµEíœñnj9Õ¿›Ñ@ Sá–ÓŠ»„wî Ea@êCŠUr.Ii¢€£¦F¹bósñ—ð¨Ü¡«¤j–©:0Üu¥TÊ‹êªoê»±á*ŠÀ060TZ •` ?xDÒçªC*”Ù#6¥ÄÐn¢N}3an`¸-ª­£Œ¿„·õŠHr) )Q%BH)P*T“[N”¾£æ†Z ¤-áM‘I*‡ ˜@…1ïJð ïØ 1[‘ØQ®¡<0ìê=%±„ÿìý¹Î9h’§VbPÐmAÇÞc“#tê›Ñ@ “áಘd–ðÅÖ¨ ©PaÜÛÊ ŒØU4Åh †ÙQ®â ]2KxŽ20¤DÿÙ;ßãìz§¾]œî&0L åH%^ÂÎOßxæ)¯æÝ6qàCŠõv×%ôvñÉmìnF!0Ì õH%ZÂ‹Ž‰Üêý½€¡÷éUåäEܦ³V7£f†[ÑQëøKø‘(¡Ašï20ÄmÑÓå}O@ÀÂh †Ù¡ê¨uÜ%üK^$/ ^Lo'nUêH ò"Áò³° ³áê˜kÜ%<~ý}³ïkQ<{öìÙ³ï›>¨Å[§·cHbTÒÝ%l;F!0Ì uG­ã.á-¿‘KÿÏ›Puì…ÃC7b”s8%a™Œ”t&©†š ¤+áNwi¿ïPåMYŒyƆNÄ /(£éÚ14¡è©ÄJ8±÷âøç½žƒÛl20¤Ò’þÈå ÆMM×+™ ·ÊÛzã.ჾôÙ´·¯‹q®30¤2í±ëJ8A ¨nËÊ/ú4ïÚV™Œb•·õÆ]§û¢M×t™é½š„Nµ†TZÓì\ÛC@ÏBѵòâ žái`”(½)5î¾®ÇïÕí¹Z¤ ²Ó;¸ï—¾ówÍ‚"0ÒCY úl`(¾­7î^…®—Œ:éUÙÜ2ë¦ÿyªß?À††öПžáB§E³›(J=`¼e'pæCõe÷ñ—ðHÈY#"÷h• y5Ɔv\t: `4 ŠÀh@§h6xäÄÉ>MHÆÃ¥ð©KøçxWDöúƒJIk’öäŸòa)—-m=Š2&0ÚWýšä¹"ÀáVþ܃JxYo ŸKDdc/íÖâ²EΔ~E…%s%eH` Äš™Š F±òç$RµŸ-òÜ2¥=3æÏtç 9r(I?ws8Š"0Ó!¸""mï´ŒF‰úQû“Už߿«Ò7 ¨²(ªHч—ZwëQFÚ„”qgúÃçèﮆ¸ŠSáÕ6ÀR*E`4¬Å8?2qkÃqI“TÂÝs¨±¬É6—Ëž*Q”É€á9¹mÄ Åž3Ê/JN kÞpXIµ±¡³`I*2ÜKÊçÓ)Ê$À_ó#Ƥ¾š1ÀPTâ%¼Ê>ªË8YÝ¿û rT‹9ÌÙi·ÃXxò4E`4Qïä…mmÝ<,s€¡þ©DKxÛDЀ!Õ`buºsôN¡Éêe¸ìIï°P”¹!ÏâøÒ¿yælÜ3À©Kx{CjFb¬;½9H2ì‰v v¤bJ„¢L Œ½·¶ÏØÐ åF 4 C0dGÜjx`x»°–ÇïìÝ¥E`ï‚"0bS•5scIçàæ¸K¸Ð/ß ™…f¿"nG‹=®îòb(âF=ŠÀˆIenðAc,J¬„§‡,Ü*}ÀXL50D¤¢,öû®rï} E1j€cï~î÷|*]0³ #€aR‰•pŒ¯ Æ: »I€!â*Õ:ÈV‡³‰F¯(Ñî(.çXE`ĬB伞6?#€aR‰•pëf[%lJcRÏ ›Ö-¶–”7L g©]ƒ……´ ŒøÔ£#Ò6¨ ›£Q•:#½a”R‰•pa? Æ+L qWØ}3qV[‰#ÜâNg…£ÄVä{G‰“nƒ"0âÔñQbIyžSI“Åf3ÔIœq—pßcÃqñNÜk46K¨‘m¶°E¶¦Ž\QUó0Y?w›Ê£õ(l‘j"4¬–ˆ»„/ëà ÆúV¶*kã©át8¢Ùßb+v”:9 E‰6åšþ=#ƒEÄ8d¡¼3qGŽSUj~Ü%ü> ÆöÃôQ÷¡ IÒl\ÚÀˆ$EñheÞ›áI»‡šÙ™ÒÌñ–°gЃ€ñÉ0ËôÀ (*5À»{ì KYJ`˜RÑ¢`Î [çŸ è°À (#>¹¶ý]$×Ǧ†¼ž”ß}ìî9Ú€~nišs@Q”Y€1¸OŸ} ²Ó›ÀˆK‹;„Ø¶í¢´ç€¢(“c¼qBƒñiÇmmý–mqér@Q”9€±À0;0Dj–Ü|î°#† ¿æåí:倢(3cç>ZT@`˜¦ÈEQºCFå.]%U³ìHÃ,À¨¼|Ð%ÛtÍEQ&Æó‡G$}N`˜cÌ 0(ŠJ0¶õŠHrfF!€™EQɆD‰um¶Ð ™ Œî@§Mºæ€¢(3ƒÎÄÄÀ¸ x1<ÍómLQF"ro\ñ›‹À00¶tÅ/ái›‡ÒÆE`Ä­Yó Çåe:3CÖtW–TF`P©–Ûé GPÁÁf†ç±ö={ƒ>£310d³u|èñ®“ *•¨p[£IRRêdé˜{'„6û¶˜;t&ÊãÆiÓ®i×Î6ͯ©çu‰u_&mL5åö¢ } …È3  ÏÅáÏ7Й˜£“ù…6¦š"W©·caµ;œÎðnG…Ã{¬º¥¸œgÏ@þØ>\µaKÅ»ó.je] ‰QñA¥§oam¼ áª(ÑÞeg?èÀس?ÐmnPp7fí»ƒÎÄ$À¨nK`P©ï\Ø-,öŠÆû®òb(r°›aH`¼ \¶œv!fÓ™˜r:^;牀¿÷lƒJªœ6°WÄÖ±”¸XtÆÆ˜1É=áh:³ã |žtA%E¥±u*í`'2 Œƒ;îŽHûUt&&†§×òð¤O *YrÙXËc¿Ñí°àÀ”Ñ€ÑîºÈ´_ñ+‰I€!Ï…c쾚À ’"w €¢ò8ovXK9KÑPÀh9/2íK|Kgb`Tï p<ç¦Ú4#‹2«*,ñãÂÏÇ¥ŒŒL» éLÌŒYïóWöžÌð/T2ºʼn)¹l€¥”ei` ;/"iMa«ØBéL  ™ƒg *a9-€-ñQÀÆ™ Ãcv³ÂR~ìsèLÌ ŒEèãIc(sÊ )]w1`q²@ Œßrz†Ä¿^û¯}€·èLL Œºó€Í•˜—·Öd… -µ$‰=Tê!ÿ@³ËÖh—µ«?#À 1>Ò™( gYYYYÙ¼_¤^¾0€Š´ä€2­*­€ÝÜc©•=t4~üiG4×"G´ý™ÎÄøÀXyêSnUZr@™–É+'2  ù±C¨;ió‰t& CöLªç¥)”9Už‚I;‰a`ÈG{“Ã~Ž=t& Cüµ©xšÎÄ$ÀØ™öw)\íMìÉó0¨XUœ¾çþÒ¸Nl¡R Œ¿|»¹ûLŽxí%  31 0ºj3u´óŽ<ÖµÞE`P±É™Î™ŠXâjïü'"’Þ¶Ñ™˜§Í‘¹àZÝ)9 L!k:}¸p°ÈÕFLJ"’VÒ™˜Ó»n‘¥ùNòÍLÝ<„À bS9àLã×Ù¹CU` žâ9ø‘ÎÄÀX›ßùæ§/ÏÐÚº¾´Ù¥›ŠÒÐÅ.†ªÀ˜•ýeXÊ#¶Ó™˜r¯w/fóÅÚÿ/æ”Ê ‘v1ÆšìÎËBúe#ÖáJ:uá¹3ú|*"òÇÇd¸ŒÀ Tî`ˆ¸-ìb¨ ùò¦¯õýSóÎÄ,ÀY÷è5·¾ª¼»dÚ´iÓ¦M›E`P±È™î†ˆ–»’ÀØs€¶ëK^xôÆQíÝwÓ™˜fÉ¥›lé?×È•®}åF¬ú{HXèÁV_Ñ™´1¥§ó.NG¤C#UÏÌæE×ÏéL Ú˜ò©Dᡊxƨ[÷å²Íä–†ì%ó¬®IO¶kV™"«¦´ðá¢íŒí±g‚΄À L«¢XÈÑé[×M³@ÿ‹¬´ •gúöW/ë†iÈò_/ž[xt&CdÏG·\2jÄ…7}¸;žLЙ”YUè=ÖëbÍEm,ùñ˜/cô‘ìÇdH=0Ößw|6€  ASÑd×g6ÁãÒ¬/šånñ\àX9 @;YxãŒÒŒÏ¯¿8ŸÀ 3!0hãÌ–E§¬1Ií=§Šˆì=ø‡,0Ó÷Ò/i†ˆ<‘¡Àp¦3!0hcJD¤RŸ©XǤž.‘¿îy`ȧ·¼8Õ§]š€ñy†㣡t&mLÅî·u#Õ‰Àõõ½v@š€ñ]ÆÃSåמ-ŸL`´1%""6]ÖH‰Ä6V“ü_}/H`$µ Ï<´eèÖ=ƒÀ )¯åõ:þ®8†£Z7€ÀH0–öE„ ƒ6¦DDœ€^qcKc ûžÀH0þ(A`ÐÆTt9ô;.5–IŒ Œôãr …>åmLyU »Ž•ÎÙ”·ýUVV6À¹eeeee^2Ô¼{E½Àøí±KFŒŸþq==§Ý¯_¸]D¾>欛ք¼âY~Çäg_õìæ°;ö,(}ÖŒïÂQýþ¿Î9cò£ëMŒn@‡ÙνA…²kQ7ƒÀ )«ŽS4ñ»ËB†Gþ-"ž¥WwDËz€ñë9YÚ;ûþ7ò³v½rN>°Q¶œ È›ä_é ËØ GYçþ/è–Ú Ð뜲§‡,«õÌÝ_ûšœ©;MŒÈ]žVJ`´1ÃS~JdoÚ¬÷_eeeÓŒòö0~»ù@õã­‚NÏýZóë£-ìgÂ?ê_Í—uFëÖ(V×4Ò-"_ ä½à¿åïÐa‘G¤öþ}Úcç(ؾÜù÷—à üË<À芣")Ë{mL‰ˆK¯m{""ަŸÃñ,€+½×oÞõà!õã£Üýµ¥%²¾û˜µ;à™–Ç}ã©«èàtï+{ŽÞö"b€‡}Îr0Z­Ô._G5§â.©;Àpi€aÃ9t&mLE•SOÃW4=®z00D¤¼>`ü^€ÿxSGéÉ€«jDD64à}ºZè‘_[9Ÿj×S€‡|éãÀ˜Š"ïÉ2:WI0¾À :ƒ6¦¢ªT¿ER1Ñ* ÛêÆdìëcÄs°"â“Np¿÷z,€w¼~XìÏ ‡Ô‰ˆ| ´¯– OÔ€ñ[.žð¥öp–i€!WayxRõyt&mLÅ4*”|¹›~ˆR0jêÆ9•_ੈO:À'Þëé€wŸû@«@odS6€WDD¬Àdò2?0.~ò¥NÐÅ<ÀØ}äÛÂ’V ¥3!0hcJg`Ä0ã w=Àx 8ū˳ü#â“N ÆýnÙ– ô¦ãœ#"?xÄŸê_V[× Y—û¾g l2 0dcᵡ\¿€À 0hcJDŠu‹$•`ŒZµÖ© ãAßvÀ׌ z“@[ÈÞˆÆw@vÈ×´ÿÈ,À¸fÊ+ú ÒqûqãAS""6·aˆ%]¹|RT`ØÔ¿[Eΰ,óΊz’D›òÉ`hƒ6¦T† åI†gà–x€q€AoZ ?Šô°6wÙn5=I¢Mù ƒÀ )EÑÔoo0v¸,`\ `pЛÖÀÏ"íµ?áÀ˜`½šž$Ѧ¼)‹À 0hc*€á0,`L÷ÎYø´¶‹4Þ ÆtŸ™2¾¾øË€Êß¶·%0 Ú˜20<̀ܭqãØxÓjm<"ó"q'€ëL Œ’^›AS £©[¤›8éÝÀÓÁ÷-¬IÀø^¼é¿Î‘þ.‰Æ ºÖ}¨g–"ëjƺ~I[ ƒ6¦Ät«¤ÎPTx߮ދšŒÚö&Þô€çDäŸÚ×Dãa|‘yýëÌ ù:2i- AS"ź#éû0ž€;ü/ÔÚŽö4 r3€6»üošt©‘7`~00‰ˆxzèˆQûM‹÷Tñ$ÉoÊöþœÎ„À )1ÎNï'\øw7€<ÿ=ýÀØÝ@¶o+FÕXD‰ä>?0¶¶ñG ‘Ú¼Sîn:û†›Þ  ØÜ |éµ:^•pµ)†\Üa ASƉ%u€q×€¨¨=€_µËûç}ï©z¶®ˆü¤Á>ö^Ï0U»\`?_¥±ž€£´€ék{Èù]Dd{ØÿñÝ"žŸ&ç®VÆ“¤ )O‹rD Ae Œ­öl½!Ž>€ ï?›`@íIÚÎÖ‡uÍFîø ÚÎÞSûDD†+ÔœY%""«Ûbè-Õc˃ν«Ÿls$´9å,ù0ÝýÐV@þGêx’d4eç§o<ó”Wón›¸ð ASzŸ‡ÑÄ/¯}1nð…úÛ`¹EDDÖ€Öi/í²ö›]UñIžû` 6‹ûZ6€f¾Q«…À Oêd÷ü¶˜T廡æÿǼºý_…ˆÈ›Íýé=–*äIoÊ‹Ž‰Ü¹÷= ASRi€÷>ÐÖë·Zô=ED¦öÒï‘×ïe™ßÛûOnß;ED¤îéžZÂéK"?ê‹÷îƒß—×»y?õoxÁÊ©ün¹ÍΞåõ¼v Ðîþíÿ±Ä·ê·sòûÚw©äInÊDÙéÝ| AS†8Ó;Vy¾wÌyìÍ-qÜYû¥cÎSïmO^µ`ÎÜ÷«D¶¼ÒeÙñÎÜûŸ]^«–9mÊ¿äEò¢àE:ƒ6¦DĪãºZ}7˜Ó“$Ú”gï¿oö}-ŠgÏž={ö}Óµxëo:ƒ6¦DDŠa×±ÒUЊc@KmãÞ¥ÿçM¨:öBOÌv¥! Ê”rÀª×Wë:B`DW§»´¿ßwðÍû/ЦEgB`ÐÆ-' ×É¥°°üUFþbïÅñÏ{/<·‰1P A™·îé5.”¢9o#‘8èKïÅ+½}]Œ‹bÌKgB`Pf•U·S½-MŽUK¥ §ûjCM—™Þ«IèÛR0:ƒ2«zíõ®lz`*mÀ¸®‡o=òí¹Ï7vâNoƒ6¦49õšzÖ5* QV¡ë%£NzUD6·Ìºéžê÷ð AS"ú Yu\ÐK`Ô«‘³FDîÐ*òjèL Ú˜±ë³°ÖÅ]JãçxWDöúƒJ¡3!0hcJDD*ô“â¢Z5!Ëzý\""{i¼h·ŽÎ„À )M]"tqDJM`Híg‹¼Á·LiäŒù“΄À )¯Jô˜}vr”ªÀ–ç÷ïªèL Ú˜òK—Ù»~!I :ƒ6¦â•-ýç´º€r¼‚À¨¼|Ð%ÛèL Ú˜ªOåé*ÅÍ‚WcÌ 3!0hcª^¥}ÚmáQj£ÀL:ƒ6¦êb¤we­F6WÝN›Í A™»‹‘ÖY v0”ÆU@ĬžGèL Ú˜ îb8ÓøuœÁP[ºâ—ð´ÍCéL Ú˜ îb¤q•« ì`¨ YÓu\x0ó2ƒÀ © 9Ó¹ÌÕ†"v0T†l¶ŽßÊ÷“ ƒ6¦BxÚF‰Ê¹C]`Ü8mÚ5íÚÙ¦ù5õ¼. 0 Ú˜ yŒ´¤ëÀT·%ýû Œ¦j4¢ˆÀ 0hc*D¥é RÌ%µ ã5ƒÀ ©ÆeM#/òVÕm ƒ6¦•+-CE•Rr:^;牀¿÷lƒÀ ©p•%©þ·•[0ÔÆø0<é"ƒÀ ©pÙS¿|©8½; Œ˜åéµ<<éƒÀ ©hÿ© [ëà–=Õ!ÏEc÷Õt&mL…ËeIíÄw9x.«òÀ 3!0hcªIª´Àšº)†r¤òÓ)ƒÀ ©t*•>½’¼ 0(ƒ"1šÖ{±T²€ ŠÀ HŒÆ>—¼ 0(ƒ2!1*Sñ©äA”ùˆ‘tßîŠÈ ƒJR ë/ZòªÂKRwð¹íœï&0(ƒ2¥*-Iâ²ÅäAQ”å¶Ödmá«°¤!HE`P¥“ìHÒ°”ÛX*X EQ¦U…°%Þɨ(Jb_…"0(ŠRQ.`Ið¬#W1nÀ (Êü*µEÎøïw;,)ÙÓAE©&·€-^d”%ÞE¡ Š¢ "§-Nd¸K‹Ø¹˜–À (*cT^ ¨<6ÏïrXØ9ÙM`P•YȰ°Ø›<á.·¸ 0(ŠÊH9‹ ¨ÄÙZTØ-,âB7`0lEQzÊUZ–âÒ†:N‡ P\Î#0(ŠÊ\U–,¶’rgxÿ¡Òé°[á¥gºõEQ”ýŒb‹ï!²Èf³;%6›ÍæO´Ú+H ƒ¢(JƒF¹#ˆ Y‹N‚À (Š S¥³Üáp8JЇÃátrŠ›À (Š¢ ¤ÿ¬œ÷Î3‚Žõ%tEXtdate:create2013-10-08T11:38:49+02:00$Ë"%tEXtdate:modify2013-10-08T11:38:49+02:00U_sžIEND®B`‚stxxl-1.4.1/doc/images/install_windows_msvc11.png000644 001411 000144 00000060261 12350112610 021623 0ustar00tbusers000000 000000 ‰PNG  IHDRo˜Z¥M#sRGB®ÎégAMA± üa pHYsÃÃÇo¨d`FIDATxÚí½]Õàé¿yc^:b6¶;.E+6¢CÛ;önÄÎìK ð°1/ö<ÂíŽ˜Š®r»=`¯w0cÑ*l™–°-hðªM÷4’b¨’,aÔëv7. ‚E¢@R•¾@_%ª®TÞ(=¸ ¼'óäÇÉó‘yòã~Õýýù…¸7oæÉ›7óWÿsNžÏݼùk€j|ŽCØ$`“€M6 Ðw|J~YØ$`“M® ›Ü²å/þÕ¿ú_~øÃüñ2°I›Ä&Kðüóõ¹8~çw~çë_ÿÓ7ß|‹C Ø$Ñó8räÈ /¼ðÔSOýèG?zã7~ó›ß`“}g“»výôóŸÿüçŒà6Iô6^~ùåÉl<÷ÜsªPöí ‡;Ö›MºT›€¾µÉµË¯mÝúÚåµµ.ØLáºÖ‚9&&vëôö¯Û51,6TYI¡O<ñÄÑ£G———Oœ8ñío[Lyýõ×±IÁO|S”¶cÇՉ⭘(>jÌ&{þú×ÿÔÚ2G%±Iè´M §JNÂ’üÅËec°ÌÂ2—ŒUR_²Grb©í/<8PvK£û!^xá±ûB%“)çÎ È֭ؤU(K©¤¯M&jø¥/}i~þ”¿Jvß&Û‡_ ~{/¾9(—ÅÓ3OËù–™sëãX~qBûâáîyЛ $,£µ8íVÓðºo“k±ßE{$2žóخ൧MjS\GæAE¹Cb“O>ù¤¸1]¿~](o¾a“¥¦×ʲ*YÚ&e÷šéé}I·U%¿ð…/üð‡?nÄ&Ûí7_œñÒáöJ‡ ©}zß–‰ Uã¢e'žž9½"%¯Ó†íæ–}§Û+Ö]Ö&¶ÛçfÄFO<ýâ‹ lžõXɉIˆU”ý.›ìÎ1€a¶IUÂ2:¥H•6EJ•¼Äíz-ž˜­ü5--ÉðÉÙÖå¿¶+.ùX*p꺴2µÏÑæEE›•àQ~q×.YN`œ¹6é<8k‘³úk÷ ÇÎ;Å¡›ŸŸO¦\¹rELùîw¿‹MšBYV%ËÙä¿ý·£ò…0È?ÿóÇ·lù Õźõ«Ãšzö¿MNlyzK$sBÔÄë i“]ÊÊm·ÖµË‡_L7I¿¹I»_VµIîyÐq›4u*P¨Œfem2““K?RrùyDY¼Ý&'¢’c‹3֥ϱÕ(khlŒ¶I™ròmÒupÖÖ’›|å•WÄéÉ'Ÿ¼pá‚TɧŸòûöíÃ&Ͷ’fÊ&mR¼H„R‹{îùã>øÐLdúÛ¤T$¦¾óâ‹iQf¼&¶ì›±M<]Ú&ƒr„úo_Ü7Û¤êUjº.XE¼ X—t¬¤¶ZMÅi{¤Vj«9¿x‘Uw9Q7¹vY‚|l­²yÚŠÌ¢½›Ð·ÇtGk6Qû.ÛÊ×f37Òzˆºm“Ùù¥™Möf·´äŠ›û´N)ô¼ŒMæV¾[ËQ“ i+É0‹é´É.ö@êmüò—¿œ´…ÊÕÕÕ:6Ù]³É¤‚ÛÚ)§I›4…òóŸÿü7¿ù-Wµ¸§MšcæÃâ]Z5lX>7Já‹oœòœi“Ñ‹$ùbb“Oo‘[h³+˹%/§¦[« ÷7˜!)ÍsEJiç¬Æìªæ¶Ô¶[w$wbþ&t¯¦ÛfxÇ”î/V›\‹›zºc-›4öÈÇ&+ÔtInRUÉ;w>óÌ3²‚{ß¾}ªJb“Z[ɲBYÚ&wíú©*‹_øÂžþ¯±ÉƒI4HÏÛs I«ÂVL.¬äEÖ´,B&Œ×¨Î IžºGÖÙ|lRŸpz8ƒ’.µ®È¥nêœ.›LŽ|:§ØH›4“”ªMZ÷;"t¨N|Sê‹£rjºcíʳI¥¹áD¶r<[¾*yöuÙ-P©ûvö™лãTè…“lÏ0´›TUrzz:æ~¶ÉN¯ÅÚí¦”P–³É—^ú‰µ÷£~-yxPµšîLm²a0ªåäO¬’›Ìº©¯M*ùQ«*i{TÇ&ål/΄ÿ­¨]”+Mbæ+eÛ°I˜'©¶”©ðͶ;Ô{ád;ƒgêµC‡;¶kB«>v•o¶qÔÖå® Oóˆê%D¹ jýu¡MºÎ0ôé.¥’Œ…S“6©> è+_ù7Z•wòð :½p’^&Žšî8?—©éÎLlÜ&Ý5ÝÑ‚5ËÉU­éöMÁj+ŠkÆUÛ kæÕt[ûèØs“Ùò5[¥¦ºi“Ýy'â²QáÞ¹ç Ãó&…Aú«$6Ù=›LJnÛökÊþÏÿëéé}5kºKK&&=TÔÛ‰-/½h™X¥N¾Mæõ‰4«›ó÷(­•Î¥ì²î”ZûHkMwª¼[Œ1i7í·nÚ¤Ö“&Y]vìåÛ&Ò ºh“Iön@z©0N©HÎkÃ2Îtž3óËêžMþîïþÞÏ~HýÈì”ÃX8Л$ˆÊÁ/«ã6ù7óŸþÅ¿ø6mÚœ<(G(±IÀ& l›,GŽPrˆ›$°Il²ºPrˆ°ÉÒBùùÏ~Ӧͯ¼²‡C €M–ÊßýÝßûó?üÌúí`“Ø$`“€M6 €M6 Ø$`“€MÔ´Éöܳ›Z­;âhµÆ§ßýœ"^ψO7=;×^q̼yÛÜJå-k¿÷—›G2¥}ÿà³ê”±™u61æM#wdcÓ¶ãÕ7@Ù»vûø¶Mé„û®oÞ¶™¿ÜœÌlU´ûíöÏÆGÆgÚ+µ¶$>°ÁªÛ+Ù)™ã<·m³¹ûáö‡[.¶Ðñ5µgÆÓ‰cÏÊù;˜ÙoS|wÁõä‘oçþ2s„µŒo¿Ö-çðLfJ°åæi¿µü*Û_™ã?6nÿâ™[ɹîBklRÚÌXúicîkÍéÚAÓu½cuâ›GÌc¥ÿ<çVÔoü¾ïÿܲTÍP7¦•¹´ÒŸ¡rÎ[æTOª™oŒè“ãêGœ‘æ®·êå.øj’³kÿ˜yÙo);%œÓ&ž'ƒ¹®º—Jã·ùn¼ñ™µãÎÕª·ؤ×S›˜s[­vyÕ¯Åáe:JÓKjo†4Èè2—ìr\¸eók|ï÷oÚ´Ùºyu/ô3ã-y}Ì\Ž£õŠ wz .Í-õ™^÷í T.£3cÑ릾Sý»»0÷¬õ[sa׷ߨ&…Ú¥G=ÔÆLfÈüãÛÆš8næÚ2Ãéãc™sU{»¹™/1»®“Í:]¿VÓ‡PÜ·ÁsÌøy*àÅklêr¡”6>¶9^‘å­º…ö¢ßàøˆ<ÃÅ Ms#לktË“ß`zù2®ö¶,ó£r&·Ÿ EëjÞ&Ý?jëv¡›´úGã·‡àJ'ÿ˜ž^¸m2¼îß/¯°3ãã33ߨ-½h(K§ ²S‚„¥r‡Î»hšÂ¤x³Ï P÷N/î…cû ¿Íü¯µG·˜ã î4bËí§½~ð›ùxؤuÅô±mÏ6n“î“Í1Ýb“ 'óKÚdþºz›4.§@Mw“Ù Ff&¸äå¯4£83ßa¿÷ƒol›‹2” ^"õ¯ öÅ5ÝÎZڎؤô¶š6Ù“šnÛ=ÒZ©×Óº8È^æ¹Ñ‘©WÓíj±×›ÔþiðrñÞîQr`Áw‘­ÚÛóùsæþÁ£œcÛ¤ü"fÆã&ãÛæ¢¬¡ë²/N¿¨EÉšîüuõ°¦†¬Npaíl/5w’Ô¿äÛ¤l:¹)iŸ>óÍ›67ÐQ4¿kÈÌx˳ެ íy/œ0¿òîýê­1¸¿fÛ–åõÂQ¾ýæ×£NT›œ¹G‡.ÜòÌÁïR:0ȃ¦'§ÑC«sj[·ΜòZÇòßúÕÖl®Ó^ÐG[ù.“\ù“IžcÁtã¸9§µK¶mZwœÆmRþñ¬\¾Æ7)—/ëe_~¿ÚUENÌé…S¸.l`ýÛdR)<*"mRÙÅ'¥GyOy¦L*€ÔÄIf]söÇsVÔh×ãB›Œ]0Ú5YÙvxÏ4û³}Mê‚­NÜéGôgpd&f[£jGØxÛ@³)çÍ¢'¥[î¨kê A^í&Í„šûý]²IëÉæšn<„%}êP«¡ÞKyí&Åay7}ÂŽºÆÆlÒ–G .YÆslí¿3û±^ʤŒfš°Ï5ù„ Ë_J×lý²ŸÙ©qkóbŸ“宎Ødþ›è´M`“€M6 Ø$6 Ø$`“€M6 0œœ;¿=g]~/œlÝ<¶€Mv£ïž»}û6_<‡‘=…›ü”èi¸Ä‹"ü-Wï¾³ÉçŸÞú9À&ÙSÀ& Ä ›Ä&{"N?ÿù!ëkr“=–ƒ[Ùèä–¬ëZéáa¹å×VyüJWÂ}üu»½"_KÄ[1±Ñ-w­b¥‘Cês=ºuk¥·_`“Ø$;Å Sß&÷ÁnÜå‹»ÂHÞ®}rr2‘Hõu_Û¤¿\»Õ¤a!sPQÄþÃãÿöo'wd壋×ÿûoìþÜ¿Þùð÷~Qj]·<âæÍ[¥Îfy//}"/žzê)—PæÿJ¯_o=ú®ü:Μ9÷æ›o>ü¦@¼o›/±.ë*®\Yâ¤Þ¸“•zú†0ÈÝ»÷˜¯×CnRìü{ç¯ =úg_ù‰ð$x!ÞŠ‰ åÊòòu¡ ªFˆ·bb¾@äÛ¤”ÈÅ?OTò®lHÁª¿#·nE¹É?I„òðáÃÚêrl2™GúŸT@ù6ÙN¡´Ú¤RN©_éŠPIñH§”ߎŠÍúî%JÈYÅÅ‹—Å‹úBéi“_~á¡SWN•2xÀ& l’“­·6©eaÄ-ï?ü‡'Õ»jwr“>úèÜÜœj“â­˜X¸vU{¨’Í·›{þÆñ‹‰Gªˆ‰â£†¾˜•k×–TÅG9Q˜›*)^¨BÙ¾ÑVuÍ%X¥Tò¹ƒï›GIL¼~ý†Ïy\h“e·S³IµÀ 6)¿‹·ß~G¾xÿýW®, Ä 9åÌ™saõt­Ó@” Êq­"9=ºc“ÿó–ÿñîïÿÑÁ÷~F­76I ^ØäÙ¤šÁIT2¹™V¾Ý—'¹ö;wj/ºà²ý˜›”YI«J&BÙD†2ÈJZU2шœ e¾M&™h¥Ô2)”M¥¾ÅâI¾VÍMªk©o“¥¶ÓÌÂæâc“’?¼¨6B¶'¾£&”kEž¢LµåƒX£º]³IÉ–×þcj½ÛsÏnÚôì\{¥o ì«Õa“Ã)^kgøÀ?<»¶†Mb“ù÷>5¢¥Tº“›Ôª×›J] j»I±ÛB‰w|vß»âËÿªB)Ötˆ“'T]¸pá£7nŠ“)9õ›ží&o~|Ñl7™Ø^]ž!‡Âª’žç±Õ&5­ì›Ô¾Ž+W‘㱺¦ô.\{ëŽ8ÆfVÖM®­ûу#­\/ fé'qYß6h¢rš=vhmÛäÚ¡ÇänŠ]Q§z¬Õâ”k:7éylDœ4ÁxõÕW|ðA¹RñB¼írg þÊM&‰I«JÊôd}›4“šP椾üût›+È$e³¹I­2½;6iíµ#ÎÝNؤfö°IQ¦Ö¨·6YXëݼMJk ßÎmÛ¼iÓf©qþ+jÒ&3^Û² å€Ùd`Š=Ú"6Ù/6mi–V¡\6¹8£Ø9}ÄdqJ>À)ׄM:tèðáÃâßG}´q›,Yõ.,ÛJ&m(Õ®9ëÄ&=«ÿeÊ-QIù©ë ìÞ½gR w{ÒÈ!„>š$BYÙ&¥Ÿ™ÙïD(å_ •wAžI»IñºB޽ðÏ)ÏN5݇ÿË‘ŸþÍß&ˆ·~5Ý^PvEÕl2¿ÖÛt©$¥×Š¥0ÄV’áŽ(_·Â¼£YÂÌX”œŸ™ß´íxPHø"š9üwÛXKͪ©ÄVX ¶%ÉŠÂéá”öñm›‚é&9²¤ê[¥ØdûÇÇ6µ¬{d.enžmƒÓrÄn&G²ÂT»ÁKQüû7"“Cª8Ưƒäe’@’©Ìp“,@$1==­~âm›Tß&9Ë(kLÎ¥MQ44»T0[m?«¾SkQ¶1Vä`S=fù&Úð>så>6’L]Ç'Láíþã[ò²šðòN_#í&‡Ô&}D{ {á4e“…ÛÿòË/ç[àÔÔ3ݱÉÊmDümÒt>ñ¶Ù§—«ž*Þ6ÛŸºZ/œ£Žú÷Â)´ÉLÇë2Þ63gÇ·ÍEÊš6d%gdQ¡žÆ«H²ªªSÚwÍê¾…6%Ps§ä–ÜH»É°Ö:½GÙØdß´›´z¡£y¥Í&ƒˆÍÛdåv“²ÏÅ&ãëÛV¡×nR>ïYh‰š›ô¬ëP»Éõ_Óíßnr ŸÔ5›œ››Ë·À‡zh½ØäJ#éÃR‰Ïf…²¬M>1óï/]»”¿ekºÕùÍzaµéä¦M±tÎŒ‹×QëI—;ëÍV7["g‹‹’:66.›'ŽÉƒM Ñ51.MÝAûÉ”§Ò‘¨•ÝÚ$ùJÉMb“ÍØd ’ñXt‰Ÿìë¼ÿˆt“FTk7é•7cPÇÂè§—ßÌŽ’™c“ $q~8ÿÔ™?±0è6Ùng¾¦>¸ S•:•Eùê9Ðn¨¿p©§—çÔn»£“vvÑzá(ó[š‰{Ïlßä°ÉXÂôþÔn›\Ò™)Öº%ªÃÅÙAµ“MÛ®e-S«©Ïv¦‘Û¯oL¼yiŸsJQ€Z6™í©Tv' ˸«w0ƒøŒ^8ݳÉô4K3vz[÷Y•,¿»¤ubw*Ž}záD-8CŽ«¶ÕÝÄ&kÚ¤ª’Ú°ÆÝÔ/íF¬=7Zí­1tí&}dÅ›Æp™{–©ýQ¥jìú¿w ~nråÔ©3Ê3Ì;~6'Ï-ëm* ê?²b~í6ÔzðPØ÷( O/›ìPÈÌà:Û)l2Ç&•ìܵ«°Ýä0Úd)ãöO×J TÑÖ÷­ü¦„fDZ²:è=¯é–c¶»òdA±9¸bƒõéeFzEû&ÉȶÒþãúl›¬Â!ÓŽ;Lõa“}UÓ]ÓL¼ŸiXýÁé>·æú›Ñí&›,«hwç¼™½+Ó§{¥ÝŇT‡ëZéò ÃØÜë nðÙ›d§†­NÍ;lþ³Æ|ÚM–ŠzVÌÜä:°Ià0rÂ`“âÅN­W›lgvƒ~Ø l8Œì)pƒG¼Ø)l°I€ælzκü^8Ùºyl›l°Il°IÀ&›l’£Ø$`“€M6 ¢Ýà86 ÕlrEÀql`sG6°I€†m²}â'_ûÚONôÇ}±¯6ÖMªZ‰M”³ÉÀÏî¹çÃøÚK ¥.ù¨‚ä©ëqÏ=òÒ‰lúÐ&Ÿxâ›búŽ?T'Š·b¢øÈUìöÊ NTX÷6Ùnÿýwb ^çï«ÙdײŒµÉ矞×úºÓ¹IM( U2°É+.@0 6ùÔëY9K²†÷„E§h\¬t /}-Nj~ç©4Ii,ûÒw¢Ù¾óúJކs̆åüÉK¯‹yžúÎ×ÒÅ3ÙPۊҷѧ+kŸrN¬{¤üiá’?ÏšîD(}T›€!ªé~=T½{⬤T:©}'^ú1Ým“+Æ Û²ñ”öëOÝ£è£^Ó:¢>ᩱ&©S±¸øÈgEŠ"§J†rH²’šPæÈŸ»É¤ÌB•Ä&`èzáW“NÙÖ¬18›t-›]*¿ÎZØaÒ‚3³1²Rþu¿eZdf>…!ÌP櫤®™eVÒlCiåú[¾›LSz½¶IéµumR«C÷³I²}ë2Cé“GT3”ù*) ôÊë×o ¸Àzo7)4+©ãΩDŽ*‹£êã`z¦bz¥`Yo›Œ+©£¾A™ÅeE¹ÏŠ”O»ÐwÖù6©µ•ôJl†¤NÚ“&yF½N{EÖA«}n’f—ù½pœ6©?!(µÀ´•ä=÷¤Öí‚^8ÖÂïÉæ2ÊÚ¤µÛM¡Pb“0t5Ý}9Eè“Üd°IÀ&±I€ê|rýÖ'Ø$`“PÑ&?¹)à86 Ø$`“€M`“ƒÂò'7—±IÀ& ¢M.ßp›h€ÿéùßÖ›À&a¨m²‡‹c“ؤ/Gß=wûöm¾õ~;ŒCh“KËmç`“Ø$6‰Mb“€M0+·ÂH^„¯+jÐ-[ôù¸UØÍÎÙdÙ}`›\j ¸À0ÛäŠAJ†ñTÒ6îºë®š6)Šº+Yl§¿€v[?í2cEʽv…Ë&åG꿳I/Tm2g_\…`“f“ËË×Ož\xóÍ·~S ^ˆ·b¢¿Õ/!QIÕüjÚ¤Zf¬’Ý\ ¥8âPˆ=…²0+™c“>‘³ïþ6)Ê9tèÐÃ?,¿¦;wZ…ÒÇ&sDymòã¥öÇØ$ ¡M ˹vm)±@1Q|Ô…4íÛ»g¯@ú_m›ŒJŽË:¥ V³©J=+YJ( ³’…¹IU:µÅóeÚÓ&¥JjezÚäòÒ'*ÚÄdû±I€³ÉååëVLtPÌÐé4•”z!…R±ÀêI5U¶vîÜùàƒÊ’Å ™Zk.a¹â:ùB陕¬›lßh'º–#”þ6™d%U•Œci›t¥'Ò&?¾!àÃf“QòLráÂG7nÜÿšäY‚µÂL•T…²~RMÏÕ«W}ôÑÄ#zè!ùZLl®1eu›ôÉJúä&Í”¤Êü–£þ‡ÑLLZSÈ…6i•Kl`ðl2I+ZUR&ó]ÐLLjEå–àTIO¡ôNª­ìܹSzä?ýã?ÉÂÅ ™§Õ¯ò6;ßN:ãSÓ]ß&µ,¦ú6ʤB¹›”%'í\±Il†Ô&Uÿ3‘ŸæÛ¤œÇºx"”®òUÒ&”Õ«h¥8J•LäF¼•Ši-|÷î=“Jˆ·þÝnJ5šÌ¯.››ÔlRËP6h“I<üðÃ…6ùIÐ%«}Sü×n‹×ëÏ&¯}|ã6 Ød¯l²0qUß&E!=ôPR`R¸´LWew"”eU²Ô‚êÛ¤« Ž:±ŽMÊr’¦Z:t(ß&Å‚?ýå©ö•Ÿ|î_ïÿî{ëM(±IjºSÎ;_ª¦[ëÛ›„Öá·f÷™ƒÔÖ"^Ë”9M'…Gzª¤Ÿ.>ªM ß’4’›lÄ&M•Lz2=üðæJš6)æù‹cçÅ6ˆÅëv»½ÞlòÚ   N·{á”±É_×±Iµ¦;!¿¦Û¿¹dƒ6™“›ô Ç|,ýX"^Ï]\w6y]À†Í&{ÿ„ îØäÍ›·:× GÊS§ÎTÈÓ&=Ëi67éêÊí94¢\vnnN¤š›TŸ[¹>ž´xíú"6 Ch“aN¥î³Çë”Ð-›üµ«Ù_#O²5,±xaŸnÏ¡·óÛMæŒÍèi“‰Jn‰j“¯¾úêÃßûERƒ/Tòïþá}ëwM ¤MÞt‹Ø…ºf“²fçž^^gxî csWÈMæXi¡Múä#]6)URüë7üãÀÚäâu R›”²U¶~¶‘<ÇqnÂ&£*ïNެXOǪs0ë PYaKT*ù•ï*u@°I€A´É>©|)o“ë™ÊJÚøa¬©ƒƒh“W¯_Å&›8°Éþ<ŒØ$6‰Mr±É26yõç`“Ø$6 Ø$@¯m²&Ø$6 0D\¹úÉl°IÀ&›Ô'}JDùøíoë3[d“W>pl’ ˆj6¹,àØ$AUlòò•åËØ$`“1lqäÈ‘^xá©§žúÑ~ôÆoüæ7¿Á&›Ä& Â+^~ùåÉl<÷Üs‰P–³ÉËË.@€M–ŽµË¯mÝúÚåµµž,ÞWûb)0(qbb×±NlÃÚ±]Á¬Ha嬤ÐÇ'žxâèÑ£ËËË'Nœøö·¿-¦¼þúëØ$`“y6) Gfb„ŒtÔÀ ו³xòQÉS×+ÂÓº·É`+b•ò—nO8ѺºRÛe߸øÀÅ /¼ ¾ ¡’É”sçÎ'êÖ­lòÒååKØ$ ƒM®Å½.“6+k`>ëò±É®e›µIu÷£e\rp,–lNÌ=ÂÒW±É*ñä“O w¼~ýº:Qê~%›\p€!±ÉTk´LÞ„b?jVO Ík»&¤……D™¿]¯EEo³)Z—bTŠ0ÅòÌ%5wíJ“”ƲöU›NW ÇÛ°õµcbQtºx&j[QúV9\Ú§fâ0)×Au£Šl2Úø¸4ù!jX!vîÜ)þüü|2åÊ•+bÊw¿û]Í&§§§Õ¶•â-6 C]Ó},Ô¯´Vq>‹„ÉãÜš™Ë|dT¼ÚÖÍ•â´É5ã…mYǪõšîÔ“U®©Z•í³"óp)ŸjÕÜŠ¼êV­ïiR~I›”›ƒVˆW^yEœO>ùä… ¤J>ýôÓbʾ}ûÌÜd"”šJ¦6yiIÀ†¨ŽlÆ—¨’ÒÄ0“~‹TÌf{¦ë¸êd3ëRgNÎÇ&]Ëm•fxI ÎÌÆH=æ·"íp¹×e®¨°ah:QI'a¦!ûªÓÅ/ùËI[¡\]]µÖtO‡áªé¾xié"6 ÃÖ§;J³Y“aIÒÎm{þ6i]WOlRzm]›ôkטڤҀÒß&}û9‘›¬§’;wî|æ™gd÷¾}û•,Ûn›€¡i7)Ô$©w6*vM}‰Ä2#^FMwŽÒ¹×¥×tÇuÁ±Ê&ëY+XÖÛ&ãJê¸R\]\NóYQîáru¸vö™лã×tg7€v“uTÒškÄ&›Ìï…“vþ˜Pj½v(,™0–¦{™^8v¥Ë]×DV¤’~?IŸ›¤Ùe~/§MêOJ%,m%™ìu¶ÀœY—Ë&³)ÉdÕ†¤Úz·›ŒnÒ§»c*YÚ&/. ¸ÀpÕtyt¡Å¡ú¼ÉÆ6[©Úæy“e#§3MM›üèâÇ.@€Mb“®¢üX8…ql—Ú±pª¥Jb“€Mb“Q+°IÀ&±I‚À&°I‚è{›üð£\€`}Ú$tl°I¨c“×XŸ6y› ˆŽ6 Ø$Aumò‡×\€›$›l›$l›$l``lònï@ ›l›$ˆÆlòü…E À&±I‚À&›¬j“·n®RÍ&Wç·oÞ¼}~uµÙyPlkâ@½b£m³µºº°ãþ1¹Ï…fõàäH+8¾¸6 06Z]ëŽ0„Ô”²Éä£ ÆéZ¤TQ©M‹.©¨äª°¶û£yVCƒûÛª¯³b5~[›3g°9ðu›l²¢Mþa¬Üúu¡M®®˜ˆcÁk[&ÏÇ&«x˜X]S6iµ Zj–­V+žG|&,Ò4ÎÎæ slrUn6Ù›üàü¢€  µMþìg?ú˜¨ä–-[¾üå/«smÒ¨#޳•2ugæÿb‡ æ‹’ši’ÒXvûD4›U˜Ä,òÓ–Vf°\K¦KÃÉâå}ÊG‚ÓÈö%2Šf´Ëšá…o'&îI¶Vΰ}r$ÚPKÓ½ Rœ‘¤*%ve@âæwÄÅÇ2´Ûx—#¯µ&† l 6¹% ™’/Ô>7…5ÝBÕKÚ†~Ù’Ú5GtÚäªñ¶l<%Nƒ®ºZO©Ñl¥ùjf ,S\‰LSÎì6©¼Žæ‰3š¡ J Œ·?š”IjÆÅš†ª§Ó ˆ>‘YÒx©°ô®G6yUÀèÓ}·tJñbúÕi›Œ\çÀ„tJ‹ÌùÛ¤kÙìRFr´¥¶_Ôf“™KU Õ)ÍÛäfµmåæí”) Ù×#q¶2N6ÞGKjh^r´¥5ÙôoI`“±ÉÄ Å )Ž¥l2­õî®MšN©Í&5WUCuJ]›´ÔtWµI‡;º¦hNIn²‡6yîüÕsØ$`“Õl2°±¤Ž;§¶:ûž¨cÔJRU@Dz9¹ÉùíÒ ÕÊëı¢fÙ®BÊgÇpw»Éœ^8_”%Ìk~™SÓÝÊÔk[kºcŒv9Z˜v“=¶É® ¸5Ýöšîü§—¯*½^Òî#Ö^8áÿµ>7I³Ëü^8…5ÝÚó‰¢2Ã^8i›ËH"³S\6iëÓö­q!h˜E¡ÂX8¥lR}>P]•d,œžÚäÙ®žÅ&›$¢¢Mž»"àëÓ&¾{:6 Ø$`“Ø$6 0X6Y¶…æëÕ&Ïœ½"àCj“/ÿí«•mò¯þ·{‚M6 °>mòþઠ¥´Iu˜êÀ9 Ø$¬w›¼,àCj“wÜqÇÆÿ]¡Ä&°IÀ&›ÜñÜK„2±É»ð·ÉwžÿS9bá#;ϪSZüµ6:%¿¨hp»žÚw쬶ìÎGGäºä‹tNÇJµÒ'°Úäé³—Oc“0Ì6)þ­ ”Ødbu‰®Õ´I9[b.›ÔæÏ_é;þãÿ¼ùúJ°Úä™Ë.@0Ô6)øßÿo ¡|cv®~M·æ”.›Œ,í®¯?üïžoþ¬›|çØ?~+.°›ìÿ?ÿ8ØÒoýl°I‹M¾ü·¯ •ÜñÜK´›ô´ÉDÑö)®ÖXM·M+Û¤ç66 0Œ6YA%M›T=R«õ¶Úd&ƒ%)--«Õt§yJl:o“§Î\p€¡îÓ]V%°Él*Q†ÙÒ±¢M&zJM7`“µÉßÿý߯ ’.›ô¯é–b—ø™Ô53›XÍ&ù{çØO‰;Ш¯«ô¡[7¸lòô% R›¬¦’5m2»¨çM¦ßôNãÑ<~ëq=ÓG®+š¯Ñú„  +l›€a·É:ãt3²"`“Ø$`“Õm²T4µ^5טæ•4'@—mráô¥l°I¨h“§. ¸6 Õlò¢€ ¬O›¼MDÇ›l’ ˆº69êâ<6 Ø$Amrᢀ  ©MÎÎÎV¾•–zBA`“ëÐ&7nÜXY(Õ§—kO,WÁ& l`ÝÚäwÜñ‡ø‡Õ„›$°IÁÉ…\€`xmrzzºšPJ›¼ÛO›\}í±;Gâ˜<è¹ÒÕSÏ=pÿŽ…ÕÕäNC`“½´Iño5¡¬c“Bï™<»àÁÉɃ~^¸º°ãþÍÛç‘H¢lrþ# j›155%„rii©~M·æ”¦Mʼ¢PBKÞñÎ8Wgw<¦MŠÞÞ?9ä&Œy”œe&‘i”¬Í£MÊ“À&›,c“³³³B%§§§i7Yl“¸MÈÛjXó-¦Š× ;¸SXßÂŽûÃfðéÁÉ;¥ƹÉèż1’¼Œg¶•¬Í³n’¢”À&'æ?:MÀ0Ûd5•ÔlRõH­ÖÛb“¶Úê@1UÃÒgñB›MjóÌÛL±¸äƒ“qæI"°IÀ&Ëõé® ’µl2Î4º3x=2q`¾!›œ÷*YnXXgŽSØ$`“~6¹aÆj*iµIÏšî°Æ9¨ N¤íµÇÄk¡˜Á¤¨ÎÄUWnòΰícŽ;Þ•”#óŽfÉÚ< Ï=¶c!Jñ™nº‘g“'?p€!µÉÊ*YÓ&£&Œé‚HŒ'M­6¶k¼Sö±ÛäjZÌý“‰ƒª%«kç‘¥†S¶ÏãI„¿M?ùáql†Ö&ë#+Ø$6 Ø$AØ$6I½²É ¸6ID›|ÿćïc“€MQÕ&/¸6I6 Ø$6I]¶Éã\€`Hmrvv¶ò­”'ؤà½ãÞÃ&`hmrãÆ•…R}z¹öÄrl’À&Ö­MÊqº« åPÙäê©ç\dÄE›l2k“ÓÓÓÕ„RÚäÝFÚd`fé ŠÁX†õÇÅnÄör Q‡v$ˆ¬Mžp€áµÉÛáhÝ„²ºMö«™ål6I8mòýó.@0Ô6)bjjJåÒÒRýšnÍ)}lruõµÇî R”qæòþŸ{àþÉÉ8‡9yp5›Ô Sˆa*q‡XR|¾¦Ò¥ÂIwz,•¿šc!ÍŸ†óc“„Ã&çÞ??‡MÀÛäìì¬PÉéééFÚMzÙäZEw¨kw © ,/°¶àmð"˜ÿàäï ãœ<úœø4tÇ 1)5Ôùh©p‘ûwÈAù¹ËFóØ•1øT¬›$°IÀ&­6YM%5›T=R«õö¯éš'$SÈ 6O „#a¶Rml)ür^™'±ÉpJ²xôbÁcÙ¬MfwJûÄ& l°IÓ&«©d'l2ÆVk³Ý&7o?`Ô/xÛä¼Ç²êRam{”u”@Ø$`“·7lØPM%­6Y¹ÝdÜtrò@Rë¬Úžø$xUxW±IŸeÓÚöÌÚ±Ä& ·M¾ûÞy R›¬¬’umRBPÐ 'mÅt¡QçQš<:jŸ‹lrµxÙðu°òàóyù"x=9An’ȵÉ\€`Hm²NttdEz½ƒh“Gß=0(ôÞ&;Ø$M`“±þmòØÜl°I‚ °IÀ&±I‚À&°I‚›<'À&›$¢ŠMª?ÈÏ=q`PhÆ&ggg+ßJ;ú„ ‚À&À&7nÜXY(Õ§—kO,WÁ& l`ÝÚ¤§»šPvÇ&ƒ!³Ã¡+|JØ$@ÇmrzzºšPJ›¼ÛˆB› ðÎ`tÃŒ.ؽÐ>¨w,‘<äœÀ&`Høm6úÈ&o‡£uWÊê6(àÄD胷‹†½±Û¤1 7fCôÊ&Õ>Ý\é £6©jeÙ¤ˆ©©)!”KKKõkº5§tØäöùƒ“wNL߯¹I™¹Œ"ÈYŸîx,š4y03Çý““¡MSÄGAi«¯=vgä©Ñ›LŸ7©ýJ¿øÆíµO?“œ~ã¤ó÷¼}ñôòâÇ÷÷_v~›üÁYÓ'\R'Š·b¢ø¨³69;;+Trzzº‘v“¾6¹ºúdœ_œ—… ŽLPr–bú#“rŠX [Á¾HÜT0qÑ!zn“J./~I:ßãç÷çåöÅ3Éœ~6Y8ÿ—_¹Àk^óš×¼îÃ×ÎMjB™£’MÚd5•ÔlRõH­Ö;Ç&Å‹âìcd“ Ï=×\g-sÕZÁ¾ˆÕ4”I“D¯mòñóû>½ý½íïh øE5­½>ùÌrœ¿œoŸ^nï‹ßîÛóÎçòçÏÉwÀ åO —üyÖt'B™¯’ ÷é® ’ؤx-|rr‡bB+Ù¤ø4ÔɃ¡Lb“D÷lòÝ÷>è6š_&}¤'Û_UÓŠÉkõEâ {Ûk…ó]¤Èðš×¼æuÿg%5¡Ì‘?ÿv“I™9¥5f“6l¨¦’V›,[Ó6s82’ZcT™]l“a&2S”0Ó;Gîß>åý`“gL›ü¬ýH¡Mª•ãŸÝ~fO-›€ÁÊP櫤®™eVÒlCÙ›¬¬’MÙ¤l ©Ød¤—Q8lRf"•^8RL£±¢÷6d"o?“­é²•%mò{{+Ú$™^óš×¼¸ e~ÑÌP櫤,0_({?Nw§GV ,³Lš°37O "ºo“çf/œ/ý*è…óÅX ÷}úÙ™_ ë²Û_ '¦Ýt’‰ª&îi¯IûÌ™Ÿ?膸gw¾Jæôòî#›ìP¼öX’šœ,Õ’þ7DOlrîýóë‚¡ŒŸ´ï;ÚÄÓóíÄYˆ§Äó¯}Ú~$ü(o~zá`“EÊÊuk“±nl²44ˆ€Ú5Ýþ`“M`“M`“1´6ùÞûçßÃ&›$¢¢M?/À&`Hmrvv¶ò­´ÓO"ˆ±É ó×0pT±É7VJõéåÚËU°I›X·6)Çé®&”ݱImàœRŸö<*l^ŸïM6©Ûäôôt5¡”6y·…6ÓHks< ¶9w¡]eìv-5Òj)Céœ WÙaK ÆïiµîC¬››|ÿø…÷±Éœ;¿=„«4`“·ÃѺ+e-›Ü<1©]!ûèe“ú*DtÖÒBƒHÆï989q`››Æ=ý7m;ÞM›,».ëüíöþ±Öæms+ÎMF¨dî;´æôȳØ$AôÌ&}÷úõ¶4ȲDwÉ©j”²Isq¡,´IÙzRf(÷=|oýv“IJ2±I5CyW6úÚ&+«dS6™6(œWZ@¶ô^8šMʼ Ò g5Ö2g?ñ¬‰ZÊ·k¥Ñ 'S¦²FE(íOrn@vJÚ 'nȉ´ ´Mª*¼Û?x6ØðøL»+6a¾’›$ˆÚ¤P4!”RÔJÚÞ`Ûd’›|å6ŠÍjî 5ݦG¤MÖ‰N¬˜ts)1wŸ­Óý5}h“'N~x¢À&3*&mÛXK­;Nò—­pæÄê“k·oÛd”c[D‘B±Hœß´i|lSºÒ‚ù·W¶_iényºF}¢UŒÍDE){çÌnÚmòïÿÝÈc‡,~y6“›TÓ™-l’ ºb“ªó ­lÐ&Ý5ÝžkV¼Ë)Ýnòôô’YIÙçÄ_?/Þžú塚í&}jº×¹Mv(ÆÏÓ)ÛEö†éò¦vyD?ÚäüG‚œ_æL(Ž­±ýªFš53Þ 4.069enÛf1gâ âíØØx bŠ’5l›‹XìP³Ò8E*V* Ïßš°Ìl¹uÂU„åÇ/Œµ{Öt·¤ؤPΖ¬'7I91===©„x[³Ý¤t5Yå]JûÊöÂ)ŸªÔK0ólòÆ¥„Gj}º/½5ûŸü—âßš5Ý…M6™éÈb×>mJè‘3c~ÍŒ…Ú'—Š}T«’Ž)´ÉäSYù>SÉ&EÌ È/­\M÷Ù\›<ë[3ND"”ªJ~Zñ A±}ðÁ©’¦ðåH›Ï³‡<¨³lÅܤðÈ·ÿâ[Í>oÒX4šòÔSO }ÿb“MÚk½§ßýMYÉ™ ë¸gÆÕþÔëß&eU76I ¥¦’ŸÖxÞd5ió)Ù¥§ÞÍ(+–o“"ÌV’fԩ鶪$6ICa“'ç?:é¶I5§(íê]Ó&ZcùÑØ¦Í²ýâܶñ±±qµ¦ØQÑ5¬ ¦hÍ.Uù“йóÛ¤»ª=¿ž½D»Ié‹AóÉ`JP n©éþ—~ŠMDÇÛM:­~O—¾µIÍüšµI59)UÒ¿dl’ †Ã&•®-QkBWQéÑ¢w Z<ÔR¤PésÍ/[mS’§ øÌ÷ÂÉÉ\æõªl“i»IE"õ<ðØc–^8AËIåSl’ ß&kÔt×*Áç A×tc“M†6¹ð‘ ß?ÞÇLg,‚À&"7Ù!›´Ötÿûߤ§—ÏÎÎV¾•vú AMb“Aô§Mv"ÿ×ç¹ÉNÔtûô€‘7nÜXY(Õ§—kO,WÁ&‰õm“ó  ·MÄç&ëçÿzK÷mÒ? :ãtWJl’À&±Il’ ÈM6š›ì_›l¼¦»?©h“ÓÓÓÕ„RÚäÝFÚdfhÁ–},ÄtnetAslÃÛñ8×wd#qQðW®E]ïæí,K‰™2ó0ª!Mb“MvÉØúÙ&×ë^7f“·ÃѺ+e-›TG¯±Œ‚Úd<·Ë&ÅêcjGà F¯ã13K9æ!ˆ<›ojÿˆìß;¿MÀ&› FV$°É³ÉøÖÜ´Ö÷K8â½So{Îéy(„Áx–Yó‹ó,¡þZò Q?rí{#ÛP|ÞkSõ?2u_ŽP6ð]Øöß&`“ƒ²Ê[}ê8yD¢—6yæ’ 6©æÌÔ´YxŠ>JóLaÚIN”w>×âI $ÿŽ›Dvþ¤dYfp_oEÉ›ä¾.o–[§îS7)\ö¾­{¢MºwD–¯l¼\‹ºIá²\9¹ÆB› ·*³j{>ÉïÈÛ’mbY›ÔrF3ÉE{Ñ¢5ʵŒ¢Š­Èø6­§v Ô9m¦eñK呞Ÿæ¯Ãÿ(io¹~›öÝtœKÖMl›$ˆa±IwÃê %*ã¼lR)3¸Ææ'n„¦¢É[»q‡Žæ _GE‹+E‰mP?µ \‰úe“ü¬•ij ÊÜY`fž2GÞ'`“Ø$Aô—Mž>sYÐ=›ÌæNÔl„&C5ïXê4y¡ÞnÍ™3ùý–­ÎLšZMSÛä‘8ídG5gS³¦;گ؆յ{yí(åL4K0wÓzëÚd&çg,žÝ÷üÅ­0ø´}tn§²v»MÚ~þG)Ç&Í}ïáoÓµ"ËÏÐ=' ›MnÙú<6Iؤr#1îèšp¸l JmZ°.¯ôÆ‘©ÑÑÑ4cÜÂÓUã6Y?7YªÐ:¿ç‘׎RÎIJí&“Õ9›´}¾6i]¯= êÚwuíÖšn믣ÔQJ«ã 0ÎÌžý6sV¤ÿ s7 †Ç&…JJ°I‚Z›4›îåÝlÔû‡3'd»cÉD‘61jæh$5ÝZó5¥N¹[ëëUêC›­éί]­Ðn2ÿ]xäÍ£äšXÅ&レ·+(k“q¥sÔð´H˜l‹Ûkºmº\b;õ#¬ØdÒ|Âúëð?Jùî¥}ÚÃߦkEŽŸ!}z†Ý&•´ %6Iýa“g/ :÷¼É´~3Éj¨$”›¨µ2K[<éa©^´Ù¤91³®´I¦¹dØ‚Mݤt-VuKj<Íš¸Ê6YªO·½?„Ã&Õ½(qä­G©èЩ}•ò»b´² ãf SjÛA­L׊’ÅG÷X¬Îºï™>U¶oÓOùáð6êƒ -e®¨ã}ÒË/^u‘VýÎï•Í þó›´osÑ1¸u¢çq Ï“}¸c<ƒeQ5ó4{ËÓþ‡®Ow•¬c“¦Sj‚(?QmR‚Mýe“\ ¾M¦c =.»îå2§Âò¯Êa÷ŒúŒCXgkÇ&ãV»ŸµÞ¿Ï²}›µmR= áÑÝCwZC·¸znÙdrÖ2=êx‰[ÓADÞÖoáñëœì }€ãVË+ ñV0Ìcáø~¥î1êÚå©™$ëØ-j†)|R6·Êµ¸yÖíTw_î‘k7ýÕÖúm6f“ñ“Æ´IÏî³Ë™Û+üqY·ÊõãRË”%¸‡#·œ!æémýŠ}Ö^¸G^§·ãÈ×¹.Y’sí-åoìô²æú6Åô½Ãk“•U²–M†™Æ¸ÛM”€“’^8Âã|¤”Èì”yl’À&;™›Lï¦#î Ç4Þ»¨ 4ìºj{Jž:`q¡ÿ‰Í‹«)Í!’«Ø¤käÆüeUEH‰^ÔGÌÞkÇÓ¾ï¥m2w“2+Ê ݺ®[n5h±MZ¿Í¦jº[ªV6d“‹eFìL7RYÖ¯#ó㊠;}Ñ!ýŽßQ¼Ín›´œ´ÖÓÛùç¬=wªÙdräk^—¬G)ô_Ù½S[G[YmL÷ê0Ûd`dE›¬f“éø¼¶Qƒ;=±T«,í6fM†ÕµÉL"×á²Ö›ÍÖTÏMÊ£äUunkÈey<7ÿšOï}÷§ôÞlñ·QÙ’ÓÖï½´s—ú&9þý\ÓݘMÚ›r:GÖݯi“–“ÖvzûÚdÉ=ªP¿Ùd½ëR)›L.ZM‚òcP”ò¸W³ïN_çÏ& › lòüUÁúÈMÊkeô·Q›l«Zm"7éo“Ú$Ó¾n»IyEÎÙW±„MæZEƒM-ëÚdî÷Þ˜M:¾ÍÆl29?ÝUÕ½ªéî„Mfš"XkxsOZ×éÝ'6™:bíë’y”r/ £££éÁ×Öîf¦;W? ‹MÄ@Úäç¯~p~ö‘y/Û-G©@4þÜß=Új•µÉ$‡TÔ)R½§VÐV¯ÒÊÐÈÆ¹vÓm“æâÎ*EsßËÖtؤRZQM·e;]99gÎÒcã]ßfó¹IµÞ6ëvemR­ N’yùê}³Ù˜õ먛›tj–×IëšÓ3-]vÊÖt'G¾æuÉz”ŠjºÕ&›Ú/¥¸½ 6IÄ0Ú¤z±ÎtQ.å9U-÷NM©-™´Ì“u¢ºøèžœîÞzZHÉø®È¿²8SdôÂIvÓe“ÖÅÍ㙳ïÍÚdæ±&F±ZËEëvf”âFænúl¼ëÛT TÏ1ëDÿoÓÜ÷œ'i+ÊÔÿj}¿”\¾çó}Ôç›äk“®Ó[í4£ö ñ9L‹loD¼<¨O9`·ÀÕL9²Tyõœ8άMÁ&‰A°IûÜ2;} ÆŸ¨ñŽÎؤ>äO‡6ÀõHó:ëª@¹ÉÕÕÀAÅ2‘¹+1¦`“ÄÙdüÐ]ëð9™'û€ÑÙq#¬‹Ë·¦ƒI¼m.žó¼=ûðÄŽ±1Ô2ejnÌ5œL2¦È½££Á¨„÷Nm —òeHÃŒ«Ùâ|YtÙ¤+±§vâw@Âá:?ŽÇn1'æ[QfCë'&ŽŠÃ+ÎÀ½££{ÅürïÌܳ®ì™aQôCgÆÐq&W;CrTÕÿxºV€Mfl²šJj6©z¤Vëm±ÉPì’”¡Yé,3—2iNñ²ISOçó¦DŸ×t·¬V~¬julÙüÅåÚ£»l<¸mfqe]ËÆgǜȱIW6Ë!sÆXÀÑhÑÐg9é.]tÒa⼎g¸¸}Î|Y14±h`Cﲘ%œÝTÛD÷’]»õ‹ <2%þº.˜Ø¤ùÇCRššýu:gM·åÈ{ž!%2£%Ž'¹IÀ& m²šJÖ±IÓ)5A”Ÿ¨6©NÁ&‰¾²É?º&èZMw]›TÐÃ&MókÞ&ÊX\!»Æhœîìí¿È&õ‘¦KØd6)›ßò²TM·y$=ˆ:³¶:ëÄ*øâöŒŠINÂÄ&3#)kynÇÀÊÚÒ>6Yê€øQã<±IÀ& bÆ ÕTÒj“þ5Ý“Ò ÕÊë¸î;´LñZü×tg§ØdX5ÝÄz³É8éÒ[›T³nù=TJÕt«wq׿5d“á1 åFÎVÎ&‹šZV°ÉœtZá‰gÕ÷Ú:Ñ'™gÿâB›ÔjÆ­yî¨À©Ñ–Ç¡ó·ÉRÄœ³¤Mꇛl² *«d-› Å.é.#'ŠII/é|±þÔ§äæãrè…CtÍ&?t77ÝÂw‡UÑål2S#™·¸=7émTQùZg÷ƛڡÎlVgjºKÚ¤Ú ÇÿxʪOGôo7™HóˆR3í´;¿m¢³mvíÖ/N;ª¦M&‡.Uö=£Ùæ–RÖ&}ˆk×dÕ8~Ç3wEØd­`dE›ì¼MZžhs$í3¥T@+5‰©'éÕÅG÷ì-Z½\{b¹ 6I`“uPŸ\ ý¢\…­ƒ>+ý¯KŒYìÑ•»/Æéþ–®q ”2@»ãz¨gBÝOG•,*¡ìZ4÷*\‘>4‘mß»saÉ}ðj‰=ò8ò}÷xùNü”|Êìå8ÝÕ„›$°IÁ¥KK—:f“Á#Úp2Á3ï»7œxdjjÔ=‚6‰M®{›ô¾2;2dGwß§„FrÞˆAêж}ïÚ…¥Ü€õuTÞû+Æ&;e“ÓÓÓÕ„RÚäÝFøØd©Q Í™ƒG—ßÏ ˆDØäå%AGÆÂ±Ž˜,&ŽNm:".+£{LÝ'/ÖZ¦aÑH03¤rK3?#¢Ê­NÌ=gLTæÌ¹ÐËëãÖt@š·ÍÅÍ},JG9Ê´ngÑÆçäK\›d=JŽîÛº'š¹•3°ââö!1¤æ¡S@²ï¶SÑ>®’õ )³ïÞÃWZ5Öný6ÃrÔßQxÉÚÝ›Ôò9—*=}ÆùX%ÉÝò9ÁLg 4Ë:Q)3ã]9ÿ(Y\þ'm&=Ù­ ‹ë%Ï=ªpaQט9C2à™eú^.¬¿8ë9o¿28NoÍ&ýÏOsí½±É 6TSI«M–«é«³¥5ŠWêG“aûÈÔ"Òºol’*›€N·Efß×ånjûkæÑyzy“6YY%kÚdØ&rDë…w» üRëv“̬öÑ¡j(AôÄ&¯\Y¾‚MôÝ㽞oMöxœnFV$°ÉÐ&?`“€MQÉ&¯~"À&›$›l›$ˆîÚäÕ«Ÿ\Å&›$bÐmR>{¢Ôóœµ}ýÐì)6IØdc} åIs¬t4ÇÃ=Ç•Y´=ÿ²¥§›·¢þ±É » 0 6ùÞû§¬`“Ñ6¹x]Ð1›TŸ£[Eƒrü©²ZùŒcÑÇ Á&›lÔ&ggg+ßJyBMvÏ&“GòÚsfícÅÚÆá0QG’(;Î@¹­é€oÇ+ºoëžh«¬Ã`ä_¢N·Ò>jNÝ×èCò|cÀ&›´É7VJõéåÚËU°Ib}ÛäââõÅÚ¤®}9Cäy}«Žy함Ó2£õl2 X^6;pYf;ò2c»¯-$(Óx^±Å­•¢°ÉêãtWJl’À&;o“zMw]›Ü3šæäŠü/lÚh(Y+7o|O•©DË`¾Êvš3dlRÉ,&9KUF 7RŽ{KV°Éê69==]M(¥MÞmD¡MÊ·•«ÿæíÒÁÍ)­p¼ÅdqudÅdèEk„c4Æ…LÐF÷ŽÞΫו³¬üˆA‰Ø ¯ :n“±?uÍ&s²˜ÍÙdš›¬e“ÆèîÉÙHé”Ã3ü`“MÚäíp´î BYÙ&uá“J§x^ªzÒÞò¶©d2=Ô:—P†Ò-u;é{âÀ¼Ã&³«Î_v5Þ\ŠèªMfr“JqY›LË Rƒ-?›¬°"¯– J­´n“JívQMwP{nöïÞ44};g§ìú†˜`“^6)bjjJåÒÒRýšnÍ)slr5rÉU-k¨Ûd0[àvÉüÖÙŒô§î|Îܤ¶êœeçQIB·Ék×®_»Ö…v“©I{±L¦Š©¤ØÓþ:úDuñÑ=þ—t[©¶"Ï– V›TëÙ[†ÿi3ýŠ’N<꺔Œc²ûJóÍ–kEؤ¯MÎÎÎ •œžžn¤Ý¤¿MJ7Œ³ÊÝ(+y‰sZ¤3MÝ&•tfvb¶S§YÓM±-»yófT’°Øä cátó=<è Ð&»÷ôráOÕTR³IÕ#µZo—M&ŽhM1ªæ×Ê™-Ç&œ¥onÒµ¬ØÜíé6Dd“ß`“]°@µWáô‘MVSÉš6©ÕYçÔt«YÆ25Ýúœejºó–•›ƒHØ$`“Alذ¡šJZmÒ¿¦;©ã.´É¸ußô‰»áàR„ü•}üñ±IZ›¬¬’ulRZ òPžÐÕÌ')’ê]”/TŸ”ÿ˜uβOÊY6ßb l`ˆl²N0²"Mb“€M®‡Ð’æÏ ¢ƒ6¹Ô`“ÝëÊ>ˆ^80Ïq,ÛÑ[»³&+r=‰Ý§ü#S÷©i-³pEþdPÝ)1’{¥ÞúɃB}žô™9£r^Ù3¤W{dÛAûR³I¶…Mb“Ñ6¹´Ô^Â&so«<Ò¼îý,§âáȃMݽgT;§ò?e]a8mRÄ(¢)ÿùPu¼ô-ߣ~8C°Il›$lr0lR½³ªcÏD÷Ýð~¶5àçíE}Dï½£ÊhÖ$“ySÌ “o“bÎѽ氊þ6iŽýXÓÔÏ9 rædÈ"mdsm¢yäm‡ÎNß3dϨ6>S~îMÌ£é~²Ê¹Tæ ééÙ¾âì`áåH“;2ÚÄNì& MnÙú¼ l’ úÌ&—Û›T¯ãéà„ƒ<Ñ+7 cfuGÜžŒs(o¢NyòŽ\»0û"6OÜeMÛë€MzVý[¼Óu@‚!ÔŒ9Ñqä]_‡~èüçô¿ç%gE°µ÷NmuÖä&ƒa:GÚÌ&ýÎ^î‘ã+Ž&š¹|Ûémýɘkíæ\¬Öwn2_%±I‚T›Æv“IzL»¾[Ä1X*t»z6©ÔŠÜ/³Ö«f¿š­éÖ“XE‚«¦—ÜÉÚT ²sZ}Ë8òÖ‰¶Cç?g)÷ºwj¯øºånîm•ÍMªc#µÊž!½Þ#ÛW§! ïÉù 8~2ñÄì& \MwŽJb“Ñ/6¹¼|S@nÒeir‘¨2ΨM¶Ý›ÈMúÛ¤{:h“~…HáP²MV›ôΞšGÞ:Ñe“~s–Rj­†º”Mæ$Ø|m²×{d|ÅáÉ?5ÚòJ½çÿd”__Ý$7¹nÚMºT›$ˆAµÉaî…£vS°Ûdœ´S]awXW>ó”6µÌIªŽRAJükº­U¢^{¤‡Îr@v¶´Œu¢ûÈm‡ÎNÓ«r•:ÖA¥Åa›¬r†ôÁ_q¬€Fê:Ï&3?ëÄ» 븎U%±I‚À&§ŽÞ×µ•I!(½ÔÛ‘´?Á”ÚÂRÏ@Ø&ª‹îÉéî­'díd©y·›Tv3ÿ7™’kW±ö‰Æ‘wM´:ÿ9“™MÍu?OÇ+Sëê…Sí éáY¿âÌO&–×Â=²v“j9êôKý:€>ÝØ$AtÑ&?¹)À&ò³žìQGëC HÀ& b€mò“On~‚M¸û—°GØ$`“A`“€MM6‰MDßÙäõ[‚hÌî’?c€‚MD_Øäõë·®c“€MM6‰M6 €MÄz´Is(‚÷Þ?ÕÀ`8qjƒMÄ Ûä[‚B›´Ž‘Š`“em²A¡Ä& ¢/lòÆA¾MÊ?6 €M4b“M %6Iƒa“É/›À&š²ÉF„›$ˆ°IõgM`“ Úd}¡Ä& ¢?l²½" 7 €M›$¢ŠM¶Û+í6í&°IÚMÑ1›¤O76 @Ÿn‚ jÙdã ž7‰MÄú°ÉÆÂÆÂ!¢ŠMªpal’ l°I€’`“€M6 Ø$`“…6ù¹'ŽT„CØ$`“€M6 €MÀÀØäöÅÓË‹_|üŽ,ÀÙ¤ÁO?[‹¸ýÌöwªÙä™åÅ/a“Ch“©fÙþj)Ä&Ö+ÿæyo›|üüþÏÚ¤f™IX~ñÛz SÍk…œ|fù³}{¢¢öUÎt@?ñý⪟Mîi¯É× *Z©µ‰ÜÛ^›?ÎóÙþ½ïd ‘É¢Îsè>+Yœ›Ìäcƒü,iL)«¿•LägÂ/CËLæ?kèþpæ¯ÎÇ¢ ž•ôËMZ7š4ÛA*yû{Û•O³ .“×,|¶o’Ú€ÎJ–h7¹·¥!ƒcÜÒ˜-h@¼ó(M*3]y>;ý«“| ë#+éÝnò‰£_úÕí¨“M¶‡Íç‘…èíéùvÚJR›" yüä÷–ã,& zV² 7Ù8IW¾ €u‘•ÌÍM6 ýoÖ?À&›l†„ÿØ9¹SÞsŽIEND®B`‚stxxl-1.4.1/doc/images/vector_architecture.pdf000644 001411 000144 00000006227 12350112610 021244 0ustar00tbusers000000 000000 %PDF-1.4 %Çì¢ 5 0 obj <> stream xœ•ÍnÛ0 €ïz “æ(J²Äë€aÀn­} zR'ůkZl{üQqâ$N>Ø”>ñ_ôZ üÞ«Ö|~Œ°y7#{ðÇì÷áñ›a_Z,Kˆè$yÁ²àäлÆT†%ZLé>ä|bµxŸat|Z€‹ÅÓºøl˜(¡u`)yT¦íÝa!La{æâq­5¼UŽÇ¶'Ϙlöç:øx¢\vÓ‡ Êb þŒòÄ”3yNlŒA5ܯ߈yýIƒî¢U‡õÑÂL8VëKÁGtO T•DѬ©§´^hDÏ¢e ÒU¾„Ø ôL ª«úÝviU]ù¶ª Tî G¹cGTSÕ°Õok‡ª«ûðMãÔÕ-îΰÛ~_çßmÌ›±Ý÷áµjáK­óL3"("êµéæœÆ–r®Ù;WBÝšÙj¹ziæõóµ6§ëÕ}Ü7²pa€Ú¨[6ß› ,f¿–›h^¸èt±ìd;/B ”:™²È>˶ûƒ½ò(ûNŽ9Í‹È:#9u²ÌŸêï]¼}Ú§$ô:^V3!z°ŒcÜ'ôžftP…B“®ÕáÀùGõ³™5?šÝÏå6ãN{FJÂF$R­xÿxݩ¼_”(Nô€ýþë~7+…zƒ'/Y4èh¡ÐƉ$Xô)Ñr¨oá˜Òœ©¥8¯¡eÕë]sjŒóR¡~àendstream endobj 6 0 obj 554 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 9 0 obj <> endobj 10 0 obj <> endobj 8 0 obj <> endobj 11 0 obj <>stream fig2dev Version 3.2 Patchlevel 5-alpha5 vector.figrdementi@mpino1302 \(Roman Dementiev,,,\) endstream endobj 2 0 obj <>endobj xref 0 12 0000000000 65535 f 0000000867 00000 n 0000002587 00000 n 0000000808 00000 n 0000000658 00000 n 0000000015 00000 n 0000000639 00000 n 0000000932 00000 n 0000001032 00000 n 0000000973 00000 n 0000001002 00000 n 0000001098 00000 n trailer << /Size 12 /Root 1 0 R /Info 2 0 R /ID [<18D6167055741AF70A4173806CA439BD><18D6167055741AF70A4173806CA439BD>] >> startxref 2830 %%EOF stxxl-1.4.1/doc/images/pdm_small.png000644 001411 000144 00000066364 12350112610 017173 0ustar00tbusers000000 000000 ‰PNG  IHDRX1 uÜÓgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ< pHYsÀÀ.¶·bKGDÿÿÿ ½§“kÑIDATxÚìxEÇ/$„^ÔÛr% ½÷ÞAªTé RD¤ ˆô^”ŠÒ« tÞ•"ÍR¤I‘`¾÷Ý›Ëb€„Òþ¿çyŸËÝí]ngvg3;û®É$¬ªÆ1šbÅ|ŠïQÆRŠCù±Õ :‚UŽâs w WÄ"‹•ÑtØjÉò¤A‘S âÙ`ÁêNQŸDG°X ÚÊHy¦l8ŠQ 1‘ˆ\rË%òLÙ¸PÔ¡èAá†@LDÂbEYŒÒ¤H‰­±‹TrVùd.Xä¤ÿ½@lÅ‚£Eÿ¸¹±+~&›¢™,ôýM3ÙtÝd±Ù"ýoÕd§÷8¬¼Œ¿ÿ¾'³Ýn²Ðgèý@Zþ?ÿÇb1Ùéu›¢Ä¶ ò"s;âR²2QL ŒíèE1â ú»`ù’`å'Á %ÁÊnµX]-òû]Ü\y¹Œ$WÞ>$X^6³™S$ðçH˜tc9 –¦§£es…⣂Y|þó¾Ål1éþñJ鬙')rðh’MU=t]uå« —*i,èïË¿©+Åy›¢Žø}AªÙE¾wɪªc¸ž<¹}EaœÀ묈“ÝÓÄì³*_%x€óRÙ§ßß§8-þÖ$XéyY÷Ý噫é}#̓¦™Ìv{„oÀŸ}K¦b¨ëøNÕøN»ªóª)‡yä-†ëXT¦…Àäv¼ÁÊB1>¦™Ýݦör‹s\ZT=“EÑ2êŠÊÂÕ‘Ó/ð²)B̦ˆW>ï;ó©&‹ªZi¹Ÿ)VZ5ÝF‚•ž+#EzíέeÕµ´1XGWŠ.M1z€7)Y|Eà{1S-hz9Ùý¸‘Ô1·j ů2hGC°rùóÿøŠ_‘`1ºãT`=ŠCò{y®Öòû—ÛT=Äêç“ucÙ›ÄÜQÛx“‚e“)²Æä³|ŠÏ®*)­Š–Ÿ>û>ÅPŠïZU½ ¦éF¶t÷`ã” ¨C‚õ¼[U5év›#K<}öÅÇVUmdÑä©Èbc²n (ºÇö¶@1•¬T2}B͘žF³É9RAªnÒ­º[€®¤°š9s;=דܥ`A‚õrq³Xá5z4ÛStññ2¾‹….†ë•NÞ(?NàM –IÞ6§L'»;Qå¤u…Õj“ßeó÷çÛé˜ìôûB½¼^eŠQ –W¢¢ðÆ%‹saqòϤ #|3g™Ö¡N ¾„Ä$ç<µKìB"×ÅŸb…‚Ñ+ħ”ØŒ4ªæ‘Ö…¯Dì‰Ñ+Ä·˜¤”™Ýk„'Mœ‘Vžî̇Ñ+Ä·`q”£D‘^&éLŒQRfnOZ@B¬ìò–4œk€ÌC9FˆÏ^°Ì€¼ÿY4¾#¦áüŸË1¹ I²RÈ#óñNQDg9·‰ì#™`´c„Gç2}¤¨uŠðzGyÛšÎòïÑRŒ:Åa|$³¿—ÄéA$Á2É|XE¢œß¤hùŒÛÕ°ÀhZj‹ª™e‚ÒB¡z6«o.4==ç›AWNo·¸Ø­}®ºUÑèµ–ô¹ZV]Ë@¤Ðâxþ•;E/ŠÂ¨M$4Á*OòT†¢5E+-­ª:Ÿ„©½_Úâ¨1$TÉ9O,ŠfuÜšFå$ŸU鱨¼™toŠ¹Æ ¢5­/ËDaãߟÞqk­j I°>¥¨áÞ‚M(šQ4¦˜gSôÚÁR ‘@ðÎË·ËQ39rhé#éqDÁjnSµº6ͱn×x„ÉÉâ ë¥!XH.‚Å#Xe(ªX5-W‰\š) f*—Ûq•¡MQùÞ~eI°8oV›™oE£š,Šê&el =Ïã4½ UÑÊéºÅÅ¢ë™t>ͨʤ¦Å!XHN‚UÒ”ÚdR-›¼Ç n6;ßç{ûU ÈdQ´,³Å?«›U“¦˜Sxe¡÷|9Ÿ–¢k®ª¦¥²Z,&,“«««I×ÔÔVLjŸ> ˆüøfÏá7‡¶;îkÈ÷$äç|sic¾É4=·Èç,$ ÁÒõ™ 䪟¯+‰QN›ªóÜ«ò,‘'™ëzz’  9y½†fHÐÿßÏÄRäKßc—7c¶G!Xœð4/ßQ °¸HÁâ¹]…è»ÓHÁòdA£Çì,$Áâ+ÿø Ádúƒ"2=Ï 5’jÏ»*$³¿7’©†[Uõ='ŠêºªóiÃúýä¨ÿ=–>[SŽ`ÙÿûT¾ñôFŠ9v¿@7ù»x>ØazÏ&ŸóDûS XH,‚UÖ¢êm-ªÖ•þþâ{ú»¡UÕyä(‹Ìø®KñyÛ&Uû–Ä©KKRuZ>§ÌwÅbµØâ¸Ú0ÈjLvz˪(ôút–¹ ì!®òw±è-"Á2ËçWžÍÖÁ@B¬êò´Þ·2VQÌæìì6E塜#ËN±PŽ6ñã Š™,86Eãd¤ÕlšV…S;PÌ’ïGñÅ;Ï;EËßÁ@‚,¾Ÿ_)‹bœÆK'#m„¿³ÊùTI°RGxß¹L9‚UƒË5ÂëÎeX‚R9G°â8“{:¢`ñÖ Š¦2ÁhäxWNvoûœ÷›ÉLí#^ð~39×룗,Óàß4oÃÚ@B,>ØŠ¢ùsâãž‚ŽÇ—-ÓüÁï·xÉ21 þ¿ x’=j M²^S¨&[ά&›ª¿æÿ£¡"<ðÏæçbUôJE÷çìì X4#«;gxßdU´,þV” @lK×]I°º`ݧØiUt›ÕlAÁ¼ ŠŸIWU½ÚE!(îYY¶ÌW”À+àå•ÍÅ¢ªÎÑ+!c·EÑü³fÇ1‚ÄÊdQT%Âè•3X¶º™R»£b‚®ª)äÍšïG,Ž]$`–; ºXUÕDj>Å9гw(®È¿O’`5$ÁÂ…ÑŦié­šVТi¥(ÊT­¡Ç>%¯é: f‚e²r,Šº&#Ëú,ú»†E¾F‚eÂ)B€`Qã9,YV³–B VM«/¾mN¥: ºØ4--‰T E0EN¬¥$Xéï ŠݬxeM炈.VUõ'©ZH2u‰â"ý}—â:ý}ÿ¤h®xäÄ,€è’×#£ IV{)V‘Ó4l´(š¢dɉ‚ˆ.Á>Þ&›¢d#™ÚI®þµªú{&S~@L±ªŠ‹MUÛÊXNÁÚn1kÞºŸŽxÁrÎÅrŽb‘h©mls¯^Í“s`½Kqç^Y÷'D¹¼*®ª7ßøÙ‡¤j5EÍ;;F¯<áQ¦¸ M±¹X½¤EQ< Hcÿ‰ŽRÅŠ›jV­fjÙ¬¹©Uówb-š63•,\ÌT½RåWþŽ6-ZšªW®b ²ÙM^Y=QAH|Û4’™Ž$„ ßóA ÅÚÕªjÅu³’5€DG ÕV­û‡Þ[³zµX»fM‚ˆ‘ÃG<-˜7ßgéÒ¤uC  Ñ`±V›1mÚ=‘€øiÛ¶°Ò%Jö§ŸÁ@â¬iS¦Ü{òäI‚¬Í›6‡•*^‚ ‚Á€`A°Áz9?·oßa½tÙˆiÙ{wOŸB°ÁŠŠ'Nˆ6-[‰Ý;w=w™?þøC|=û+ñqïÞâ¦ÍÄÈaÃÅ­[· X€`EÅž={D°=@¬Z¹ò¹Ë|ûÍ7¢r… ¢Õ;-DÉ¢ÅD‹fÍŵ«W!X€`=O°BÅê•«ž»ÌÍ›7Å¥K—ÄÕ+WDûwß-›¿Á+6‚åäÑ£G¢c‡¢ ¬¸¬‡‚…,@° X, ¬gëûU«£5ɽS‡÷ Áº~ý: ¬ç V` ˜?ož!M×®] [7oÛY¬ø½K/ŠvmÚˆ&o7§O×iÕ‚`‚½$X¹‚CD­ê5Ä»mÚŠ¶­[ѦU+1lðC®NüzBtîØQ´nÑR+TXÊŸßH6Ú®u±ß~ X¹p႘:y²˜0nœ?vlxŒ3F,ünq¯Âóý%&ñ½6VL?žbBør§N‚`‚> ø¼xÙ2˜ƒÖ‚ ‚Á€`A°ŒkúÔ©÷DbÛ–­a¥!XH¬ÙªõêÑãî†õëŸlÚ¸1Þcó¦MOÆV(_þ®.) XH|Xm…òçP¾LÙ!Ê–‹÷¨X¶üb…  ´Ú*zeõL@¢#[¦Ì.ôÀ"ãšÂ#CF—& ßF ñaUµ¸ M7Ùl6“U·ÄÙw$kì!!&{` «Å×…XÌæVUmdÕ4+  6è¾Þ&’« ŠÝ$Wý¬ºæ¦«f À+ –Ÿ¯«UÕzS„Q Ñ ¶«V À«`ç éŠHbu˜BP<¤ècQUר–·(ÏŸÔnÁÄv“ɦh)X¨¤X G¬ªj×%*Ár§÷«St¥øPFŠZ$XÙ-fH’1ªf²š5+ÉÑÁråÅ꫘ýSD!dé½¥wä¨×~Š¿Q¬±©ZÎ@ó·|ËEŠâQ$Áâ8dUÕ\ÁKT‚µŒb“UÑüé1½]Ñ}¬ªþ>ýý½öE ÙìŠÒà´ÚLys…šò…æF¼ òäÌÅ÷ ÃHXTM!)úÚF2e3„J»MqÖfLt×öÛTµ) VŠçÖV³–ɦÉùWºæ)G´Vhj:”.¯&XÔ­ )Kå)Ê!¢Œò\F$XþØb «¦¹“LyÊð#9ÚbU´nòy6«ª¦Ñ-£‘`e¦ï1Óó(†ÚuÍŹ¼F¦ë:§0ÙÍ8uÀ ÑÍJÝzoÕ¹4iü„ÛQÆø±co׬Výw›¦WÅH ‚e"I2B×´T$GëI°Þu¾ÆRôÁâ9X—y´Šb1ÅfŠ‹󭪮Ûd¢R«¼âÐf –Ë‹+üuUåSŽèõ«•å÷kVU ±¨Æ( €è øúe¬X®ÜüãÇŽÁ¨"±g÷nQ®Té)2âJ@‚EÓŒ¤¡C(žF‘¦áÉQá—Œ`evfq·kZzœFqΪêŤ`ó¸,ޤ¤,¢ƒÙÇ×”;GÎVÓ§N»õô)Î: “&L¸lhH‚… `Quãö6¹I|ŽG’«Ç#ü´”ϬFbQ¾1´fœü˜bÅß_Z5K¦ˆŸ±8²½C°ˆ.iS¥òiÝ¢åá+W®À¬$ΟõëÔÝì›Ý+¶@BG×4¾ÑóÐHÉFOHå´ûkÿYÞfÖÒÚTm¼rp½1’¥jkßÕÎvEɪù=›Ž‚@Ìq-]¼Äç߯^ýjå`ñ¢EŠ*ÜgÞ¤)Ø:‰A°øTb–rÆÂeSõ”Q-oóós±©Jf›¦y;îiYí¾>nV™Þ!O±Â,b‹¿·OÑŸö¿tç_Lv¿}û¶èÞ­Û_é3cË$ÉÒuž‹Å£RO(~¥ÈçuRP›¿¿1¡s]9ÃÎáç÷ŸeyþçÂ"Áú€¾s+-›ÒªrÒQ…ÀËðÉ–=S• çùåH²¬];vŠÊ*NK—*uZl€Ä‚#¨–âˆMÕ†šu÷â©rÆú{I°r`µ¤ï]hŒ)Úû$WÕ¬ÏDÀn³›r‡´ž9}úí'OžÄ™¬\»vMlÿi»øzöl1kÆ ±vÍqúÔ)ñðáÃðeÎ='6oÜ(6¬[/6¬_o\½÷Û™3âþ½{Ï~×Õ«bËæÍÆwþgÔéÖm±yÓ&qñâÅXýÞGô»&ŽŸp3Àj«‡­ØÐ->Åבd+4Ð?nF˜lšV¾s:Å\G–w#OVO/œ*àedJ—Þäå™Íï¦Íö_%‘‰-|Eâ«M›‰b… ó„qѲysQ­rQ±\91mêTqïî]cY¯Ü9s‰ eÊŠšU«qjQ²h11ôóÁâï¿ÿÿN(^Ž%+2G~ùE òÜ©Xýî¿ÎõÞª³Þìãë­ð2r‘3(8Á„ŸŸ¿k®!qö}Ù<2¹hþþnªŸ®*ýmöõq ÉùÒÏX¬Fš’;.eJ”òýêÕa±¬ŸæÓlâ­šµÄº~gÿ<+.“,:yR|3gŽø¤o_qU^µ8d«D‘¢båòÆèËÒØÑcDî9ŔɓÅãÇŽ¹÷›6lÔ¢™_~þYÛÄ bõ»—.Yò pþcSD’ÿÌéÒçJçž*'âÙHŸ:M.o»MÓݱ¥€dWVÏŸöëwù®]z—ïEÊEÝÑ êUªŠ¶­Û;Xôæ`•/ºá4O‚gÁZºdI§%Ÿ/_/ãÎ;¢KÇNgü¼½Cü½}° X,^î.)2¼U³Ö–ž˜Âs·8‡U¼ùÄòeËDä¹\,mœHÔ)oÿ¬ƒ/ÍQUºx Ñïã¾ÏLžg)5b¤(^¤¨‘â!Æ™Ûwî|Z­r•Ù¼Ú¨y ‚Àk%@·4>uê½°°˜Ÿ)¼pþ¼‘X´xá"ÆdwžÀ¾wï^#“{ß>}D§ï™Ù Áš2E”.áÁz<cGE S¾üRìݳGìÛ»O|5k¶(Q´˜Ø¿¿ˆiz ÎÜ>zä(žÜ^5€`A°xÝ#X&›¦[ÞiÚlÇåÙÔc¿ÿ.$Ê—)kŸ6ät -›5×o ¹qäÁâÄ£ujÕŽÖèÓ•+Wĸ1cŒlð5ªV5«U32ÀùüsqñÂ…ÿÆßûM4mÔxƒO¶ì¾¨u ‚Àk'µ[J—" þaíÚG¯|o¿GÄ¥‹Åþ}û§?ÿüó?ó²øTá?ׯ‹°GÑû7<¢ÆÒǹºL—*U Ô0 © Öóæe øôSQ­rcT ‚@ü –Én±éÕ£ÇÙ¤0ÙýÖ­[¢kç.ÇlšžÏ=…+*,‹'¼W([άû÷ïC°H ¤ªU½Æ’C‡=I삵wÏž'eK–šéîæ†Éí€d!X|'ŒÖ-ZŠzoÕ1î¤Iî$\‚lö†_NšôàE÷ L §G v#ØPÝ+«'jäëïK—Œ»gp²çÝ»v!M ¯,YÕ¶­Zïy•Œé …ßÿýió&M·¹™\² FI]°.^¼(ºtêlÈÕŽíÛÅ›ì C°ˆ&Ù<²¸å ÍÝÞ>tèƒáC‡Åy 2ä1EØëønþÍÛw¸—/wžN´:˜|Hô‚µzÕªç.såòeÑ탮$WU ¹zÓ@°ˆ&ž™=L6{¶Ìé3Hí–2Î#cštåý½}¶ùf÷þ6›{¡×ñ?<2d, ›$¾$rÁÚ#‚I°FiœöÛ±}‡!QG~9"nÞ¼)>îÕ[äΑS ­=^¡8hUÔÀ×ñ?lšn °XQ‘€D-X‡62³—)QR”+UZ”•ÁÏ»tì$N:%Z6G/\Äx?|™’¥Œ4 ‡‚`Ô °°ü¨YI€R<¥£èc³h8€`EÁÝ»wůÇÇŽÇŽEˆ£GÅüa¤aøíÌoÆógß?&~ýõWq÷ÎI@›îbSµÚ$UW)„ŒãVM³Ù4 €`%B XÄ3fsÛÿG¯œ‚õȪª},”‚ÁÄ‹¢™lŠVŸ„êZ¹rÆa’¬v]CA X,@ +«UѦ’L]²P8F®´{ô÷Ez¼@щ s±, ºØý•Ô|Å U(EAª#ËuUËAÏs[5Íl·â4!‚ÁDE³˜,ªîL£Šb/Å,‹3­…]1£ ,àU ¡J#k6JÁ‚` X€ä(X•!X/¬ö, XCÁªTyÞÑ#GDXXX‚‰ÇSÄÿï¸qã†hßî],ˆ%Š›>ø³Aw§M™zgÚ”) ¦Þ™üÅ—¦Nžr/¾ËÄñãïV«\e«êç¯cK‚Ñ&gPpÞ\Á!ué1Þ#E°= €Åz5Ðf_D¯Õ‹×ßÂÿ¿µéé°¥Á€hCaÊ›+4A„lG?¦xL±Y3›}ãû7qùðá$:r‡›lº@íçÏòNw­šÖήk)^ô9]לirþ6U5§@° Y”‚ÚÎ~ònÎÛýdÑ4ï—– -çM‘“"—Œ Õ®ª©uMCáÁ€äØvª&›fŒ^Žt?×»ô^›¢¼H°Üh¹Ï(þ¡8OqN>þLŸgÕ4Ýú‚Ï XT‹%©/EX$ÁâØjQUÝfªð"ÁIq–¢!EIŠª#(®ÑwOµèº;J$7ÁÒ©Ý\!GŸ.ÊÓ„7¤4 xë%‚5Šâ8…9|þ•¦¥§Ç(X5Õ¥  ’MË`Õ´‚$EÅé±)µ¡g(&ÐóbüšMÕý£!X¿RXòç&ÁÒ4W‹ª©ô|;Åzž¥  ’›`±ñ¨·£òJ®6ù –)§/S ¦øP>n’sº(‹‹f¶˜t³n²Ò£UÑòó7Yp•!,Hí(_ø E·è,A°îð|-е(NQ춪j%?³‹ŸÙN2¥¥´šuäª$E¾%] 9Ðdõ(ˆ‚‚¬(‹…*¿MÓ<­š–ݦh|Uâ·û­Šª+zZúû9²Ås³~¢øŽÓ;•C¹ÁVdÁrNr×lòt£Å1Ù½†œ,ßÊ¢Pû¬hõ¬ªZÆ¢ª>VUç« ws½T=5J€„×DŒpÁŠœQ@´ÚÑ`)XF§$XŠU“W**?6£¸%ˬYœ[<ï*Ÿ™ÿÖP¬¡Ïx Hx gN)##Å>Н)Üäkn,xa;šŠÂß$U« ¯$ŸóÕ€™£!XgI”êS3raiZcÇ,íE^Ö×××øŒÍ,Í“^_Nñ¹UAž,bÃàAчb¾Œk¿QÌ¡˜Ç=' ì¼ðüvÔB1W ÑÏŽ îÚŸòÞOòTß‹‹3¹_‘#_)ÉTë)ªùú„ßÏÐ8u¨i©éõòý ¾¢ðW9”ý0РļÃ×âQ.”<·u§E*ŒIêŽyYQÖ×îB‚¤Û4­$E)ŠÒ´l1)›EQ3Ú2x™üyF­š…O¦±ª*wŠ7Òcᜪ7*€Ú0˜ä0öî(†¹ò´! ^ÜŽæ–§#¶¡Üqíñ¢6T×ôÈs¸"„j²êºü´œnq·(ZWÇÈ•ZÒ¦8—à ¹qèBq?BÃÀIïª@® Zí(Ÿ é~„ûe6¾_w§è`¤sP¨½V´PŠ\VE·YU‹j€„+Xªœ/ÀÂS9ÿ*J¢ÝކÊ|VBvXyúEœÈ‰”§¼ÂûˆLFºVæÃÏsiQüw‡L(Á½¯N²Q½JH’mNR wyõàŠ=ö8ûn³î&¯T z6Tͪ*®({3@Ì>¾®ô*¡„O¶ìºYÙA1×#CÆlñý{R»¦t·(j l)Ä ¹Cr˜²dÈèžÚ$îþÞ>E© ýYó7÷z ߟ’Â-R¤D¹;"[f”¹CrbG&SŽÀ zÅ ^[±\¹õ %J/q¡L‰’§+–+¿.>G…²åÖÌ›oAÍž[ qƒMÓ3æÍ:£\©ÒëR»“¤¢l¹¥Š—¸T¶TéŸã»MNQºDÉõ´m ´ÚÒ`O¦’E‹õ˜?ož¸võZ‚‰þùGüsý:ý}5^Ç¥‹—Äð¡C¯’„VÁ–@ÜàæêêÙ¶UëÓ§NžLPíN’ŠkŽvôzhG“SìÛ»W4kÜd‹››[ìéÀTºx‰V¯ZõH€ÿðèÑ#1qüøK9ƒ‚+aK ÎÈúþ{íO°”8sú´hÕ¢ÅÚÆ!X‚õ2ÁšÁàõÖÕ«hd@’â4 Ö;,Á‚`Á‚ X, X@° ‚€`€`, X‚Á‚ @° X@° X‚ X¯…Ç‹ .ˆóçÏ‹û÷ïG)7_ºd¼ïî=, n;¹ä¶4,,ì?ïß»wO\ ÷/Q[úðáC $ÁºJkë-E£ ÅŠåËÿóþýxCM5;¶ï€`Á‘8tð hÚ¸±hÞ¤©ñwdÖ|ÿ½¨_§®èü~GqúÔ) $Áâ^Už\¹DH@ x¯];q÷îÝð÷¸'6fÔhAb#r‰•+VB°€`HlÞ´)¼:x°xòäIø{·oßï·o/‚í¢lÉRâЃ(0H‚uá‚È—;·hצ¨QµšØ½kWø{—ÿ¾,êÖª-ÞmÛV-XH¬Z Á‚"³eÓfQ0_~Ѳysc´ÿ¯¿þ ïÈ/¿ˆŠeˉÍš‰ª•*G9 X ‰ VÞÐP1~ì8cx{ܘ±Ôûzl¼÷Úµ¢fµêbúÔi¢D‘¢, Xày‚•7ŸøjÖ,Q«z ±|é²ð÷¸Mmբ߰^T©X ‚ÁÉM°f͘)¾ž=[Ô¯SGüý÷߯ä÷nt½zô0†¿‹* Á‚ž#Xòæ?¬]+>í×O|ôa7cºÅ?ÿü#7l(¾œô…!_•ËW€`A°@r¬¯g%Ž;f aoܰAœ:yR¼U³–X¹b…qÚ§€`Vž¼bëæ-bÓÆÆ)ÁßΜë×­3¦^;zLÌ&ÁªÁ‚`ä'X³gÎ2.%îÙ½»èÞí#ã´`Ýڵŵk×ÄÎ; X@°ÀKkËæÍâê•«Æ<¬Iã'ˆþŸ|*:¶ï`´a³f΄`A°@r=EÈ,[ºT”-UÚÉ1l¸q5  x¹`ñèÃW_S›nŒd­XæHÁ‚`d.XW._o×o`\N|ðÀã5,}Á:xà ¨T®¼1ÿêÜÙ³,H®‚Åy°œ‚õôéSñóáÃbïž=á™Ûwlß.а`!,ðøB ü¹óˆM6†·Y»vî2R483»Ïš1Ã. $Áºxñ¢(œ¿€qáóصs§(U¼„ø~õj,‰­[¶ˆb… s°ž·±5ªT‡FA°@r¬;wß-¿?þÜeøþYK-þñ ˆ|&àüy±hÁBã~„σÛX¾*û*Ê‚’‡`% X@°€` ,Á‚`Á‚ X, €`A°€`Á,, X‚Á@° ‚ X, X@°+¾kâø ,X•±¥gx‚uí¤8ÂբÅF0(V¨ðGãÇ ã{&”Ø¿oßÓ}{÷Åûïà›M÷îÙóïAÁ ŽH—:MÖ¦X¿níg{EBj{’JìÛ»W¶£(ß7YæË–.õëÔݘ6u0™r—)Q¤è¤ eËN©P&AÄT’¾S%‹ÛFOŽÏßRž~K‘‡ÙìØRˆìº%]þÜyú–+UzJjw’T”-Y껢 ],U¬øÆønG“M”-7¥LÉRSò…æîhUµTØÓ©@ž¼¦÷ßko=b¤iÔˆñ=º}dR|͹,ŠzœâËÔ©S§Š¿ß3Ò4|è0õ´M!ð+â ¯¬ž¦ª+™ h=2þÛ¤+V,3éf¥µ¡—)†R‘»¡\^ð¶Ü¯ÏǦŠåÊ™²gÉŠ$,¬ŠæNæÿÅŠs%­ªŠ‚€è´¡~6µ›Ù(VPŠSmª†Â ¹’¥„Æ‚(n“\M²*jj”¼ÿLv¬z7d;F1‚Ë¥@2%Ь¸PC0@Ž^ t°ú[Q@ð,f5µ›«"´¡§-ª–[×uÉ «¦Q¯K·QCp,RÃÀ²5žËÝêRÏkGýU½jDñOíè(¬”(%’Ÿ`¹QÐ/Òè•3ÎÐû%¬&‚€çµ£ºîMíåRÙŽ>•íçSùüW‹ªA)ìKÍN ÀŠ›)nQüEñ#ÅVUolÍœâ™Ï¨ÚK#ºË@¢oGUÍbÓ´~ƒ¨S:“ž_¥Çø9Åú»hLÛPgû“öEIiUU/jü(Bhgý™býík¼¦èY¬¾¶È‚U‹¢7Åǽ(:QÔ¦Ð(RF¬r™>rùäP:/› –1ÝÂfL¹Ð PçvÑx.#Òòé(šEh{R´§¨Há±m¤G>ËP3B›Ëñls=!Y$\Á2q:Ç\,'iî£øÒÙ(XUÝD‚¹q˜OqbÅŠí‡)NòU3J„Æ'ÏߦØ)—ýI6>,roQ¸¢IH¶ ñiAŠÎ/XÆ‹bÅŠõ›dyT¶])<¤<¥¡øVÎïÚ(ÛÑÆÇó‚,~ÃÀ;ô~¬—,Çs vQðäøÌª©NqE6^rÙÁƉŽF'#…E5¾ºF6*ÙPò€d&XeøµœÈ9ˆ"=$ Ázæý™2g˜œ£õÅß²Í(__,çeµæé9²‚•Ÿþn'¯8ìHQŒË m,‰\°äж¿œ;Às RE8Eaø;¥¼J†¯D¬‚Áz¦-Eq_¦¸I+Oîs¦o‘Av\y.k€]Q]dÚ‡Y£e'÷ EÑê¤@µø+Š šoËS‚=(\"Lr‰´\)9 ; \+Š64…©º%ó_¥Œ0+e¤e[R<¤¨`ÓT§t‘™SZu-‹œ¿0Sº ¨¡`¹ÈI—©¤4’¹\òDºŠÐ)X.²‘à¤zw)Z@°ÉT°æFh]å” NÄ|^^è-ÛÖEry÷ËóŒqÿR䳩ªCºÌj ›ª»[T•§kÌã;udH“Ö5@â¬?å= »Q ¥X"_Û/3½»ÊeË9XÃdöáîrXûœ¼‚’$SÁâi9â?Z¶‰çåcþ‰Fɤ¤É齤<]¦˜Ìó´Â¿[ÑJÛTí+ùÝKlš®“`¡bH$‚õ‰¼’e½l8ÝÂ4ÙPFºVËÙ‡×;îohLØì''r¢àÉM°øôÝÄíè²ã:Q&õ‰pG°úÊ+×ËàùUßȉì^ÏÞžLÏA‚Õ™^›d,§h B½r£bH$‚ÅËùÊFÀ[fNýœ›=gË9ÃKæÂr\’©`¥í¦o„v1‹¼ï`ä6ÔEÞ1Ã7B;šÝyväv”oof¤k°ØRÒß|ë²µµÃ¨‡`Eû®î¸<‚ývñ¿Â½ey’UÉÂE#¼§óýdy$k‹Eqܺ À À« Öëù¿Æ¤ö^6U{‡«Z§ ù¦ÐƒÌþ®¨ XÁŠ!ºf$$í-ç¸.–) :ø[3šÍ¨ XÁŠ)U3úë&ÝlIc£¶Üîçë`±šlŠŽJÁF©!â? e‘p´£ :ÂëMÿoÕ¢’`é&ßìÙßÛÚPl (2C°Ð8´¥ˆXEAŠãPhCB£à*3}× x‹¢6"^¢©Ìº¾VÞ¢e’p¢EY¾4^ Wœ6¥:ö—x‹Z2i(' .Ÿ£\F°[T’é0’UÃQ&Yã›[Þ¤¸ˆvðmÉûVÅÕ÷ÝF¹&ÈzæŒø…¡à9í(çEúûÊ+ß¶ëAnÊýõÊ5Ág¾ïÏy“›`­’™j+S”DD;>—C”E’޲Ñ.•ÏiG9åïrä¤ö™hGYyåÝI9Ò2IšQžb¹¼n–ä(Xó#gG¼4šÈ'+Ê"IGˆ†`ñ)þ>Ø_bœ)}¤œê‰òH²á&Gx·&WÁZ`EJÿ˜–]s)X($]Ï X ‚uZÞ·Î%írK)o´|€ç°¡D’l=§–pA° @° ‚ X,€` @° X,Á, ‚Á, @° X‚Á‚`€`A° ‚Á,Á‚` ‚Á‚`Á‚` ‚ X‚Á,VÂáìÙ³¦ÐÐP“®ë&EÓž,E§çf«©„[  $¸°p;àh ¼¤`}lÕ¯ÙTÝàgEAEÞŸ •—î(7Es¥2%3¹gç׸L5 È÷Y—²žSFÈäžÉYÏv?{Ò[ñUu¤¯w4 é(VÊ[å¤q®¸Ž ¢ÒAÒ¨çú²ž Èç&  @²,wj+j3-Ei»øÓÑ4+¿fSôl¬(ö-³!X^²Ü©ÌfP¡¿ ókT¦f¬”(©D/X.T—Þ²žƒ©ŽçR쥿óò>Bïù“`¹&EÁJK+ZƒVòŠwéïC»éïvÍiÅ‹qá`ù¯`Q¯´ˆOhÆ=Çw×´±ÓëݬŠÕ%•$+ VªÓîTÇßõ¬jéyzü€ §…“ýHŒ5³EÕfÒö°O¶¡¼üeDTmÅ;vÅ‚v4vÅæB‚Õ‰Êg,»KwdrÙMÕ5=J*qCmd ªËÞ²Ny„ò²ì¨”õ>ÎîoK•ôVÜlNE+7K®,Ÿú“Á_!Áj«Z!XϬÜòTÀS"Âßkh£ò@I%ƒ§ª…P§xE=oÒUÍ¥”¼ òµñé­Ïå6""Åo$_¹sš‚QP‘È©™lŠ^HÊhär{dåNŒŸ9J*±·¡VnG˱SDQϨ³ÚÞn1'½1›Œ,U»Ŋﶨªš+‹†-$ªF7&ë •B±ÜnRÔ3 ”QÒè}©îTŸŸÈ?b=SO[m§+Þè€$wÁòÌM.5€¶‰£‘¶n>§Žj*ky´£‘ Q¹ÜÒÒv•ÓãHe·Çªèº—W6T,ªgžã=3ŠNÈ«¢ùØÕ$¸,7Ê‚å!'¶G\é{-ºææˆ†!ÊÆ1G­•Ó‰He·‚ç\X}BPHI]UxÉEq8R=o#ùò·ªfR2'€¶êp¹S›ÀW>Œ°³(Z>+æ±>›1¿Q/Aeõ{„r»oUõíªÅ-§jG!%‘z¦ieªÛ ÏvRµv~ºk’]ñ5€eG±þްâ?YTM³ ax!ö€>508Bïë:5 sz+ÕHJ2­k.T·ý#<ùzk»ŠzÿïpÙ4-‡œW>ze3[qŠëeí¨ÍÎW–‹0º±“¤K·ã’$…¦(©å”$çT‹ 6³æàŸ„ë™7bÚ˜3É«ÞÉU×:Uªb‹x™•[,Ô°ê¡Tf§dÙ­²š5ˆiR;xÊ«œŽ;«1z¥¢p@8=<œ"Îru’"§U$¼¼µr;ZX^}ɘnÃ>ú“ÄÐY ÊÉÁœ»už`â)—Máóîò;ù»Sðÿâ‰ÿwbi,"V–QfT¡,P½º÷à·]"•›{„rKC‘ÙÝÅÕ;K†Œº·g¶ Í_y‡Ên¥êç_Þ;«gPæô4ú°-—I.Ÿ:B¹9ËŽÏ;»Ô{«Žq@çº AcñÚê™wb–®gÞYÇŒU=GÜ?ÒRx¤ruóÉ’1“ÅÇ3{°æonf1«k_ÿÊ^Y=3¥K¯¦pÔsFù™çÖsõ*Uí+í¨ç„Ý)å6Œ·‘â…ÃóɺʺŒj;á¤ÌÙ2¦I«dÏ’5€ÚÑ‚ºYªS›à›-{ެ™2[Ó¦t÷£e8kuºµ£7oß4Ú"îŒñvjO°ˆûÿfþí|€Ð±*Õ3o«Ñð Þæ³¦uOåç™ÙÃê›Ý+Õóû´ôó÷öÉÃõœ!MZstë¹H‚ÆöÅõQà⬞#,8ØðH„ÒОÄÈN?8WÏr´±·¨\¾Âç ëÖ›Óäí·—6mÔø‡f›l¥Ç=ôüP“·iÙ¼ù‘÷Ú¶;òA§ÎG{õèùkï=ÿþ¸Wïs½{ö:Þµsçcü^ËæïiÚ¨ÑZþ0¶YãÆÛ(~¤çËÞ®Wÿ›ª+ É“+Wk‹ $eyýé7ÐÇ+Cê4i¹ÊlT†ÓRãÛ¶ƒm üH _JúTf*U|N:£ ¨~©â%zÖ­]{jㆠÒ:O嶉wÒ:hÜðí_š7iz¤m«ÖG:uxÿh÷nÝŽõíóñoŸöûä<=žü¨ë‡ÇÞoßþh›–-ÐçŽÐò?Sùí§2ÛAÏ74iÔhU£ ¿«]£æ¤b… @µÂTw9hô§ºÌäfrqåßDzs#Ѭg…“R=Hi4Q'Â=Cê´YHŠ4ŸlÙse͘©8•kÃ2%Jö©Wû­iTG‹œõÜLÖsg=·vÔsnÿ¤oßÓŸôíw‘ö‘Sݺv=öþ{í¶nñL=ï£zÞnÔóÛV6jÐ`~íê5&-X¨³æg®Iõ\•ªg?ªg–²ÎíÑÙ)o®=pJ7Ø|  :Iï‘>£ÕQ g¦ÌùiŸ¬R o¾÷jT©:êíú æR.§ícÕñO´½ì¥:ç6ñH«-Žtx÷½#vùàXŸ^½O ì?àtÿ~ŸœéùQ÷ã]:v:ún›¶GZ4kÎm(ÇAúÌnúŽ-ô¸–¾cIý:ugW(SöSúMhÛ(E¶Ü>Ù½,tàñLŸ*ujþ}¡ü;é€ßÒqT/T–ýÆTÒ¤ÉJû–…~{(¯CH``“ò¥Ë|Bë6‹Öq1¯+­ófzÜÅeÀeÁeòn›6G:SqYQz†Êî í_¿v¥²ä2mõN‹#\ÆMeÍû×O\\¨N¨nFÌ›¯=ÕYU®3z òÈчÚu#–s4Ä9ÚbVÏNág× ý#5o“¾Ù½­Ô–æÎ–Ù£4½ß¬BÙrÔ­;›êh‰¬gÞ¶w?SÏmÛá}¡W÷ÇûõÜÿLYÏíß}7r=ï¥6t›³žyß«^¹Êˆü¹ó´£ãteÏLùx%Ïðæ}vÔ°ááõcÏàp#L; ó%wú'6Zá:ô…i”+Uú Z‰U$HÇtc¸qæÍ{oÓÆ<$Ž?.~ûí7qþ¯¿Ä•+WÄ­[·Äýû÷ÅÇxôè‘ ?˰GaÆkÎ÷yYþÌÕ+WÅùóçÅïô]¿Òw>tHlÙ¼ùñüyóîO?þÁàAŸßìӳ׉VÍßYC;×dúmé7v!ÁªGU ´RS•Š•Œuâu{]h~þ¦’“ˆ£yÔ#ÊFÖ²d¿m©BúÊ_`t½:uæwíÜeÏ€Oû_5bÄýY3fÜû~õê‡{÷ìG§Ngÿ<+þ¾tIüóÏ?âîÝ»âÁƒáå±ìøÑù¿ÏËÝù÷_qýúuqéâEñ矊“'OŠ#¿ü"víÜ)V,[þ`Ú”©÷G v—¿:¶ïðS­ê5æÐ†4‚~_Oú-­šVÄ=…+„¹|;ç›gN-“I¥zv Qç¯çÌé3xQ/¶Bž\¡ï†}\¤`Á1 êÕ[ðáì§ûòè‘£îÏž9ëÞÚ5kíÛ»W=zTœ>uZœ;Kõü÷ß↬ç‡/«g¹p=ÿËõ|횸ÈõüÇâ䉓âªç;v<]¶t郩“§Ü>dè]´s$f[kV«þÕï0ÚGzÐ#€ruŒ„¹Œ5ÊhØPÏq‡ó.5ØÎ‘Ë´Š¯_(µO©}údáó**NoצÍzê(:xð¿_Núâþ¢… ïÿ´mÛ“Ÿ'~ýUüñûïâÂù âÕõí۷Ń(ÚQgDl 8îÝ»'nÞ¼)._¾,þ:wNœ9sF?vL8p@¬ÿq]Ø7_}oܘ1 üìZ>"ao¼´dÑb¨-ø„¶å÷é±:uyTÇ·u÷ðÑš×y  ¢ý‹GäþåJi3íWÕè7uàßV¢h±ñT®Kºwûè𠯎=æÁœ¯¾¾·îÇÃìßo¬ã™ÓgŒuæu¿yã¦QË-ª²s¾ÏeÌeÍe~᣸.~>ü³Ø¾í§§‹.º?yÒ†òo?êì’Ðn ºœš#Ç`j/»Q7¡v"·5s¡ƒ¹±-°tõLÛuúõìÆRªßÔämïÓRÅKL$ñYF2üËçŸ}v}ܘ±¾™3çÞ†õëÃÒ6lÔóG=_áz¾³z¾ï¬ç«W}ìÿõ|XlÛºõÉÂïÜÿbâ¤C>|»oï>§Û¶jýcåò¦QNØ•êùm³oNyÖÈ¥Qƒ†ÆÙ‚çzKHËfï˜Ò¹§Êjöö)N=îkU¯¾ú½víNþâ‹kË—.{B;¿±¡ñá h|Á…Å2ñ`~ùÙØøY"žN™<ù:õF“<¬)[ªô§Ô°•&óÌþóƒÆ:º™âöb,¶YŽÔn)Ófɘ)8ohh‹j•«Ì¡ßAøçwóæ?ذnØ¿o¿!<—Hžîܹ#âÞ°.¼²¸²Üý°v­ ¡¢rßSÔ ÜVµb¥É9ƒÐ%ž.¾Ù²ÛÉ^g :ì:›-$%®’lmÚ¸Qôû¸ï…¢ Ëî‘Õ·L©RɪaðÉîå“%+•¯0w̨Q7yÄOÑ%%ø”ôú׉^=zž-œ¿À€lY²ß~üÖM¨7nÊ’)S†ü¹ótlßîÝc+—/ʼ¤"ð™‚Ӧݧ^û÷E+Y o¾X'm,˜¿€«nV‹×¬V}Õ´)Sïqç4>G¦^çΞ«V¬|úþ{í-'o—¬™3gÌ”6yÝö0_î<|•kÙºµj¯›9}ÆžºŸgÀ^|6mÙÒ¥OH¨&¡n—%c¦ôl–º}Ðõ"ŸÖHêüþûï¢OÏ^WCCr469®Fx%ü½}Lº¢*ujÕ^ûÃÚµOãsóMÀ½‹oç|ó¸R¹ò_eÍ”9criÌ>¾|Ú' AÝz[6mؘä„Èð<†Y3f<*WºÌéS¥IuŠéÝS»-T¨ûð¡Cïò©á¤ wXù4]ÛÖ­ÛuKqÏ̯~T¯¬ž¼¦žÿÑýûö%©Ž}TðiÍQ#FÜ+V¸pŸÌé2¤LNí¨Ýb-CS<ňGÑ“2/\ŸöÙíBùó÷2QcºgÇöíOE2aϮݢBÙrÉ.3ÇB°R(¾~í&Œ÷(©o,{°=»w¿A‚U<5 Ô»VºÍ˜6ýqb9[îݽ+:¿ßñ2òA¢‡kŠÙ›7izôܹsÉ¥‹.Áö€q$X¯|6ÀÛ3[ª\Á!“.XlÊíü_çE‹fÍOpJ–ä²б2]¾ÐÜ_­^¹2ÙÔóéÓ§Ÿ6¬Wÿœ©HÁB׿3'QͳŠMïë»yó­ó/VÛ+ßÙÓ#c&— «íÃîv{ÊWª$“ FÐAä¶Ÿ—w²¬Lé3¸† è÷ñÇÆÉãÇŽ j®ûdËÁŠ&Ù³dõ­X®ü•Í›6' ¿wOŒ5JP8ÕË3[êW-7›¦§ ɱdôÈ‘ñzAÈ›dÛÖ­¢Rù WI.•d3ÿ*0( Ö_NúB$õ³= wÆX³V”-Uú‘©Qƒ†çúò©˜5s¦1#)ŽÈð ó¤Þo¾ž#>îÕ[4¨[oYµG,¶—òeÊtèÒ±ÓÃáC‡W<$¦ z1çCðÕ£ûM5¾’'g®üÉip¢r… ½ºvî6røpã*’¤ÚáŸ@m Ö…à€ÀP§hÄø¼U³Ö…Ÿ~*–-YjœjMŠ#ž|là´ _Lœ(>ìòÁÓj•« 793¾iÓ¤IMmñ×}{÷ãÇŽ3Rò$ÅÓ„Î+zW,_.øX[·ví ~Ù½ü“Ëþá‘)szêœ/þ¤o_ñå¤IÆhIÕ3nüsC,^¸P|Ò·Ÿ 6á–©OÏ^ç8íÂÜoç/~5k¶‘;‰{쉽‘øçú?tP<Ê—¦ ®Ü¯gÏÇŽ½{ôÜæfr‰•`Qµýšï¿À—ç²|Œ1Rðå¥|ÅPb·tã*̳gô C>ÿ\ 2”¶‰#bâø ZmÉidõfÕj=7mÜøpÛ–­‚ ãÆŒ;~Ún\%”ØçdñÂ|ÀäËŽ üLŒ>ÂHé0lÈ¿_Vôñ¡öåŸ"dùèÿÉ'bÑ‚…‚¯ŽbÙJì£þœghÿþýbêäÉâS:Fðö¹‚¨Ã5,6‚E¤nÿî{_óœ.NqÀß=ù‹/ž}ûŒV‰]¶XªNŸ:EÜEFÇeâøñÆDhjGλš\ü“Ñþ‘¾û‡ÝsuÙÒ¥‚ÏL<ÅHis% Ô3ï㜋pÁüïDÿ~Ÿˆ)_Nœ¡O¯^7M½zô8Ǘ²QòÍU+W íØ1cøŠ±öû5â×_5„+!ÿ6NÎÉ—ö²̘6]Œ=FðW*O<ãuä4½º÷Ø–2–‚U¡l¹7lxàè&ØÞ;m#üûy´Ÿ;Ù+—/S§L1NrlX·Þè´2‡zÚ¸áÛ±¬÷Ú¶ûšFbj¿yuìèÑbôÈQ†Ð­X¶Ì¸z‘…‹Ï$Ôý‹ë”ë–s7ñ•×-6¬_oœùGõ<|èPã˜Ï‰Â ¢Ã+rzû÷îæ½yÓ&cã3j´Ñ+c;›0nœ˜?ožÑ`ð†Ï‰ ¹PøJ3n y'ˆË„¿+Œ¾Ó™ÁúÚÕkFî Qá ™çTqπߧýú‰ÑTiß|ýµñûX¶"ŸÛçÆ#N«\¹TÀ"ÄxdcÏî=bé’%FoöóŸ‰~}>6Do6UÆêU« ã+/“ÐFÌ@ËW ÈÓ'ŽÆ’¿—Ë€órñ)`®W–„åÔpMŸ:U ô¹ñûx¤j5¦,ØÜ£äo‘™üÅÉS°¶ïxy›ämp÷®]œ#ȨçAåȧ¹q]óý÷†`s•¼S²¨òþ—õlìê™RœÌ–S“ìØ¾Ýè\p½þlñû†lÔ;ð©mnì"Cë@‚åÁzÁŠ<‚Á#æ?®ýÁØ÷G &>ù¸¯ø¬ÿÁóQx¢øÖ-[QCîˆq'–%˜Eë4®ÚQÞÖx›ãmÏÈ`M¿‹ÛAN’Éûúºä«„ ±á‘~CÌŸoÈ8oѧ@ÐçâL°x~gDøýùÇŸbûO?->æðoâ߯¢Ç>@sdžGÚ#Þ1äµí_uÃutñÂE#I3ϧâ×ågp:ã@ËuÍ|†[‘F0ùw ü4y Vä‹@¸Íbñä³Ü!7f —QÏÜ1a÷X¿nÑNqª >&ñ`Ðë­çŽz–™ü;&¶lÙbŒHóéMÞy¤uÄ0jçgÏ6öÎ*Ï¿+ò6ü\ÁŠ<2Ä·`ù‡6,¾ýÊÞ={Å÷$ Ü+ci@ÂÕÄñ¨ážfl\s¾úJ|ûÍ7†üp#Âçžù€Ã= >Æx>øðPðÎ;רy´Œ—å–?Ȩ̈÷fÏœ%¦O›&&M˜ †süOîÌœ>ÝÎRͧ9yãg {ÑHÛ묨æ/q"J>àq¥oÞܹFãÅÒàAƒÄ8Ú¸§6sÆ £Â¸ñ˜OË,¤†…Ž| üñ‡ ÙågÉg…çuæÆ%“ðU+W‰¥‹— Ò¼oçóÍø@?sú c¤…{„|ˆ.Ç…ß-0²6óÈ…S^vj‚5,Ȝՙ’|«ÎÜÏÛ.°x[eyåS‹<,>‹êùëÙ´„×ó£žyŸâ•ë™OAî•õÌÇõ¼‘zöÜhóö¾”¤Ž‡£ç}û­Ñ™pÔót®YÏà‘TÞ—ø@Àù휢—Ú„`Å`E$L&îä"÷~wlßAõ¾Ì!âv€¥‹{ó_Rñè;·ys¨ny;úŽDgñ¢EbåŠÆäYnG¸Nyz§7à¶”eš÷g> ­Yý½Ñâƒw†¹-þšÚdn³§M™*&ŒgŒ®ñ)o‰åöš·-epÞ¦ëE¯×)XQ°s‘;ó4Ú;>.ð¯wxw$xû×7ƺspY¬Y½ÚÈõÆeÄeÅeÆe·kç.£,¹L×RÙrsYs™Ï•û×ìY³Œ:á,w ¸®¸ÃÂí6ï»ü}|û+>;ò²Ä¨¬ç ‹ o{¼Ÿ>ä;Oð´.s®g>æO7^L£"E®çË–Û>ï[6“glß¡žw:êyýã{Ÿ©göŒ¯žÁõÌí& Oý1èÿ±—°·ð(´óÖU/i‹¶`½ >¸°é‘· `Y2vr*ÞeÁrð¹@xàSO|°á`)sì³eø ±hÁ¹S8 k+‰Ø/?ÿbŒĿГ7)X/;¥ÉÅó4x#0dÉy]±ÒùŽGÚ€¸äÊçƒ(7"ShÇæJgyúŠv~nlæR#Ê=N9ã9ÜÃÛ¼Ñ!e<â·3¿TlŒ‚sx'䞦²4ý´u›Ñ˜s=óNÎõ<_ÖóײžgDQÏÜÈ;¸ßrµŒëy•¬gC¾·‹ƒû#£±d ÁŠ{ÁzÜ1äÑî­³|±°pObÀqq>àÏ‘'un;ù Ïí(þÐqÈ;@øÀÀîÀò†…{Ü<$6sß”`½ ^>À£€¼n¼ŽäAÔè¬SpYû×쯌.+[.;Çñg†Q¦ÆþEeÞéYíèôð-zX¢¸nø{äÑŠ˜Ž&B°bwþy âØÑcêùÙAG=KÏpÖóäÿÖ3Kó¤ó¿‰»~ãx¬¾Ç¶†ý1Þs.gG,%ež²‚ñ‘é|Û¯¢ï|ó}g¬166ÌŒ˜éL]ms¯®Žÿ„»¿ÇDF1£uGCî?æƒX `}\ÂãV,LMɱB °FA£ŸR¢€5^ǃ¬‡ñ¬°ºÀë`P8‰^°Â˜¿‰ï´Óxî§¶º9Ö,°Zzï`ËÖÇ$9¬½–(Öð‘ÕXÚ°<áýCP ,Ex}”×Ùknül¦¥¹å½ƒªÊØ-›6s€ÅÖ_§+ÕÕŒ»‹+Y·ÃÆœ2»€¥¤ ~º²¼¢<¾W‘—ïï§Ì¬xÝxÊrJ]†É+އ÷ÞJrJ=…°pJ§1B‚ƒÉ9j`q€ÅÖǧ¢‚BÆÂÄ”IMYÁʹ®¬–œbðŸ| VáçÀWª’ ñ£òŠš4píCƒY{a,œÆu®+’“ÛÕÞ`u`ÀÂ8jÚ4ÆÉÞž9sæ k×e°Èºp < X¯×[YóD°xà$ÈgX.]ºx‘,‚žÄÊèË€Eüç›~ë••wm«°ÚÀ/_¶Œ,°ÞMµµµdŸã{æDq1«ûaqO °nÔÔ§a¦GG·ûM°8Àêì€uñ—_˜ Þ㡽ù1ÕÕÕ¬]—{ŠP°€uª¼œÑPQe232Úµïüž"|@‹Û¦áCÓýúûLÜŒXòX6î‰ÅvâKp€…‹ÛµÔÔ™ìÌÌvßµ›¬ŽX¸6·H_»–¬Lx=¿ `]¼x‘Õks€%8À²_ºŠì‡Û›´g`={öŒ Çâ\è8O&$xy÷øÁý]8Àúë|Ï™Ï8;82……Rç«+4Œ œèO‚Ö„ë­p¿#wÆÞÖŽ¹|ùr»Û™¬O°Ðgß?úã?Ú‰K—?ŠpÎÆZ¢Ž XÕUUL dªXhK` °æÌ}§½:p€136fB&M"Û7tJÀ‘__2÷=JG—1ýjC°ðÐP2ÜÍÖ» Á3%)™<ý€e‡»¹ã£ø3;3‹lÚÊVç,+s ˆ²R‰]qsQ±´43'SÂGóóY9sެO°°“Ƨ Æ{y1nNÎŒŽ†&óÔÜHë .¿àëÝ„;àG„…?ŠŽfed2Yë3È©ó607oÜૃ²Œ>–“•Eì›”˜H3­ý§söÌÙvχÀëüùó̪•+™•É)ägꊕdEFúz¨à79ÀzOzúô)³dÑ"ÆÉÞTtª(|ªÌÃÍù~×.°:8`!8á(ŽPº;»0c]ÝÈoô´H²ûðõk×X[oÇÖ§XÅ…ELòò$€ðV?ºíÆlÈË#Q<Xï÷ýp=ÆÛØ›~Ôщ,WÁÝñ9ÀêØ€…}%.;r¦}¥‡›;Ù¼9>.Žô“wY: C`€…Œ:”ÿKGG"ÄÍ&qq3Žf½-6¦8Àb߯øZÒr€õéO¢¿lyÛ‡²èG;"`aÙ`Ç÷>?ŠŸ±1:Ì;pó¢Õ®Ï_÷lõ‘ï¬x,rTj3Àâ ~'w¶“°V K€õ©¤”¤d°>qÀtÀjiÀ üNîlƒ1X?½¬ø¸¸;`ýûdËçOË?r¤I˜œjƺô:,=a,gǹÅEÅÍÂd甤¤Û²RR`ý ÀšŸpW˜êH`I„†LùNëŽVï1Ów !`=æÅDE=~üаwr=ª½{÷錘˜R‰®b>¢Âtµ43_räðaò©ÌÚ´´GªÊÄ °ÄíøÖëŠ‹Š„ªH\º¬^VšÁúàÞC¢‡êœøÙw…ć?zª¼üoO¯• X½'ú&ÀÂiÈóæÝÀê!.>0<4ìÔ-!,œžŒ›õ”gajv07'Ç> Òïä·˜oi¹TFrH÷Ù0ÔÓÛºeK3›} 2Ýúõnryv˜‚¢œõb¦†F‹öìÞýRXõk×ðiÆ2YiI>,©()kÙZñ·UVTØ ÕÔÐ`2ir¦©‘q0.—ø¯³àñ$\œ6á†ÉÂ’*+*qAý÷2ƒ%û MÒ]¢Ï8Ï=§O O–”¼ï5n/OY^!XIN~µ¬”´ÖùéQѼ¬ŒLÞï¿ÿ΃¿ëЪ®ªæ}¹`!o~Â<´s7…¡²®p¯é Ý­4Šròj>ÞÞ%öíÙÞgà :âÂsîìÙ÷ôFŒœ*!&ÞU˜:Oe9ùþ¾~Çò²²O› íŒÇHÄÅÆÞ©­ã·.¡Ӈ¥áŠJÝÁÆ*ÉÊ/4X}#¼Û·óššš:¼âÍš9“·qÃ^‰ž}e妀M{–iƒ²ã‡N©.//Èl¥æ¦fæôO§™ˆ°©Wáží„­¨ îQóse%ë ÎÙLX‡KOžÄý=/Aû°Æsˆ$@ΠtP ÈÔ§õŒ"–Î*j³ôv¾AÒ ;ÐbP"H$Ò¯ˆŒäUëϬ²Ο»àxÙ,µ³$|Üw‰Þ˜—÷Ô×ǧ¢t/h$=zŠw*ljÈP)i­1Ö6õUm1DÛí½I›éɓߙsgÏ29YÙOÆ{zBÛpQ®Ò£«ísú‚‚¨ÂË»wDú?*Ry€V€æ‚dÛâžFéèŠÉË 5qurÞ“ºb僟ÊO1>ì4í«¡¡Á‘›´ÔUÝ]\ö+•5×PQ¶6¢¥¦Þ |©•‡«Ûáµik*NW0 l:KÂÍ¡ËËÊpÛ”zG§]rÒ2ºZÚbo6"l0 å e HÐg Á QÚÈx'ÀÓ›êBóØ 4 äš J} rõn«¼BÇÓV×À0_:$;ƒÑzI@èëÒÖüyøÐ¡¦šš¦‘>úû©O#bþ0ª@ÇvªüÔËo¿ù¦ñË kì6kkh†HÊ=t‰Ðužhgh(¼î¢]{‚ct2Ö7H}.}ݺçGóó›½y“,hìHv¾ÿ>DY¥-ó6¼˜ŸðÀã·`ÛIC’™—0O(íÜF¾ }õ) % !0Qú7ÁJPHôͧ÷ƒþÔ$ÞVyEú™¹¯oÏ^ý5UÕ¼-MÍ2âãâ®æde½(,(h¾ûÛ]2*ÀÆ m1 ŒyŽ–Š ›s³s^̉Ÿ}ÍÊÂ" Ú”Oÿ^½ÚZñq4GèÚØ–àÁØ§ß ( ?¾…e.”ÍÜœœÅEE-uµµLs³3ž¤Pðãñ欌̳bc«ÌMLÒáÞ<{Kôèkb`ȃþâ½®/Åò}ZGG~¦ÓÆfÒ)€zü“Ãhc‰RP’)ƒF‚ìiôˆ`5(ô9Ȥk/ç¥0T–T ±n¢rRÒ* ¾¥å‰|Œžy%%)ùÎÖ-[ž?NF„nB‡ŒO2>yò„õ¡RœÊDˆÂŠqõÊrðå¾½{3×gÜÿbÎÜšÉAÁgƺºåj¨ªTé…÷ÓÑÔÂ5&BßBôÅÓðèß»OWÙ!RjP6ζ|þ’?¿B®+©+Vü¶}ë¶?Š ™‹¿\d~½ù+SvÆ‘@¶±3DTxtÊ•êj¦¬´ŒÙóŸÿ4®OO¯Ÿ=8 °ÒÍÙ% ýD°³ž¬”t„ªáŠJ)µ¸H‚,@Á E õtáË dF}¶Þ„–$F}½ h8õù. ) yÔïc°AgÛ1A?*3DªûÐ!RÚJò ŽcìWùû—Íœ1ãêšÕiµ»vîü³äÄ æò¥ËÌ­[·H €G¯±ØàµðšxíÛAKz°¤8ÞöÙˆÓÆ?ŠÂJ J¥ï+:\ü9:Ýh ²¤ÑÎ( d* y FýA½(( ¢°ÂÓ™R˜C¯í Í%Pg•B‡¬ç€éuUé÷ve3*ÄÎ £1ƒQd›(QDeÊð¹ |jjd´ÔHO? œÆ¶ˆ°©K/¾ž‘žþtów›ZöïÝ×x4?¿"¶¦Ò“'›+NŸ~yîì9bT\tŒO_Ôác§‰`††ÇcêïÕXºQSCN„G€ûù矙ò²ò–â¢â¦ýÐxøà¡FpLÍ_çä6®\±¢¢ÃJŸqÞ{ÍM6ë¬254ŠƒÎÕœf¼¨ÛÁƒ¯F«°²À=p=ç aËÆØÀ ÕÎ= 2ŸÛêéO532N„rÍvqpÜ^´lÉ’ë×?Û²isËþ}ûåý;Ÿ?wŽœ/Hì Î#9„`3bç†GÔîܾÃà¨huU5sáü|L‡¤ÁÎEM?;ÖxèàÁÆÛw@Ԝݸ29¥6.6ö´·§×3c“ Ɔ©&†F±šjjîàìG@žq­Ø’/‘{ÁH«G7qθíZ]èhõ‡ž (ÐRêG1x]HƒÃhÐDÐXȆBš! $5é輸~ žtIÎ4 ¥A'‚›.…&9>Áw£A2BÓ,:2µ”úÐ$ê[}éÌ…ýnQ6ý¨ÚpR'ìð-®÷ì%Ù€†úpGhWQ௒¡íåŽuqÝÍɤÄÄ[Ù™™Ï·mÝÚrðÀÆcG66••–6WVT¾Ä3o‚j®_‡Îú6™ÞoxØðª}p ?Ãß]‡¿ÁNÿ§²¢â%~~~'~÷v¸FvfÖóäåËoSêáæ¾ ÚÕ×§hcÑG'É51ϘwW7r/ð9×(ÞJêÔÎÐá[œ2í=dà mðE.&†1P®) ¹—™ü€è§…"]ê|hdGag>Ú)­¦k²@y oA[@›A߀¾e‚Ö‚VÑi½¥æ¨ó‰¥€…ÎÇœÂØÀrÄ”’:tX[­•¡ œY0 ”Û_f°¤Šªò0s€¯±@»!#µuæBÇ¼ÔÆÊj58–¨T[}Çû 81eòä³á¡aW§M ¿Ñz1­~>‰ ¨X»2åâä àSþ¾~Ç SýÞÍÉù›16¶é–fæÉ†£õ‚ᣇÊú@~Æ( •Õ…<È@7­ `È¡¥M:YÌ/æË‹¶þÙΠZÔÎXnXÄΘ Œ •¢ eovV’•›2JG÷ Ûe6Vü4ˆÚr=ÝÇnóóñ9Xv>v¾†vŽŒ˜ölŒv~ùÊÎ÷^Û90¨|âߣã<°ŒZƒ¼×v_…eÚ«»DW,c9)iM(skøÛqàK#ÀóÁ&ˉÁFã< image/svg+xml1 ...CPUM2DDB 1 ...M2DDB 3 L2B 2 B 1 CPUL1 R1R2R3 ... stxxl-1.4.1/doc/images/san00b_pqueue.png000644 001411 000144 00000224645 12350112610 017670 0ustar00tbusers000000 000000 ‰PNG  IHDRHÚŒ%OasBITUìF pHYs. . ÕtEXtSoftwarewww.inkscape.org›î<)$IDATxÚìÝ ¼Uuèÿ/** Z ¦] F§3 ÒzAŽ –£7e¦Lþ LOÒØã_ƒjf°± - uF!§ oAÆ LM$ðÔDá "ˆèï.GÏÎÅÞkïõÞïW¯Žk6{­µ>œ³×úF €¢‹câ¬ßÉ13îâ¡X bnö_·Æ÷ct|"Nˆ¶Sî]›(l¨´‹“âË1-žiÕ·oyñ£øLt·å)À¾GÊ;ãªX¹—xA|#þÄ6¤{(§Å9ÜÌœ¸0:Úš‚ mqÒ;~ãÍ=—G[U´.LÚÇßÇ–Üov}œdÛ R€=gÉÑñû=„åïâGqE|!.ˆOÄYqN|:.ŠÑ11¦ÅªÝþÉ3m]A °§(9%þgWmÿÊ"t)Å1ñɸ6– RA °7IrZ<×â+cd¼¹M·tr|/Ö RA Ж é×bŽ>_Ü»“ÝG‡ø\<"H)@ërä­-~tV¼}Ÿnõ€¸àõó˜ RA °›ŸfÞÛÂâFûnûÛxÔ¾ ¤»Œ‘Ë[X81ÇÛwÜ'H)À®RäñB³…ÿäúwèÔø‚`W)òÓf‹VÇ[lA P™9.¶5[xí"H*"?h¶h^´³])@e2¤C¬o¶p˜í"H*•!ƒ›-zzïN„ Ø› ù·f‹®µU)@å2dE³EgÛ*‚ RòÖf‹^ŽÎ¶‹ ¨T„œÓlÑÃ¶Š ¨\„Œl¶è[ET.Bþ¹Ù¢oÛ*‚ ròͲU)@å"ä×Í]d«R€ÊEÈ=Í}º·ð¹ßjß°Å)Àβ Ù¢sÚx 3ÚðÍkmqA °s„Ìm¶è\A*H*!3›-:_ R€ÊEÈ/›-ú¼ ¤•‹Ÿ4[ôMA*H*!cš-šØÆ[×îÒbA*Hv!Û-úmŽ·~½ ¤»Sš-Z/H)@å"¤}ln¶°· ¤•Ë»š-úŠ ¤•ËË›-ºK R€ÊeÈq-,Ìé—ö‚T´&Dæ4[t ¤• ‘ó›-z1z RA P©90–5[ø3A*H*—"Ã[Xø™Jiü4V¿Á±(~ß ¢³½#H€2¤H»¸»ÙÂçãÔ é»¹‰ñí8ؤ@ýÇÈ ±¥ÙÂuñžªé«Œ#í!A ÔŽ|®……ÏÇà éßŰ&Ÿÿ?n­¯ËoíA ”!H~ØÂÂWâÚ8¬BAú¾7,ÿ“øíëÿq–ý#H€ú’cJ‹W¬Ž¿Ý›ÏqÆ€X¸/Aš]sD<ÕôåÍö Ê‘¤“wqÕº˜§Ç­¼wÄ¥±¨…+Ú¤Ùu—6}ñ”½#H€rDI»¯ìòêõñŸqYœÇÅ!ÍþdÇèƒâ ñ“X¾Ë?ßö ý`Ó/ÇöŽ Ê&çÄÓ­ø¶çâ‰x$ÆÃ±"ÖdÿõÊÿÄÊ8¿ÍAú¶×¿<Üž¤@yÒäMqCÎ7¹6¾¾«Ï¡¶ê'¤ÏÛ+‚([ž¼?~•ÓM=_lþ+þVékŸ!]dR Œ‰òžøq<·7°1nŒG»=ü-»>ʾK<Ùôå·í A ”5S‰ â–V}ªt矊^gEÇVý »:iÏøuÓ—[¢»=!H€rÇJ»èŸ‹kâwñ?±mßô\Ì‹›ãYˆ¾¹M·ýZ~%†4¹ .›^fº->cR€?†Ëqt–§ï3âÏãìøh|(Þ=öæÔùoÒ–­ŠOÙæ‚`†Ðî‚ô­ûµ?‚`߃tc44ڸÕ¯Ä÷ötP‚ Ÿ }ý ¦84NÌBô…×~Jj R€ŠiÓò3bsÓ—Ÿ°•)@Ń4»æ/š¾X‡ÙN‚ âAš]7³é‹«l'A P í/7~ñBmK R€Šiví-M_|Ï–¤ÕÒÞM?#Ýoµ­)@Ń4»þæ¦/þŶ¤ÕÒ^M?#ÝÇØZ‚ âAš}Ç¿7}q­%Hª¤ïŒm~F*Hª¤Ù÷ü[Óß·½)@Þ!tR h´›iLñæ¦ïy¿í%H¤ H¤ H¤ H¤ H¤ H¤ H¤ H¤ H¤ H¤ H¤` H¤ H¤ H€šOŒƒb@3š®;²Ù5§½þçz6»î„ׯ;©iÉA¶¯ ØSbtmaaϦë5»fõënl³ën{ýºéM_tµ})€ EEvÙÛ›/|ü¹Ôðªÿzþ×õÊökRÃå[ÞxÝÙ[·_óͯÿ¦^ R€=¹¼Gó…ËÓöË´f×tK¯]Æ4»npÓ5—ýq™ ¤»Í‹Óã†NÿÑ1uLW¦É;x¾)-Wí´ôU·½¤hvÝ]‚T´1/†¿öå½)¿‹ ¤‚A RA*H)‚(_^—ú§÷¬Y™Î¶± ¨x6^fÙº‚@"HAŠ Øu^œWwž82L+)‚¨’®y·è-ihôtLÉbû R€Š©Ó> RAŠ © ¤{Ê‹scÉôJ½ÒAŠ ªF‡"HAŠ © E‚A Ò\.Ó²$½xeŒÏt²)@ŃÔèPA H¤€ Eì)/ÞÃŽù«ÉirzF"H€*É}tèøÔ#uß+2‡Û¾‚ âAê´O‚@"H€šÈ‹ÑõòëÓú´M"H€*ä…IMR@"HA*H¤€ E‚T"H€ÚÒEijšôP É´·)@ŃÔèPA H¤€ Eì)/:GÏsNZž–§-9¶èÆ´*­ø}tÏ´³)Àž˜e )‚¤‚A T:ô7iLú⊛édû R oãbúNNjZ~å–¿¿iùè7,?«iù%;,ûNQƒÔiŸ)P¼·ñÙoX0 iùô7,Ò´|Ê–hZ~õËòú¥¸ E@¿…ŸÃß´~ç…?»;ÝöªSÖì¼üº9Û—ŸùäÎËÇ.ؾü/¤R`/œ¸qç3›Òmà¾qjÓò¡oX>±iùÈ”oÆq[ÇÛ§Ái© E@=»}ÑÔ´£µMéöû´óòÿiZ>÷ Ë—7-_˜}ýÍ<ƒÔèP)”Äìüroš Emz ÿp ¹qéÔ´.§Ü{)­OkfD×è,H¤@kÞÂäŸ|izN÷M"H@Ö_Þ”§kã¶Ì¡ö¿ iŃÔèPA ÔuÎH]Ò¡/ECü— EUÒâe/H) H[{ßúÆØ.ãǤ1é AŠ AZù m”ûèП¤éýÏÆôŒƒš)P€·ðëcÖ{7ôO‹J¤Nû$H€â)è¤&AŠ A*H¤@yƒ4†ÆêÖtKÝÒ‚A uû~bœ:ã¡{ÓÆœrï…´<-º3zF÷î›Ñ¡R(Á[¸YöR@ R)RAŠ ê!HïJ½ÒÛž%qSQƒtV—¾º,Fg:Úÿ‚¨» 5ËA RA*HA*H÷qÝ?ÃvpFÓÒwí´tXtj\zØ–×ôÝg½aùÉ‚(â[øOcÁ;7õI ‹¤ÇÄ ã>9-»Í†òéìþ³éþÆè7|[ãÙ^£÷–Žhúî%oX~µ ŠªT£CǦ.éЗ¢!Ó¥¸ûä¨GwüÏž¥Q¯:ï—;Û¼¿ué/¿µóÒÏÞºý»ß´F‚´€AZ§}:qãŽÿÙ¿é¾{÷­j\ºø K'6}w¯–uÛׯ§) Hik²êã1ê¤=ÒUiB“ÿhºïs__²Ý¦Æ¥ëÞ°ôµ_ܸòÏ.ÅP<™ &ßÂ{Fïy.N›sʽçÒütç=Ñ7ŽÏá¾uŽžçœ´<-O[ʤ·íøóÏœ.· R ¨oá&5 RA RAº?ƒô±tQ:keÜç R@ Ò*齯ݰ£ì€2齩_zwCÌŽRA*H€*iñ'5 RA ÒªéãifºmA È$H) H+¤Å]£û?°*½,H)”"HgÄê#·vKÒbÉqX Н “ši>—R·ÔuK¬Ž+) HÛ–¢ëå=Ö§õi[Ž-útZœî¿/zg(hV=—Ì_žëZ;1> H÷VgÙ›Ô$H LâMÛ ÙšS÷¬Í’ôæûcPœ"H© Zó^àIM‚4ŸËº45]7'†Ä‰‚¤m»oïˆaÇüÕä49=#He/H@še/H)PAz˜N]Óã;‚T R  Aj–½ ¤€ ­õ #Æ/›˜^¤‚© ­£C)”&HçÄ–/wLsJ¤/¥ÍéùßE§èTè=#H)”ŠYöu½O6fÛïö;ãÔè!HA*H[³ÞbÁ;7õÉõì«&5‚toòâÜXrà#½R¯´ \Ajt¨ Aº÷—UirºæÁgätßrzš”þyI Ït¤‚¨þ[¸Yöu¤j© ¤‚¤‚T¥Ò…é¢töSqCŒÎá¾Wwž82L+© ¤ HMjªØ>ùfLÿ@ÃÀ´N R¤‚´jLj¤ H© ¤€ ¤9\Vfë<ü‘g R ˆoá¢Ó¦û6§WJ¤ßK}ÒŸlÌB|AVÐ}2,ÆŽzrLÚèÄø‚JÃ,ûbe•IM‚© ¤‚¨Ñ ]™&¦+ň8G%H—¦ÁéôUÙ­~VE| ¿+:oë’æpRSœ7|ËEé¢ô˜ u”½ €º} /ÝèЩé’ôéÿ‰k3 RA ÒŠ©Ñ¡‚¨ë ]œF¦ VÄÕq© ¤‚¨Bæy”}iƒôÐèºvîúÜÎ +HAº·÷íÏcöAsû¥~é!“š© Ajth­é¼Ô#µ)VÄ7) HikÖûÑ÷ž…óÓKNŒ/H oáožKæ/O[iqö‰IM‚J§T£C© iUƒ´|'Æ:MJãçe[óA ” H—¥qéKKctœ_Ô 5:TÅz ÿU¬8zK4¯ˆ³ì© ¤P‚ -ðèPA*H)Òêi¯ñ–¿˜&¦5‚T R¤­»<šÆ¤/<ccxN÷0÷Ñ¡ßK}ÒŸlÌÖ{AVÐ}rF ¿úñIéA*H@še_5F‡ R¤‚T R@ Ò.›Òü4sfôc) HÛvßÊ:©éû±¤çæ^i­IM‚JñÞ+úÞ³p~Ú$H‹³OŒ¤P:fÙ×u¾’6§çâ A RAZ… uPPª ]œF¦ VÄÕq© ¤‚hÍ[øM1ûÄýÒC¥zš”þyIvÛム¤@õßÂͲ¤‚¤‚t§õþzÜvƺÁi½ ¤ HÛ~Y™&¦+ň8G)Òedô=åƒóS~gI}õòÒ«'Aú]tŠN‚T‚tÏŒÝçËiL±$ÆÆ A RAÚš}ò™?â©ËÒF“š)RAZ•}bt¨ €Ré{cÀ/ž™6ñ3¤o‰SOúȽY,o¤‚T@]3:´®ƒtq˜NYÓãA RAZ… u”=Pª ]˜.Jg?7ÄhA*H)К·ðéh˜—fR“ ¤@±ÞÂK7:T R@V5HŸN‹Óý÷EïÌåø… J¤«Òät̓1,Î(jše/H€ºÒâe_¾ ]˜ú¤ÿÕíé¯ R@¶í¾}&¶Ä–Ž©cšSª wÆ©3º7mub|A ‚´G‡>žf¦ÛÄ€ÌAÝ'&5 R¤fÙ RA Tê-|` »îÑÉéAZ·Aº&MLcĈ8MEUÐÑ¡‚ÔQö‚iujzwŒzÓ×'¤ ¹þòZ R ®ƒt^œ>´&Kª+sÈ “š© €úãcÊYÏ M*H)P•·p³ì‹·ONß~ ÙfA*H@ Òzø… J¤kÓ´tóý1(N)jŽM]Ò¡/EC¦‹ ¤@Ý©YöÅ ÒÍiqš3#zÇQ‚¤‚´5ë=)V¹µ[zÚ¤&A ‚TVeŸ*H@$HŬöw÷OýÓ"Aº—­Ù--›Ýã0A ñ-ü¼1~ÙÄ´¦4£Cg¥qé«Ëbt¦c9‚ÔAM@ñ™e/H) H© ¤ Hs‰Ÿ~éÝ 1;~ H© ZóþÕ¸zøª‘iE'5õ)þshš-W~9¦œõÌÐÔ H)”"H-ÁO­) H© ¤@e‚ô¹4?ÝyOôãiQ‚ô©4.\£ãLA ” H‹?©©>C:"®¶æ’´Á¤&A ‚´ƒô'i`zÿ³1=shA÷‰Ñ¡‚©Yö‚T‚Tæ´²¥~é]ëbvŒ¤@ßÂÿ2F^9.=%Hë6He_AG‡Æ{büa.K—¥•‚T R¤õ2Ëþ¦48 X›Eßme9¨I¥ Ò»R¯ô¶çcIÜTÔ ­Ó>Ý[:¼Ü1­¤‚Ê FÄØQOŽIOpRSYƒ4ï$R Øoá*H© AZÝYöçÅŠVöH=Òf½wCÿ´È¤&A*H€ª1:´XYuB øåÃ3Ó‹‚T€ ¤õ°O) H© ¤À.ßÂOŒSg­¾=ºÆÁ‚(â[¸YöÅÛ'&5 R¤‚T R@ ÒœVvSšŸfÎŒ¾qŒ J¤w¥^émÏÇ’¸)‡ûvFÜÖñöÁipZ*HÔ$H@ÖËèÐié²tñÊŸé$H) H+¤&5 R@ RA*H€×ß§ƒž›{¥‚´8ûä³qõðU#Ós‚T@iuRÓGcz‡ÓÀ´Ø¤&A*H@*H) Hé^\žN“ÒøyÙÖ2ÿKÞñÔ«KþQ¶Yö‚T@ö6·zçëßáî°dhã’Å;}×ö_3_²Ã’~K6çl… ÒiBú»Å1*Τ‚T@ÎAú³,D·Ûþ äç_ÿïõiSã’—wXòÚ§2_ØaÉk§ñùV‚4fÄê#·vKpR“ Íçò@ê–ºnÉÅWRZÿ¢zT ÚA÷\–õ·]÷5H§å–DФEz| ïþ…IiRzZ:Ê^TøEuçh†å²l…íº{ä–cç L÷ç–D¿JCÓ›~ߤ{¸o&5 RA P¥—ÔotÿõŽ ¾{gº!Ýðýßîí²ëfœ¸Jî³!yÿâ8õ.Èã-ç ]šßg—Åøø¼ ¤‚ †·ãNn|K™šövÙ¸í_ RAZ‘ 5Ë>‡õþ~,é¹¹WZ+H)@õ4|rÇCc¶6¾¥lM{»Læð6÷–YÃW¥sK¢uiqúêYñvA*Hwɤ&A PO?¤9¼Í9¨I RA Pªø¹óÍKû¥ßRA*HÍåòhšÎ|2¦Ä…‚€Ö¾¤nyõÿ& RAZ™u{kô\2yÚ"H‹óx;#†_ýø¤ô‚ã R€z ÒYé²ô®›â2[VVâ§q‚4‡õ6©IÔ[6^Nµ]i é9±à ?ôI}ÒBA*H)€ ¤‚´µŸW“¾ðXŒá9Ý·ÜG‡ÎNÓUĈL‡réŠ42ýÅãquœ+H¨R>›§ï ãlÙ}Ø'þÛ˜–ÛY!Sz"ÍLç^} ±nS·ŸórYöu¤Ž² í/©K}ºGú…ƒšŠÅ‰ñ© ¤âG RA*H© ¤eý©õåýþó²´4Ç#jF¦coŒKë1H—e¸/-Ñq~÷íýqí!?¹$]’ݪ ¤‚@–;Hߤ¦Jî“c¢÷¼§m‚TTí%Õ¤&A*HK¤Œ¤U}I5©I RAšcÎISû—³W–ï Rª¤&5 RAZc¿²?4º®»>½âÄø‚ ^‚Ô‰ñén×­Wô½gáü´©4Aú¯©_:aCÌÎt.èãͤ&A Hi¶Oæ¾znØY¹íŽO}ÒÁ‹ãúzüõ°Yö‚TR“šöº=© ­ÿ Ýf¦ŸÝâxA @k_RMj¤5¤‹ÓÈtÁЏ:³¬ RGÙ R€:‰A*Hwû~SÌ>qc¿ôP'5íŸã RA € ­ÉŸZ·ßÔ%Íp”}]Œ½5]”¯Ž2‡RA € ­•·9§}ª£ 5:T°ç—T“ši éÊ41]±(FÄ9‚t¯÷É'bôè•ãÒó‚TTí%Õ¤&AZÃAšçQöe ÒFF‡ R€z R“ši­iœ úCŸÔ'-¤‚TÔr:1¾ ­Ù 5Ë>—KC¶Wnú} ŠÞuÿLõbuþ$?(ÞgÆEñwqUücüKü ¾—ýÿ•1*Î3âmÑÎ6¤u½O.þØ'¤¹íŽEiRzß×c ¤-®÷•1ë½ú§gMj¤@ã“»g|.~ó¶'Òn¼}Ïõñ…è+M‹¤&5å¢~ÏCzZ ºuÉ´Ô H‹ó`t¨ šžÖïŽoÇ£{ñ×ÄÍñ©¢žÝ¯F¶½IM‚´†=,H© òxB·‹OÇ}ûx#ÏgYzºmYŒø¤9<+†ý—IéÉÜöɼ4!ýé·âFÙ²‚´Aº*{ì^ó` ‹3rº‡¹ý§Ô+õØK2‡øQgR“ 9Úèññ½º½£ãŠX'H‹¤NŒ_¼ ½&uLí^Œ;ê1HͲ¤‚Ø›'ð·ZXøRŒöûp›ã+±^ Ò:R¿'Ƥu¤ËÒ%éÜåqm|JE}ú~²……ëã#9Üò›â¿¾¤UR“ši­i¦tÛœ6§WÊubüǨ±+&¤MNŒ/H¡$éstlh¶p]œ˜Ûퟋi›·šIM‚Tn¿o&5 RA ¥xòNi¶èÅ8-׿¡c¼Ïv®nüÒÇ3ŽXÞ'Ý“Û>¹1õJ—ÅM…X·1ìºG'§g© ¤@UžºZXøeÛEÒì¹bR“ ­á },]”ÎZ7Äù‚(âS÷Žf‹~ílAŠ ¤õ¤Ž²ŠüÄ=¹……°] °gæ¼úëáé‚TÖdÞŸ¦SׯôøŽ ¤‚Øó÷Úf‹fØ*…Ø3&5 ÒJ®Ûqùk/J•ftèƒÙ³ëǰLû‚>Þ© …Ò¼Ák›-ü¤íRAjR“ Ýíº™e/H)Pµ§íÀf‹Öõ§‚Ô‰ñ«¾O¾}âôKÒ¢ÜvÇÝé²ÔãGñyA*H[\ç’ùËÓ6A*H¡þß`›-šb«RvÉyH[=ˆafúù}1 ú Ò}bt¨ …RDÏïš-úK[¥>ƒÔ¤&AZÉ Íõ(û.Ñ{ÀûgàÍ9nøÍi}zúîèš)òYEr Ò¹©K:tk4Ä8A ëI{`ln¶ðOm—‚ì“šŠ·Oþì[_»75ä¶OVdÙö±‹ãAÚ %œeŸ÷¯ì(ê“¶G³E/ĶK}þ4NæðŒqP“ ­ä>1©IBIÞ^?ÔlÑR[E"H© ¤@åž´ÿ»Ù¢™¶Š ¥”Az^Œ¿lbZSÄÏv½Ïž™f¦ ‚t.ëÒÔtÝœ' R XOÚ/6[t«­R˜½cR“ ­4£Cë:HeõIûõf‹nlã-ôŒAmp¸mÞ†mkR“ ¤‚T R(ÁÛëUÍ]ÛÆ[øJ›¾ý=¶yõ‚Ô¤&AZÉ ½7õKïnˆÙñA*H) H©ãç·OŽ¿á¯§çsÛ›²Ô¸îô8²ëöÕ¸zøª‘iEi&5 RA Ôϯì© -'Ƥ•Û'QcWLÈþá"H)Ô÷“¶ùAMÿ!Hë5HMj¤‚tçcΗ§‡gGÏÌ~Ä*H¡ÉÓü´O³iaöŽIMÅÛ'××д ·}25 L]îŠïÔc>—æ§;q|Qƒ´ñ2«ð:A*H¡o¯û|büøËX²KO Ò"ý4NæðŒqbü::ʾ|Aº!ÍL?»;äñA äù¤íÑlÑæüF‡Æy‚T RAšÃ};?Úmè’º¤¹¥ ÒøvÌ>qc¿ô¬IM‚êüI{` o\x‚ ¤Òúº4»“6ž!ù ‚>ÞŒ¤Pš7Øß5[ô—‚´ ûƤ&A*H÷cše/H"ýJè‹~*H ²oLj¤•\·1vÔ“cÒ‚´nƒtNê˜Ú¿œ½²|WE{Úl¶¨!:Òz R“šrØ'·;o`º?·}ò‹481#®,Ìtt¨ u”½ …zÚk›-ü´ ­Ç ubü\ÔíyH‹¤qbŒ>bì¸4.=%H© …:}â^ÛlÑLA*H¤ûzøzÔí…XSrx.*H)Ôý·o û Òú R“šrØ'o™5|Uz1ÇóB.OWÝ ±n£G¯Ì÷gF‡îãz_³Þ»¡n§}¤@±ŸºÓš-º/Qz‚t·ŸIMÅÛ'޲¤5üSkA û©{z ¿.Hëí×ÂT RA*H"?yÿ­Ù¢­ñaA*H¤{{y!-O‹îŒžy| aÿé•©[:òÅlŸ®ŽÃˤ+ÒÈôÇÕq® Šùäíë›-|.ú RAŠ -ÀQö%eŸØþ¹Þç_BiÞhÇ+Í>¤UÜ'&5 RAZî 5©IB ßj¯jaáË1>: Ò*퓚© ¤‚TBÉÞjˆ[¼bi|*ÚíÕí}C)HMjÊaŸ 1njZ“Û>y"ÍLç^}éîÛGcz‡ÓÀ´XîÃåÑ44ùdL‰ )P‹IšbQ|)ºµá–Þ—Åò®ØCF¯’ýM×ÄOã‡qEöõQ‚Ô‰ñ ¦nOŒ_«‡¯™V”ftèo²¢}qEŒÍt*èk€ã R(m’^µË+·Åï³îì:LãÀègÇ?Åü>º‡ ÷Ƹx¤Ù◲˿K RAZ!fÙ RA ä‰üÉxnßòL<Óãæøq|?óã¸%{Ùüm<žåãîÿÜÝ»ú‰g–°»þS›ã ‚Ô¤¦‚쓯|pʘôhŽ¿/™Ž½1.¤‚T RàOåcã¹ßè£qÞnþÆU¯ùr<¿ŠŸÆý±q‡o(åçãÁßì•n/ìAMñ¦è¹ƒöyÙ››þëmÞ'u|PSÞA:#uI‡¾ ñ_‚T R`oŸÎçÆƒ¹Ýؼîöo{5H·dzöOPÆ_dz¯ÿêþÝ~=\¸ ½v§ÿìºÏËnhú¯Õ‚´*ëv^Œ¿lbއlå8©éŒ¸­ãíƒÓà´´\Azht];w}zE R(q’¶‹Áñ»}¼‘çâúø@+þ®ÇâÛñ––¿=Ö5}y‹ -Rf{fô‘‹w\´|lFhöÞ/;õAZÕu3:´˜Œ¤@ö´>.þ!þ°pmÜŸˆCZù·tÙå5C›¾Øi‚tÀ­oü;.Ù‡e%A*Héþ Òy©G:jS¬ˆo R 6ŸÜÝãÇâ÷;}®³% 1;~Ÿ¶*0‡¿ù í‡ödÞYº­^àIM¯éïÒü&ۚΫ9m[ößérAZGAúrÚœž¿#:EAº×ë}Lôž÷àâ¦gˆã Rà¿Æ{œŸˆ/Äe16ÆÇ?ŸìÿGÇÅ18ÞGï—¿snÓ§•nkxRÓkAº>׃L&?Hçút4+·5þ÷Ô'¼8®¯Ç -þ¤¦òÔ$Hö=HO¤Å™Ô´‚ô±lm?qE .ÏÇ( ubüÒéMip°6‹¾ÛâPA*Hv÷²Ò©é¬¨Ûvý9SAZùãïŸ m¼ (ø~¤u¤M§‚-òk@ÎAúLözò½¹1,N¤m{Y9¿é‹ß–pÝ‹¤ÇÇÕCŽL/RA*Hk&He°w/*ÇãM_ž'H 7©éÚ¼Û`yššÎÿF *ð>YÚ~S—4£>jº"n8wíEé1A*H)ÀN/*ßkúbN)×¾à“šòÒ8¨É¤¦êißÛeü˜4&=!H© *ü’òÚOE6Å»Jº Š{ÒNÑsÞÍËÓË‚TV Hå>:ô'i`zÿ³1=S’ƒš)@Û_PÄ‹M_/íV(ü‰ñ× RAZ³AZ§}:#†_ýø¤Ü>«-HÚúrrZ<ßôå?”x;RAZ¹uî{trz&Ï]:=Ç{X Íû ‚ m/&ï‹ eyáÜÍV¨IM‚´Ž‚´À£C© ¤@å_Júƺ¦/íJ¼Lj*Þ>¹øc?žVä¶Æ‹Ò¤ô¾¯ã¼…že?4V°¦[ê–¤ûpٔ槙3³WØc)Àž^Hþ4Ö6}99,õ–0©©î?FQÏç!Í5HË::ôû±¤çæ^imžûĤ&€V¼ŒôÚþKÑÌÏâ ’o “ši é©cjÿrö(¾Cîõz*Hª¼ˆO5}ù¢}é·F‘ƒôÅø¡ó/K›K¤1tè¿LJOæ¶² ÓÄÔglœSAZüã—/H_Éž±Ïÿ":Õÿ?ö½ûòÒ3V6}ù«è`{ä¤kÒüôÏÄ»s»‡eœÔä &AZÃAê &€=¿€¼-–5}ù›èd{˜Ô$Héþ Òߤ1é‹+bl¦ ¯8‚T•~ùxk<úÚÛWb{41©IVnÝþ5¦ a`Z\š m¼Ì*ôë¢ ¤@E_<ºÅâ¦/ïŽÎ¶G ©óÖ]6*ê,{A*H)°ß_::ÅCM_6Ä…1dz RA*Hi©‚ô›Ûj½N R "?mÍ·(Ýv1©©îƒôšÔ1µ{1#ÕyƒŽûä´ì6J¤yïA HÛ¾]Lj*Þ>é¸êS›sþälaÎCzZ ºuI~É÷bZ•–ÏŒîqdN÷0÷Ñ¡W¦néȳd¬ŽÃ© © ­HšÔT¼QÔó‰ñ/fÙ*HWfë<ü‘g Rª¤¹žߤ&A*H÷ÿ>ùLŒñÔeicž«mRu¤eÔtû›—öË1ÙnL½RÇeq“ ÝÃ}ë=Ï9iyZž¶81¾ ¤µ¤ÅŸÔTü_Ù;1~ë/¿MÝÒ¯~Bóç9Ü7“š© ¨ÒKªIM‚´†ƒÔèÐâéâ40²&¦Ç%‚€Ö3©I RAêÄø‚@¶ørï<¤‚T RA H© ÍuÝnŠÙ'nì—¤‚TðúKªIMÅÛ'ß>qú%iQnk|wE=~Ÿ/Ì–jtèÒì>N^ƒ2 RA @Ë/©&5ÕýO­‹tÒòiãeV¡_ÚG§M÷åwr5A @ÕƒÔ¤&A*Hk+HóÞ'‚€ªi®'Æ/餦ø³o}íÞܦ½§´"˶]§bÝzEß{ÎO›rZ·çÓâ4÷®èo¤E Ò©W:vc,‰¯ Rê!HË:©ÉQöm»ä5©éàè>ü«Òª´5Ç;·.-OÏŽž™ úx{Çö$¼äÄø‚ ^‚Ô¤&AZ«AÚ¨Œ³ìMj¤UI5©I RA*H)@•™Ô$Hk6HïI}ÒñÏe·z½ -J>&¥ñóbx1>µ,Hé¾¾Ü;1¾ ­äQöo!=.˜š¦¦u‚ÔQö‚@ RAZ/§}¤‚€=¿¤šÔT¼}rü ½8=ŸÛoÊRãºÓãHA*H© (æKªIMuÿSë"?¦Æ’ž›{¥‚T RöWšÔ$H÷¨T“šj HOa×=:9·óý Rª¤¹ž¿¬“š®=þ®¡¹ý 1¥_¤Áéˆq¥ ¤•Ø'‚€ú R“šêî ¦òéæìTOß]3í©  "AjR“ Ýíº½5z.™¿¾ð‚õé¥ÜÖxCZž®:3º Ò=Ü·÷ÄøÃ&\–%äJA*H)@-i®'Æ/뤦z>¨é®hè¼­Kš[šã×@~9¦œõÌÐÔ H)€ má¾™ÔTwAÚÈèÐâ1©IÔSšÔ$H© ¤´í%Õ¤&A*HiŽûä©ìU`ä¢g RZϤ&AZ¹u;,º®»>mËí'òSÓõóbHœ^Ô ý§Ô+õØK2‡ôñöÙ¸zøª‘é¹<Ÿf&5P7AjRSý©YöÅ{¼*H© m¶ÞƒFŒ›šÖä¶ÆO¤™éÜ £¯ ¤‚Tó%µÀŸ!ub|'Ƥµ¤‹RÿÔgmÌŠK)­}I5©IÖpÎKƒÓ‡ÖdIue÷­LéôŸCÓÐô¨ u”½ ¨å -þ¤¦åij:ÿ1¨Àûä+œ2&Ç(š•F¦co,ÆÏ«Ê7:ô—ÙÖ¿ðɸ:s° ¤T$Hs=1¾IMjªù 5:TPÛAjR“ ¤ûŸÜ[:¼Ü1­¤‚ ^‚Ô¤&A*HË}b|A @Û_RMj¤5?¹Nj³ÚßÝ?õO‹© ¤fR“ ¤ath^—‡R¿ô®u1;F Rê!HËzbü¹‡>Ý#ÍÊmÿ=õI/Žë© mq½Oˆ¿|xfzщñ)€ ¤ûkŸé<¤yé“iRúî²”Ìá4^¥ R“š)@Õ_RMj¤•|¼ÍÙ~D÷œ<׭Гš© `Ï/©&5 ÒJ>ÞJ7:ôÖtQ¼:nÈRŽ ]•&¤Ëþ£¢¿  JAjRSë½´ý¦.iFnk|Mê˜Ú½wÒjiãeV¡_œ_Ô[æzb|“šœ‡t—Eé’tÞʸ6¾$H© ¤û#HMj¤5”½ ¤TÞúÅ—NM[© ¨"“š© ÝoAjt¨  ¶ƒ´¬“š.þØ'¤¹­ñÂlûŒs ±n‡E×µs×§m‚T RiyöI‘ÎCšwüÒâí“-iyúÃÑ3ޤ´ö%Õ¤&A*H·?Þ>[bKǔ縉Ôts4tÞÖ%­ÉóñfRmzI5©©xûdðEÿ89Ç#½‹ô+û¼ƒtEšþnqŒŠórØîF‡ RA PAjRSë]Ï5͈ÕGní–Ès—še_¨ õ+{ª¤¹žߤ&GÙ Òš R5P_AjR“ ¤‚TÒ¶^Lj¤• Ò¥Yð}vYŒÏ5Hg§‰éªGbD¦ƒ ¤´ü’jR“ ­á 5Ë^ R¨Þ“ a¿¸Ï–-©âž‡ôèý»)‹MjÚÇË5©cj÷bÜ!Hi‹ëý7qùk/J ‚TB!žKlYAê´O…x†w\õ©Í9gxQÎC*H ʤ&A ‚A*H÷ç>¤­¼oo‰SOúȽÙ}Û(H© …Ö> >›½±æcˆ -ó£É¤&AZÉÇ[Ïè=ïÁŹ9ÁèÐâéª4!]ö‡ý)”âIûlAZÎG“IMÅÛ'·¿yi¿m|cê•:.‹›ê1~i·qí°5—¤ y®¶IMTi§qY\Sãw1'æÇ¢x(æe_ý:þ-þ9¾ãÑΖ¤Ô{šÔ”Ãz×ñQö‚´€7£Ci쌎qfŒË²ó…V}{CÌŒïÆ 8Ä–¤Ôk6^Lj¤ Ò\ÏCzdô=åƒóÓü´I RAZ3»¡]|8nŒç÷êo‰_Ç_Ŷ¢ Eîö¾™ÔTç!Kznî•ä¹7Mj*T>”ú¥w­‹Ù1J²¿wÁAÙÓöÑ}¾™­qk|ÈÖ¤ÔWšÔ$Hw»nF‡Ö}:ÊžJí€!ñXŽ7· >ت‚”ª=šæ±¼OöŽ^ăšöOšÔ$H© ¤µ¾ñß?ß7»Ð–¤T‘IME{†ûÄé—¤E¹­ñÝYõøQŸ²,^.Ëq_Z£ã|A*Hiy2h@¬Ýí7¬ŒÛ㚸<>ÃbHœ•ýoXöõ?Ä q÷n?mºÙ¶¤Ò_s¿pÒ::ié&5Õ@Þ ·uIk© ­‘ i¼´‹«žëãÓñ¦ÝþéâÝqIüg4RAŠ ¤‚´,Aú?i~ºëþè›9°À8“šiÍlöÑ»¸â®87:´ávŠgYºU R òh2©©xûäϾõµ{SCnk¼"˶]§ÒjiãeVá_ © ­‘þµ?ÙËÛëãcƒ ¤àÑdRSñö‰ƒši é¢Ô?õY³âRAJÞ›|p¼ÒÂâïFÇ}ºÕ.qUl¤‚”ú R“šén×íÄ8uÆC÷¦‚´8·“cЭK¦¥ó\c'Æg?lð^;ü,ó5[â‚\n»gãQû‚TR7AÚx1©IVè§qź2ËÛ;æÅ©™‚~†Ô¤&AZ›»]ÜÓlá¶<3·ó RòÞÐíâ™f ϶])uöh2©©xûĤ&AZiF‡ Òoèš-Ú‡Ø.‚Aºû7#“š© ýãå©cjÿrö<Û]© eo6ôÅÍýÖV¤ÒV\Lj¤‚tÇˬ¿ä¤Û²gìêÛ£k,HÉgC7ÿéwlAŠ ­`šÔTAzÚö¹é ‚´8·[cK‡—;¦Õy®±IM両ÓlÑù¶Š EîþòTöf>êsqrn÷Ф¦º Ò¼'HsXo£CiÁ7ô#Í}ÈV¤Ôݣɤ¦â퓟w¨šS—GÙ:Hÿ¶……´é:µé/œd›×IÚ+^䄘½_ÜT{nRSñMNû$H© ¥i3wkaaA*HiE_˜Tàž›Ô$Hk8Hפ©éúy1$NÏá¾7|ËEé¢ôX¹‚ô³qõðU#Ós‚Tr3ÚÂÂ6M™¤åÒËÒ˜ ¤5¤&5 ÒJ©Ñ¡91©Iv3·‹—›-|« ¤­ Ò|bBéÇÒÈœ¼·fƒ´ñbR“ ¤‚T’Ó†^ÓlÑŸ RA*H+³O.¤¯Þ·ž1ö¢ûǘÔ$Hi éÓiR?/Ûš§RòÙÐ÷5[ôçmúóíãÚÝxA RAZAZüIMK²·Ë?C© ¤ëýå˜rÖ3CsçÚx1©‰7ô4[ô¥¼_p© ¤UR“šŠ÷h4bÜÔ´&·5~"ÍLç^} ±nçÄ𫟔ž.M^—ú§÷¬Y™Î}¼*H ¾¡¯h¶èß© ¤õ¤É¤¦"2©©n‚´ñ2«Ðï(‚T|C7?}Ïã‚T RAZÁ 5©I Òš Ò©W:vc,‰¯ RòÙÐ]ã•f { RA*H© ÝϦ¯|pʘôhŽ542{c\ZA:'õO'?³ò8¢[:Ê^uSßÛlÑhA*Hi}†Ô¤¦â=šêù ¦+â†s׿{êù¼&5Å)qu牯žvm… ¤‚´P›úoš-z4Ú RA*Hë*HMj¤•\·Ò½% MƒžŽ)™C© eï6õѱ­ÙÂA‚T RAº»7#“š© mö\~UQOû4)V¹µ[ng>¤ì}K³E÷çó3RA*Hi}iãŤ&AZ¡ }6ÍL?¿/{¤ô¤Eù\¯ elì>-,.H© ¤ R“ši²¤‚Tysÿ¬Ù¢ ÑS RA*H[¾˜Ô”Ã>™{èÓ=Ò¬ÜÖøÆÔ+u\7 Ò=Ü·scÉôʶÖAº—Åi`:eMLK)ynî·ÅÆf ìûÓJ RAZ 5©©ˆêö<¤F‡ðÕëôvÝ£“óýM„ã³6ø¨Þ³¯O,A*Hi1‚4™Ô$Hi¹ƒÔ¤&AZ#¼]LmañÃñA*H© ݯAjRSýé1jìŠ ¹Å ¤‚´Lùsx,iañsñyA*H© ¤¹?š–¶ßÔ%ÍÈm¯IS»ãŽzü¼¢ -^®ÌÖyø#1>Τä¿ÑÇZ¼bnüù^ÜZ§ìiÿ‚ ¤‚´Ÿ!5©©x¦:>ʾ|A:-{6_¼2˳ñÑ©Aê({öw’>²‹«–Æåq|+oåÀx_|'žiá*A*Hi]e/Hi%ƒôžÔ'ÿ\,ˆë‹¤—Y…~G¤‚´Æ6üñëÝ\½,~_ˆÆ1qP³?Ù%NŽOÄ×âÿDÃ.ÿ¼ ¤‚´.‚Ô¤&AºÛuû›?â©ËÒÊ­øû»ÄYÖÞÔ8xJ|D RAZ¼ m¼˜Ô$Hwµn*H© ­¥Ír<óM>Cöðw¾3¾“ãÑ,uw\|‰ ¤‚´ÎƒÔ¤&AZÉ }G ;æ¯&gφgʤïŒSg-NsïŠÞñöœîaî£Cǧ©ûæX‘9¼Àï+F‡ ÒÝ ħâ×ñò>ÜÄʸ*þ¤•ÛÎAú² ¤‚4Ç{nRS91~޲ß?Aj–½ eÿïŠcã+ñ›ØÜ¦?´5æÆ?ÄŸµéïy5H×Çÿïħã¸ìO RA*H ?&5 RAZî }%mNÏÿ":5?ÄY²wH§øp|)nŒùñì.¾å•X¿‹ëbdœ¶7ça‹?‰ã£]³Ÿ— RA*Hë=HËzÚ§ÁýãäçæÔó¯ìs=i‡èzyõÙãm[©‚Ô¤&AZ;§côŒ÷ÄéñÑøxœgf_Gçû¯$A*H© ­ó uPSu‚Ô¤&A*H¤‚TVé3¤&5 ÒJ®Ûˆ;êÉ1é AZ·Aº)ÍO3gFß8F"H© ¤­½ç&5 Òþ¼¢ -^:¨ A*H© ­zÎÍöÖ©⯊¤&5 RA*H)‚T ÒºÒÆ‹IM‚´BA:+õHÝ^ˆ1E RAŠ ¤‚Tî 5©)—Ë5©cj÷bÜQˆuûË=zå¸ôTž{³Ð£C¥©iÒC1$Ó^ R© ¤‚´"AjRSû¤ãªOmÎùì«õzÒâiÓr‹üŽò™?â©ËÒFA*H¤‚TV-HMj*"'Ƥ5û1 AŠ ¤‚TV=~Lj¤• ÒÓª´|ft#© ¤RA*HéþÒ²žöéö7/í—cݘz¥ŽËâ¦z Ò\²ï=Ï9iyZž¶äøxÛ˜%óŠßgÉÜýë;HŸÉ^ ¿77†åøÁAŠ ¤‚T ÒŠ?šLjªJ6*á,ûøzÜvƺÁù>ÏLjB RA*HÛtÏ—útô A*HiYƒÔèPAŠ ¤‚´Þ޲¤‚T RAŠ ¤‚TV5H‹?©ie1ŸýZ|XVeÝ.k‡­¹$-+âgH‰AÇ}rZv› ‚t.óRtÔ¦XߤRA*Hi•‚´ñbR“ Ý£Cë:He ¤‚TÖW–uRÓ·Oœ~IZ”ÛÊÎJ#Ó±7Æ¥‚T RAŠ ¤‚´AjRS.êö<¤yé©cjÿrö(¾C RAŠ ¤‚´vƒÔ¤&AZÉgÊy1bü²‰iMžëVèIM5¤ß%=7÷Jk© E RAZ­ M&5ïÑôgßúÚ½9XóDš™Î½0úbÝJ7:´øAš÷O­)‚T RAZ_Aê´Oõw”}é‚´|“š)õ¼ÓïÊ^Ä^³ºiá;,›*H© ¤‚´|AúrÚœž¿#:E‡¢iÓqeE_É1HMCÓ™OÆ”¸PR;½aß°X RAº—÷ܤ&AZÃAZü£ì‹¤qF ¿úñIé…<ר‰ñ¤‚T Ò6Ýs“š© Ý~߆ÆêÖtKÝÒå R“š)­Þé£cìnýµ ¤‚´AjR“ ­Õ m”ûèÐEijšôP É´¤‚© ¤ ÒÆ‹IM‚TÖÌQöyéŠ42ýÅãquœ+HA RAZAZÖIMzÍßÎOÏå¶²Òòtՙѽë6>¦œõÌÐô¨ ­Û u”=RA*H«¤&5å¤&A*H)RAZ¢ 5©I ÒíÏ…¾1¶Ëø1iLzB RA ‚T:iÓV-뤦ïõþíEiankü‹481#®¤{ØîfÙ RA ‚T Òf[ÕiŸêǗNMërZ·âŸ¿‚ôÑ÷ž…óÓK‚T‚ ¤‚T–"HͲ/&£C)RAZÕÏšÔ$H© ¤‚© ­§£ì© ¤;^Ƨ©ûæX‘9¼ ·C£ëÚ¹ëÓ+yî'ÆA*Hi5ƒÔ¤&AZÉ ‘º¤C_Іø¯¢iãeV¡ßQLj¤ Hi½iãŤ¦}[ïŸw¨šS—GÙz–½ ¤‚© ¤Íî[I'5¥:>© ­ÿ Ýf¦ŸÝýsòxA ‚T Ò*©IM‚´fƒôŒ¸­ãíƒÓà´T:¨I‚ ¤&5íÏ ­ƒš_xÁúÜÎ Y°YöÿÓ?Ð00-.`6Ê}tè´ìÙ|ñÊŸé$H)RA*H+òÓ¸\²ï}§ÿÇü´ÍQöusPS#³ì© A*HiÍ©Ó> RA*H)RA*H© ¤u¤Ãbì¨'Ǥ‚T‚ ¤Uû ©IM‚´’ëöÞðˇg¦ 9­Û‹iUZ>3ºÇ‘9Ü·Æô3¦ü>áZAš÷?)RA*HÛ~ÏMj¤•\7£C© A*HéþR“š© -w®KSÓusbHœ(HA RAZ¥ m¼˜Ô´Ï¢㦦5¹­ìŠ,n?vqœ"Hi‹ë}eÌzï†þéÙ<÷‰IM H© ­› 5©)¿KžVꑺ½+bŠ Ýëõ6:T‚ ¤õ¤&5 ÒJiñG‡Þ”§k³è»-¤‚© ¤-ßó;ß¼´_úM!jÚ?AZ5}åƒSƤGs[㻳ÇSÅçi5‚´é¹E~GÉ9Hç¦.éЭÑã)RA*H«ôÓ8“šrXo5 ÒRGÙƒ ¤‚´¾‚ÔiŸê/H Þ¹©OZ(H© A*H© ¤USÐIMqbŒ>bì¸ì1ü” ¤‚© 5©I ÒÊi£ÜG‡þ$ Lï6¦gŠzPÓ„í?µ~F R¤‚´^޲¤‚T¶ð\~•IM‚© ¤• R“šrXï…ÖwK¿ÍmoL½RÇeqS!Ö­Wô½gáü´)§u{.ÍOwÞ}ãxA*H)RA*Hw¼˜ÔT¨Ïõ6^êô<¤—é9ÞCAºÏ—ÇÒE鬕qCœ/HA RAZAjR“ ­`ÆÐX}Àšn©[z TAcÄøeÓ yî'ÆA*Hi5ƒÔ¤&AZ³Ajt¨ ¤ Hi•‚Ô¤¦ºÿ é5©cj÷bÜQAzO꓎.»Õë© ¤ HiÍi2©©x&“šªr”½ Íç²,]’Î]ׯ§)ìîôÖìq˜ H© uÚ'A*HË¤Ž²‡Ö=€zî§¾M RA*H© Ýù2+{N|uYŒÎt¤‚© ¤-ßs“ši%×íW±âè-=Ò¼ÒiS•ù]Q Rª¤ŸÉÞòóñ.A*He/Hiëu–½ ¤‚”Êi~/8C© ¤¯_LjÊa½/=ó¦qiYnk¼0‹ð>cãA*H[\ç’ùËs;xP‚ ¤‚´êAÚx1©©PŸëm¼ô.ÐÚ5H‰AÇ}rZv› ¥ Ò¼÷‰ A*Hi}©IMu¤ñ¦èþ߬J[sZ­gÓÌôóû²ºôÍéæ>:tlê’})2]ʤs_]ã­Ùúޤ H© ­Rþw!NÍí–pRS ¾è'çxšò"ýÊÞ,û>ÞºnÿGÂËy®¶ム¤‚´M÷ܤ¦â=šœöIVrŸ˜Ô$H¤‚TV;H“IM‚T¾öxëyÎIËÓò´E RAŠ ¤‚T6mU§}¤{¸ÜŸ¦SׯôøN÷ͤ¦\. Ùc÷¦ßgï½)RA*H© -AZ¼ u”}1ïìÛ²–ýáa)H© Íåž›Ô$H© ¤u¤ûkLåta)H© Íåž›Ô$H© ¤‚T RA*Hë)HMjÊa½;®úÔæ|OÂSœóÞ ·uÉ'‚T Ò*ÄϨì%:§ RA*Hiƒ´ñbRS¡>×[¤ mTªÑ¡§™é¶Ùcy@TÐW¯Ç¨±+&¤M‚´ AšßƒûA*H© -K¾=F~ö¸\OÂ#Hieƒ´ñ2«ðïF‡ RA*H© ­§ -þ¤¦EibúÈ?Ç…~4Ýþê°‚ü^£þ=õI/Žë© ¤‚T RA*H‹¤&5ïÑTÏ5uˆN›îÛœ^ÉiÝÖ¤©éúyÿ½³€—¢jø¡AR±ÁQÄDÅ.,lÅÆîÀÆÄzE^õEÄÏ@Å.°Å$”Aéîç;wîÄÙ»³{wï½;ñ?Ïffgwî‰gfþsæ uŒÚ .Ð¥¯­öS[¤)¤i‘^“© ÍÚ¶0gjª­šÜÜzž¾z©q³ä7ùéÕVKõêÛýêûí.s‚25¤@ ¤±RÂ>¤U¤–Ë HR€ HR€ HR€ HR€4i6¤djH# ¤#õø j º “:RkZ®HR HÒ\kN¦¦ðiSŸ¶Ÿw“QzÔ\!¿¨.#êe¿™:y£s^ÑgÃl€´/{€HR€´è@J¦¦ð™QX%,™šH ¤)¤i¡Ô*djHR€ H€ Hc¤ ÍÔ¤¶{ôª²0°Æþ%CäØ3ÕÎ)@êÛî€Ã>¤)¤iÑ”LMŒ ñÒª25¤)@ ¤ER25¤†€ HR€ Hc¤B¦&€ - ®’e²øKUWÕ õ½1À1™¤IåØÉêIÕ H€ Hã¤Ä!HË)SõxÞ9V]¨Ž+Zeh¨ïŠÇ©=¦ö’ÅA¶˜Àø)¤)@ †Ö†t˜Z^{Mä•Ë>©@J¦&€ HR€´è6¤djH«²m!NªŽUãjüÞFÚÈH€ HR€ HÉÔ”p 8ìÓ™,÷tV-Ò$ðÔ¡?I?yxœ:KKídéú®ØyšæH)@ ¤iÌ”LMá3£°JLãÕwŦª'ÕÕaRã¤)¤i•©UÈÔVée¤)@ ¤)@š^·¤fjêÓöón2*°Æ¾#]dÝOÕÝ)@ ¤)@ ¤‰R250&85HUõHƒ¾WÈ2 HR€ HR25Hñ²H3Ô-©¹ì›¨þ@ V: HR€ HÒÄ馥Y–…HÕêûš?î.»Ë€ HR€ HÉÔ”p =ôÂ^ƒdf`-ž¢±í°óU‡8©CÃ7&‹tÿ½÷…ê¨Z¤)@ ¤i®5'SS%¶ñÒ^½z«‘[/ÙIf©odjHR€ H‹ ¤áÏÔ4]†ÈeWª=Ò8éx}®œ;Iݧ.H+ÜnR‡¤)@ ¤qR«©©rí¾´óK½dR`ýZëSëg‚@¶Úöjélܨ G3Ô™šR€ HR€ 6’©)Èógh(Ú–¸Ô¡ÆHR€ HÒ*R25¤iZjRHR€ HÒ¢)™šÒéݺ›~SWªR€ HR€ , ™šÒiø½ìR€ HR€ 6 ­J m£.lvU_­!3“¤·©÷šˆÌHR€ HR€ uÛ=ªî¼æòy`-~QÚHIê%€4 mêÓöón2*°¿¯¯RMßW·…¢m——:ÐL rHÉe¤)@ ¤©YÈÔ@»ñ²HR€ HR€ ­d!S@ ¤)@ ¤)@ %S@ZNY,¿É_©¶j“êÖI ¬ûÆIúšþG¢€4h»^€ HR€ -:NÒ uÚ•ªS`5$S@š¥: HR€ Hã¤djHR€4@ %;ÉæóõH_¤)@ ¤¹ ™šÂ¦Mo–<$ ¬Åƒäiü•z H}Û½µêøé˜ïde§ñR€ HÒØ)qHƒ+!‰C †ðêE¦&€ HR€ H¤Ç! ?>/‡ÈsÔ‡Zê¤)@ ¤)@ê_s25…O›êÌèºLÖÖâ%5žÚ[­G{ErÙ‡Hgês¬çHM{¤)@ ¤i®5'SSø´)ÆNMiü/{€ HR€´è@J¦&€´*ôsi.ë®Ð=ö&@ ¤)@ ¤©YÈÔfjÛiêÊžSzk¯P¦&Õ^Ý×°÷ œ…+@ ¤)@ ¤ÑÒÕ•—Ó›LMqÒÄ¥}IºÈ~ÿjè{+)NM)@ ¤)@Zt ¦¦QZŸö}L¤U¤û´»:æÅñƒ{ðHR€ HÒükN¦&€4Â@ºJ_kf~ªš¨iXìzR€ HR€´"B¦¦°iS—n½ •å ·‡¯:ÄHƒô²HR€ HR€ MïUãWÈÔT~ÝNPSªOm-­e8@Z‰²\&Ëèõ½v=€ HR€ HR€4¿º%5uh?õÏz+›Ë¬ õLM)@ ¤i^5'SSØti[õÐŽï_$oè«T0ò”œ!ÍÞV÷©ŽYZ¤±Rµ½šQºz¨t LöžaŸ HR€ Hs«9™šÂ¦KC xðŽEoÝÍêÉ“gv—IiXôm¿üC€ HR€ - ’© ­J{E€ HR€ HRÿB¦&€ µË'r»\6EõÔR HR€ HÒèi"359@Ú^:&c ¤K©µFkñÇAéõÒ7P¹çw}¿ 霼¤»ÉÀäq€ HR€ Í«ÞuÔª’•3u#·è«ÔfƒÔ£ªE õÕhis VšJC©±D-P ¤#<ƒ:…džt€ZZgM=i¤ï=AIýUj¾z=H -ˆ<n =$@}û HR€ Hóªw“p¿.ÍYT i¡u3Ãûr HR€ HR€4½vÔ€ ­2 ÝDõHR€ HR€ õ½U¤ñÒuz]ß0Pô)½+*ÙUŸAÉáÎÁ—„ÓH HR€ HC¤fCÚ® @:_–&ÍÒiPçÊoÎç ¤WØ÷ýBþ¤)@ ¤¡Ò‚>Òej@ ¤)@ ¤)@ ¤iTÕSפ)@ ¤)@ ¤iñ€Ôõ²HR€ HR€ Hã ¤?¤)@ ¤)@ F H7V Ò˜é})@ ¤)@ ¤R/0>@ ¤)@ ¤)@ ¤)@ ¤)@ ¤iâ^Ù»™šÒØé€é'¡ÒAª&@ ¤)@ ¤ÑRœšâ¤…ò²ÿ0ô@JêP€ HR€ HR€ HR€ HR€ HR€ HR€ HR€4–@:+€º6SÓóª:@ ¤)@ ¤i @ª6W=,9/mOg{Oç´=çÚ{¶H ¤1Îe¯†¨å–œTæóuìÏ—«õËì9Êþ|@ ¤)@ ¤ñÒÃ핱i{zgz9«ÆØ+G¤iÞµúÆ^95 HÕfeötQ9_AR€ HÒ*RÕQ}hÉi{.¶÷¤Ïv=kïÙ -†2S@ ¤)@š UííW[ǧí9ÁÞ“êZ{Ïz)@ê[«Œ—gõ˜½ò`ÚžñöÊÁi±€4œñÒJè/áRµ‰ê ¤)@êÔêr{åí´=ƒí•KÓö,·WÚ¤)@ ¤!R25¤)@ ¤)@©Wö¡ÌÔ¤¶R=-¹8mÏ!öžC}LCJ÷l¤!〠HR€ H£çÔTð1HR€ HR€4–@ÚT_tKdWŸÙ®Ò=›§íé`ïY HÒÀZ÷\€t@ ¤i°@º»ýjëä´=§Ø{:¤í¹ÅÞÓ H‹yaHR€´œÖ%7S@ ¤ÑÒè @ ¤)@ –¤}U5€ HR€ HÒˆé4_eeœL•ù)¹ì ¤é³9]N“‹äfé¯5 HR€ HÒé1ÔXö–;eb’t:@4Ž-ç§;Èó² HR€ Hc¤¡ÌÔF -•r©,N*áe‚f ÍHKd?ßk=@ ¤)@ ÆHÃ?¼@Z"e@ZÉ»b€t°ªM ­£ÉÓSd_ie`Y¤)@ ¤iò€ô~™áÊ4ùQÞ’«¤‘{€îi€t’ôõ‘çä5*ÿ椑ó²w€t=ŸV}*{º‡x HR€ H ¤¿øÜ~Ÿ’—å=ù#ƒÝX^ÙgÉÔ´·˲ÒR¶”öÒUî–‘±Ò¾>­Z(Ý÷ÿ„HËt®´ñÑV²½(WË`YUd }#ë·›õÃA’€Td‘gŽ HR€ H ¤eùY9OFÙ©©m9?n/_% HEQ{ÿ³aÒŒ™š²g h©Ánmh´DÊs‰R‘ÿ³÷×N€ HR€ ­ -‡»Ñ¤JªIŸD©‡·ÆHKälY]X§¦öê£ò€´ž´1¤™¤†í“( èd!@ ¤)@ H×7n¿›I”Ÿ_ ½U÷º#/j]¾D¶0ñA¢€ô\{ÏHécD—r˜}÷H³„}r€tÏ2Ÿ¯QúK3é—ÅÒÒò¹Ô´÷÷Hà+ûv)@ ¤)@Zµ@ZZfË&öÞÓC¤%åîAF'Іô×0éÌüTdŠ8ƒõv& ý°˜@*r¹½×:5=¤)@ ¤ÅR‘7í½›„H׸pöZB€tëe\ëÒ¨©È öÞ‡C ¤íý%H×ÊnاVi³Ö)@ ¤)@ZE@ê Å:…Ò,™š²©Èöþc¤wËdW&ÈW†.‘†ö¾­dR,ôN{o¯égD\ ]¨EHëH7CŽ•²ûóö2Õ§7R€ HR€´J€t…Ô°önXP Í¿< Ý,£•k|355‘s3œyÑÒKì½/gÒ©ÅÒ‡íýÇdÒØ¥=AÞÑWHR€ HÒbép{ï^!RGUªÉ¬ÄéFÖ2e´Š>:nM?dÒ9ÅÒeÒÎÞÿh‚€´®œ(¤)@ ¤iñ€ôR{ï…}eŸ%SS6 ]%Ø{OñÙç\öä‘X©ÃréþÜaÒ¿äXצzy ´®îO.•ãôãg}p¹ìR€ HÒbé2yе 2§¦Õž‘í}ëûD…Œ>¬Ï/O¦È—ò’îà\Y# ¯[w¢m R[>., F–¤[jÀôä?rµîùö¾Ö>c õó²_¢µÐ9“NHR€ HÒBéqÆí÷)¹MÎ’fîlÜ{™Ï‡*Òæié¢A‰ì®QMb ¤þaŸ~’¦öþ ¤SÊÒÖ†44~º±|íÓê°¤ÝIŸÿúŽ‹ ¤?ªºñÒ’2Jœ}¤)@ ¤…RÙX.“ñÙ·"§Ý^†eìÛøÆlïoœ6sõÔ¡4Ö­ñms8€´†*ýeqv ]`|÷Þ‘} HR€ H«HwÒc2+Ä@Zb÷v‚LLŠª¼OÌ€´Ä=m ­¯uß“mRæp7’Ÿ¤ß¹£³ HR€ H ¤-RnÀ-¼âõåÝé]2ÈgäV9M׬t_S_—§8éÉö7ºGHß4Æs < K{O-Ÿ¨²U ¤éNMsõy°—½·ž N.tó3@ ¤)@ HË:5-—Qr޹»†¾Ù†Ë©©´ü+W»3:Q@z­ý}à ¤}ÊÒt/û5ÒßMúA& ý&€º¤Ææ¤¥å{ÿÒ’¤ÿº‡y HR€ H«HK˶ûPù§@Z‰LM§ÛûN:ߨ?¼@ZÁ8¤/»ÖÁkü´È™šÖ¸}|c‚€tˆ{˜ïR€ HR€´êTäz{ÿEÒÊdjZ Õíoü uòS; ÙÕÞÿV(Tä1ûÛ$H/·¿Q³Ì HR€ H«H‡‰ãÈN ÙÊþÆS‰R/Ðe¿é9¾ ꚉ¬L¾çZ”wÎàì¤)@ ¤ÒîAî•}5·â@z¤ý+¤«åy©mïo*sc¤÷úÎþº@:©Ø@ú³{  Òß]Kr%Õ˼°HR€ HÒ*Ò•nú1¡sj*-Nöósb¤WéV{òŽ[$–@zˆ½ÿüéö7•iAôS‡f“ÆòŒO_¤)@ ¤iéóöþÚe`1,@Ú×=Ì» Ò]å[ßÞˆ>ŽuÓT>J ýÔu¢;1“SSì€t ¹Uføö@ ¤)@ V Δ Äÿ6 ýÒÅ—¦i¿ÖÔ(·™%=eTF} þ] %;Û{ë– 4 ]*Ëz®=åw±ÒYr»ô”ôãÞ`™–åú¤)@ ¤U¤ïËî!> Α_äשD^Hûet´â%J™šL ]!“ä ¹I¸¸£Ì¯ÒÔ ò€´U œÝ$gÉÒÌ8ÈÍi¿Œ.V¼¤)@ ¤iÀ@ºÊ øé"Û87ÓùPô\ö¥ÒçoÒpi69QVHsIšMªûਤÓT€ HR€ H+¤Ùäœ4<¨úLM™¥™Ïì(@] ­#wɪ´_…HkÉ‘ò“oßG7ì@ ¤)@ †H;¦9 ¤¹Æ÷“õåp £ 3ô-@5 ­#äÞ 6‹.þPX ý"%ô‘#ûk =En’×åߌ}¤)@ ¤i…ôUŸÛï~ú¦u‚\"ÏÉïåÒRbq:ò­Œ–¿Ëé[€´¨­ËèeÿµÏx~!?Ë„2™2釅ÒŠ€ HR€ H+ ¤•< ž©©â 'V¬š:ôTµ HR€ HR€Ô÷V ¤U¤YÂ>¤)@ ¤)@ ¤iv ¤)@ ¤)@ ¤ÅÒ9)@ ¤)@ ¤)@ ¤)@ ¤)@ ¤ihô U HR€ HR€ H‹¤¤HR€ HÒHi–LMiDôA€ HR€ HÒi¶ÀøiDô>€ HR€ HR€ HýëÖB= ¤)@ ¤)@Z¶vdjHsÒáÁÝ ¤«Uc€ HR€ H£¤85ÅHÿ*’Ë HR€ HR€ Í©u3R€ HR€ HR€ õ¯[cÕ HR€ HR€ H+¤¿P725¤)@ ¤)@ ¤RR‡¤)@ ¤)@ ¤)@ ¤)@ ¤ñR25¤)@ ¤)@ ¤ÅRã¤)@ ¤)@ ¤)@lëK,T5R€ HR€ Þ+{25ÅHÉe¤)@ ¤i¤€§&€ HR€ HR€ H#¤¿¤)@ ¤)@ ¤i.­›X "SS uYôelHR€ HR€ HIšÓ]/{€ HR€ HR€ HR€ HR€ H#¤£R€ HR€ H ¤ª)@šáe¿¥z HR€ HR€´lýŒV&:SÓžZã‚’—R€ HR€ î']“¦)@Z®aL HR€ HcðÊ>Ñ™š ")@ ¤)@ ¤iÅn•il€ôéé±ÎŒ|A€t®j¤)@ V1ž"Ý”ÝR€ †8uh½ìÚ©iõ½!#RäGûÓŸS>núc™o³>ænÿ¤z¤)@ H "¡Ò-e÷€dC€´¤vÔœÒÕG¥o`Ò(ì@ÚAŸE…’ÆaÒsÍžÒ†Ò:0ir EÒÐi#™¨\ ý$°¤…º0UÖ”æIÝ@çâœW¨‘&ÜP‹¤Ë” ´ ¤iQN½ÐiàRå@Ül\« ´z+gùB W#Õw9¿@Z( ëËa€4kï©zj9 Ä]1ò@ê^-öER€ -äI0®ÊÇâkñn,.LD®HÒ,½ó¤µ ¤)@š#üì[n”¸Ýe;ÙFÿ_Þ÷6HÒøéÖâãD]À®Qj¹;4õi¬–ç(«­¬Íùû©ß9ÃûÏ¥fyà~=€´uî¤zä(+­¼–ã·O ¤vµ5tå&ëÙ?9;ç_D_ßÒ8iHŸ[åa ÿ£õ“Osþ~ÇÐŽI½œÛðŒúDËC9~»]gã~Èë}c•Ek1$Q°‚;ƒ«¸æ7[‹‰¡­_¯ª1rH«¬uó­E÷ÐÖoµ8-Aú¤…Ò¼Zò¶µ(UûÐZ<áÜî[´zÐZ| F¢æ×Y‹©¡­ßÖb,@›ÖͲ—†¶~ˬÅY)@Ê6Ss”IÖ¦åøíUÞ’×K_ž¤‘jÁMÖâ×ÐÖï^k1 Dͯ,omýnµ¤±iÝtkqUhë·ÐZœ¤qkViöŠ÷B[¿Öâ­D©ZôôÚpߢÕÖb@‰š_l-æ„¶~7X‹)ilZ7ÅZ\Úú•ÆÈ½ Hã֬ǭÅ'¡­ß‹áf€Ô·WX‹?C[¿[¬Å€45?ßZ, mý®¶ÓÒØ´îwkqKhëWâJ€ [³z‡û¢žµÉò‡Ž>^d-þmý®·ãÒHÔ¼›µXÚú]f-f¤±iÝXkqGhë7ÕZ\— }H¤X‹oC[¿¾Ö"YþÐÑÒó¬ÅìÐÖï*k1 DÍOµkB[¿Ò0vóÒØ´n„µ¸7´õ›h-nHÒ¸5ënkñchë÷_k‘,èèé™ÖbAhëw©µ˜F¢æ'Ø+5BZ¿s¬Å€46­+ [÷`hë7ÎZܤqkÖíÖâ—ÐÖï!k‘,èèé)ÖbYhë×ÝZÌH#Qó.öJÝÖï k± Më¾¶}B[¿ÑÖ†[³n´á ÐStz8@©o-V‡¶~g[‹¹i$j~¸½Ò(¤õ;¹t Më†X‹'B[¿ÒÜ`¤iÜšU gBhëWtz4@©m¯TiýN·‹¥UÑÒ핦¡~üU Ië>²Ï„¶~ß[‹Þ)@·f]n- §4èô¸D©ZôôP{¥NHëw’µXž(­Š.v²WZ†üñk€4&­{×Z¼Úú}e-HÒ¸5+ìzJƒNOL”ªEH;Û+ BZ¿ã¬ÅZ€45ßÃ^Ù$ä_Ò˜´îMkÚ„Õêskñ4@ Æ­YçZ‹ðè) :=5Qª} Ý×^Y/¤õ;Ò^©‘ ­Š.îj¯lòǯõÒ˜´îUkñZÈïýR€4nÍ {€žÒ Ó3ÒHµ £½Ò<¤õ;Ä^©› ­Š.îh¯lòǯ Ò˜´®¿µmÂj5ØZôHÒ¸5«ÔC4¼z. ÷ .@êÛ‚ööJ«Öï{¥!@šoc¯ìr“‚ÖiLZ÷¼µmÂjÛ¤àU€ [³Â çœpÏा-ØÁ^Ù<¤õÛÇ^iš ­Š.na¯ìòǯ-¤MñÒ§¬Å'!7)¤qkVØôœî\€Ô·mí•¶!7)hF æ›Ø+{bRVIë wëÔÿ¬Å»)@·f…=@ÏÉážÁH}[°¹½²CHë·K¸M Ò”š·°W:aRVIë¶߄ܤà#€ [³ÑZÓ¹A§«¤jA+{¥=&i¥kÞÔ^90¤õÛ2Ü&iÞ­»ÏZür“‚ÏR€4nÍr¬é ç({¥6@¡4·W:bRVºæ í•ÃCnR°@“ÖÝe-F†¶~Y‹¯R€4nÍ ¹5kRP P Ö³Wö iý¶·I@šRóºöJ—Ö¯e¸µ Í»u¥ùdž¶~½­Å0€ [³BnMçš4H#Ô‚öJç›ìF æ5ì•®!­ßúá6)Hón]kñ{hëw¿µ¤qk–cM·EHëçnF¨uì•ÃBZ?ÇMfw€4u_c-N ií‡[ÛÒ¼[Wšprhëw·µ ¤qkV{%ìz’”%ú@ZÍ^9:¤õsÜdöH#Q÷åÖâ¬Ö®^¸µ Í»u¥ù§‡¶~·[‹q)@·f…=@t:IYP"¤º «­Åñ!­]#{å€4u_h-ÎiíjÚ+Ç'H›â ¤Ý­Å¬ÐÖï&k1 Hã֬핰èq‚N') J€t™µ8%¤µsÜdH#Q÷9Öâ’ßNHcÒºÒü€óB[¿k­Å_)@·f…=@t:IYP⤠¬Å™!­ã&s$@‰ºÏ°W†¶~+ìíiÞ­+͸8´õ»ÂZü¤qkVØô8A§wH#Õ†ÙÖâ¼ÐÖo­µ8 DݧZ‹ëC[¿%Öâ\€4&­;ÉZ¬ mý.¶sR€4nÍ {€žMÂmRfhÃßÖâ¢ÐÖo…µ8 DÝ'X‹›C[¿yÖâB€4&­;®t%´õ;ßZ,HÒ¸5+ìzZ†Û¤ ÍІ?­Å¡­ß"kq:@‰ºÿf-îmýfY‹ËÒ˜´îH{¥fHë×ÍZ,HÒ¸5+ìzœ ÓI Ð ýÃZ\Úú͵g¤‘¨û/ÖâÞÐÖoºµ¸ Ië±Wê…´~§Z‹5)@¿†•è9!¤µs‚N')@O€ôWkqShë7ÓZ\F¢î?Y‹‡B[¿)Ö¢@“Öío¯4iýºÚ+ÕR€4n [j-Â Ç :¤=qÒ‘ÖâöÐÖo𵏠DÝ¿³}B[¿ß­Å­iLZ··½²~HëwŒ½R HãÖ°Ò=ÝBZ;'èt’ôÄH´÷„¶~“¬ÅUi$êþ…µx"´õk-îHcÒºöJËÖïp{¥!@ Æ­aÿZ‹óBÞñǤÿ»ÕWêû€¤4·Îß/h) Üÿghë¼”žçó9ÖÀÜ_fª:úfRYjꥎTmÕ¯whë¼Äž/GÌò£j‘‡N5×KŸ!åËÏjÃ@®bµÕ‹ô&’ƒü«öHÃÖ°RƒüðèYl-NH«äooolByK­“§NÕS¯ÑoH9ò¶ªàuìV;õ‚d’qj ^Ù‡¯aaÐStú,€´Šþz õ+$ƒô®ˆg¯ª¦î£ï õ*«Î¢–Ó¯HF¢Ö4%„°JWbö³–Ÿ4„„Õöm…Z­V© ²ö›£[¼ZÍðˆy¤Që¨7fò•,C,†§ÎjuI%®Vçê³ØÞ¨-„K]S¯ b0¦ö²í¦-é@Ÿ'\60•ãU;âÓV!¬R5Õ‹'¤\y8ßÙU]=äl¬#ƒ„’̲JŒ@­‹Ôᕼ^¨æ;çécS’ZV‹ñd³°pYÕjœ³±»Ì¤ã\ž7íÖZuK Þ£†´ZçØN?â'«+šO^uwf´ªË¸ž%°,ƒ=uøKíÀÕj[5ÙÙ8PæÓʼn,‹äð€õ*‹Æ­kGhв©Œ¥óYÖÊ žJ,kÌõ˜ØªÎ¶•%‚”•…êÐJh–ás¡¬æº–¨ò§lï©Âð`üŸ-ŸûaÎÆv2…nN\ùK ý9(½Ê¢q†Ï}cù„H\Y*FÊûÕGÆ©Imc‡ùÖ²üƒÝ[‚e–töcªÚ¡’šµƒçs¨,äÚ–˜òƒ‘êÿ\O½îl4—atu¢Êp1ô õªŸ{{µ¦<Ë $ªÌ”Ý=E¯¶Œës¨+·\O˶2-LììÃŽžRüDžÕÂι¤e™J'¢¼)Fd§>û?WS8õäuº;1e°Ô/ ^eÕ¹S=Ÿû²–¡HHùU6õ”`¨Z/F±xB^½º^¬¿ ä{41åg14°¨~j5ÈÙh)?¨îçK7-O0ˆ!(IuÏù²]¯.p,”«Éty"JS¯.­ò;äÞj¶³ÑU–1 (ŸJO^Šº_}¤€45Ö_=y mLXyÛœ}4ªŸª®þãùÜ¿UÚ׳Ž~"ÃXä²J.4ýê,àõê`ÏBùüùÜ+­µÀ°½¬–ËL½:¢(wÈ-Õxg£c|îï¶4®µÌcÈ‹^ž“ZÞàß7÷hTò†y£RM#ÖßùÄúKÈìÃ%F‹Ì¨Y‡ª…ÎÆEûܤÅ/SÅð¡6ª‚«•‘¬>÷iÊ4iWÅz•ÕçþÎFù eY¦ï$îÏVûÄ1€Nt*ºÞšw(L¬¿_,‹¿nòº‚Ù‡ªŠê§vTSÃö¹H‹]~2-ßU ªèjedkøõ -~!A¯²êÜíÎj-y ]™%{xƒý»Ú*ž£TÕ‚Çú{Ï>úÿ¡ýE.)Qý†:ªŸj©~v6v Ôç -nyËô«Tը«•‘¬ž¼ƪ¼+ ФWYuîtµÂÙ¸)PŸ{€´Øå7ÙÜè/ãäWY µbý½Q¸y€4,%%ªßઈê§ê«·=ŸûŸÒX”‡= ä5êŠ"\¯.t,”«ÉƒilÊ£R£¨z•EãöQsœ“ô¹H‹[>7ýêûÇ˯>Â@jùÜ?X¨y€4å¢DõSÕUog£¾¼ F¼¬#·ìbuT‘®W†…r÷ÀlßÒâ•5rEô*‹Æm¥þp6öYi J?Ó¯¾g¼“0F±ÊF¬¿x€´øå‘*ˆ™Q³.ñ|î{HgÊdKþÊéHKìo/µ·ÇÈäJ9[îÔð\Ökš<#7J7¹J^ÊÁÚúOyVn’så<¹YÊÜ ßú'¥®«å¹G.‘3õßIŬYò”¾}©ÿ="ãíÏØuÏ d#ô·¯Ó5¾Hîey¥û}¡æ ëtµK¯V;xÊäs«,–£B¢WY4®©úÒÙØ\~H#]Öꫲ;´+ÔéñÆÑHij¬¿îúܤŞպ´Š¢EfÔ¬Ãõßµ7.Àç>3¾,µí¨¹ÍƾiתäÛcMóv-[Éû^+5}äñ,G}WÊÞSkÈù2Ãç›ûY{[[뙎B•2O.TsºNò»”ï*ÝçsÜ•ò¨l\¦¥W¥^7¦dö©6.òÕª¥úÉÙØ^Oaô égÉ+²&'p/ýö/ÖÖ2 ]e7ÙTöÒ'?–¹1~¢?ë$›é6t•WË9ú"­¯Ýõ·ÛècuÓ¥¿þį ³ÿþJûAª—.;K+i_FW>ךµ»Ö¾ôƒÆ}2Ýúl†ýÛÉêð·<'§è3e3}~ì­¯,äÔ#ÙÊtólù¥Øz•Eãê¨þÎÆººï ¤¿ÙãðBŽQJZßþÄÖªOå­m›é?Y•±{­ÿî!zô¶Öÿ?šAƒ¼{ǹ^ÔÚ¹‰´“.ú5Ý÷{“íÚN³õÿ99Nkè&²]™÷­cô£ö>²…ÖàÎr­®Iéìxéo‡eÔú7´ž–jýîr†¾Æ/®d¿/×½âëµoÜq4¢@šëïàÀbý¤Å,‹äsö¡]‘4k'õ—³qx¥}î3i/©fíY?çìc¾+éFµµl¬MÉÕîʾG\&'dè„f>öÙ®ÒÀ›úmçF1QïM?VC}ËÉ ¤êˆv­pˆïŸM ä÷UÃ\­ÖQo9-Ê€^°@ê¼_XW¾ÌéH ùBÊæä=IV¸h°M™}»kàó/+ä?Ò4­šë[zq^Ï×Hz˜Ö9߸ߙ¤1£ìCÓÝ]>··^õ9î,  µÒê°“|U‰^ÿÅ|p …^eÕ¹;¼kC¿égúñ±tLúæx¬æÖ÷µàp·2ÕnçbäýÔ}­äÛŒÇ|CŸ e» ®FÊôw-¯Ú{ß·^…›ƒø ñ˜v¡}}öä}[n¯_á«õJº§QËJõý¿²§w¬?ÔÖñÇÑÈij6ò`æÒb–i²³9«UĨ~jC5Ü»…ýU©Vù©gݸ…ü‘ó‘Þt/› ­ËÿÎr–Ü$G»Áuä݇ëZëmõsõÍ'Z‰c€ð½Žîå6¹½œ)÷HOš¸V´£3éõö76•.r©ÿî|ûâéÜ®kÊr¹üWºË.Öe½©\•Hÿpѱ–ì¯oʵrëß¶œ9ÿ’’ÙëñÐø?Êëè±,®uû¹uÎÁÑ= }Müºêhë-ÁgÒÈgß6¾ó?ólMñ“KÒæ( ý7íW_¹(¼ï±ÎȤc\ÍOp{±‚}þ¾ /O„E¯²êÜžÏý-•ô¹Ï¤ÿ³±y'çc9@ú›ø¹Š·±D'‹_T£F2Öçxk$³eW§´Y[H¯/ó]'åÎÜ4Lvâ¯Ìɤs%óäå圙§ïîQ¾RM“€£ÒÔläAÌ;”¤SlK¸Üìhþµ¾;Å…÷å.}¿H–ißýNÑj{¶†ˆÏÊ}M¼F†Éä—ÈÝ24ƒÁ»®sݦŸÜ*çÈy2 í¹ÿ^¹Xÿí[ô¥Ú±7œaývzÆ:,“5À\¦ëp•nϨz~¸Õï½bGõSõÕ;ÎÆ†•ò¹OÒ%®Z‡¼\ ­cY†ý`¼˜ìhï¹Z:ëÿ×3n K¤«½ïдãuw­ÌÞ7>] oô¥Ÿo[fv¡j[ˆÙѺ6ßÖØÓÜÙUsêu køér÷d¿€ú]÷LéçgåÝߘ~õW…ìzeX(?8zóÝí2Î]fÒÓ,Üj¬µ¢¯|¢ÿçV»¿¾¬g¡ÜÒ[>–ôƒƒ3ž×ù\Ú¹èwž>ÎçúÚö”þ¥3ßt[ =ßžO:I_^‘Gõ9òµ}Ýjáδ_¡ëò‹~à¸Å¾Qwͤã]ä¦rƒ®ïwúªú°~àqü>®@?núÕ_™;d'ÏçþäJYfûé=î9ÿ}Ç*Ò}d;ëL8IŸ ÉKÆŒöEúžÖÁŽ }·¼«•nrGt/Ÿãã6øH­¡ïÉOZO®vK*ƒâ¯¦h\}­i·ÈËò¤ÖÖÿÚß8ÀÕ”3ôç?ií¹Ïž©ìšH—¸&Bµô=þI[ëOsµþŽ ôø{rÁ’—Udàh¤´l6òAÒ•îzûçÌNµ´¤¼(©40Ž1XRc¾oŸ%œÕRé¥Oþ²¯WóÒÙöÞk,d8[<}>ÌøÖÙ=åXõ4d–Øîíe×Äߎê³Ì:¿_©~lFõ{, ³Z³úx>÷盧\Ø:Ò°½ÌHKPqA™±®k_@K^ÕN-3ÛÚ~¡öO™Ë]é±¶ô™…¼ÒÞ÷Œ–Èé¶ÅŸYF¹/fËÆaè^*ÞišnÐàܪåå’‘’Ùk‰êÂë•a¡|a%lßÓtŽìí0åcf2Ѩ^™dìy§>°Ì#ȶ^7H›#½ÀþÕž2&åóOíβsõf̤.>6ˆç¸ïƦÌÂdüîÕ 9gë3Ü?~Ô>OšçiâµF®2õê˜HÝ!·V” sÿ¤«ÝGÚ|Þóx@Zú°?Äøü ûå jL426³ó]™£½à¾x¯ÌÛçÞÖÇHKï[éFDÏÚû¤Øö¯v{ÀHϵ?߻̛‰íû~¼§Ë^#²ÓIÑÈ©Õ#ù ¤ ¬¹§Ò¹œ\sDy@z©Oµ·´ç’ô¨F 2Mÿ*›fè„Ò.±Ž+ó«ƒÝ<âüâÈÎú¢“H™ÆSäš ¿ú¯9«ueˆ4ë2O³ Hw_Â\·Ã”¤éð¶»ïakÕÒ=o¥|z¸}¡ô›ÙX`ÏJíà ¤ |okÛ{ïóÙ×ÝH—ÙVùúõÿ`Ï.\’sÿ¤döú[µéÕʰP>¤Â¶ïet²ÆFgN9?Ìõ€t‹´´NÆ©¬›Ûù¾WÊî±Òs¾µG´K íæS»¿ìyÉFiïVÖu¯–¹š”~z‚Ïñ°÷åsŸX"Æ“Í µkäî뫯½1.–#+ôžÇÒÚi käi—Êìëç ƒKe}Û^tLÚß™c[2o˜òýªñ >}J`­{…’ñA½l~r]KÓ÷•½ï¸<úg­ÜjúÕŸ™$¦Æú»°RžÑþ@:Í?Å5òÏHµ/©ûi…¾\öu_ý”€áKöÉÔQ×ûz}ƒr^3líózågw ¿ž¤oÔéãíï>Gu,3k5Û}ý±½}òï$gê‹þú·Ž­]u×óp¹]_¬O´-¯Ž°/öé@ú¼û› äh¹V×á|ñîüWV ÇW›ö?‹ÕÑ!Ó¬#¼­K+¤Y&~k_<•Ü]#9@º‡Ï¾çÜùÉtýüÆçoN·áà„ Ë u7ÓHoòýE+Û;Þ±&ø¾²höß u8 ¯ÐFÓÌ÷ £T«_­ å*hûž ¤?¹·ø[ò>’¤¯¥í{ÅÝw—ÏÕÈï¥ýéö㛿óÉ™öÞ>@ZÇ÷½ÓöÞ|ö½ë ¤«í™´–¾†U+m×—Üfým\ß´^mÉûc5Àó¹Ît­,Êó= ¤W¥íùÓ÷=žóÛØç¥½3ï¤ï){ï _ }ËçŸØûöñÙ7Ã}ד ¤'Û÷þÓH§ÙZŸ+¶/×”áÝ\Õ)Y8 MÍF~h%<£ý€t´ë¬q]^s€§Ú/KñXcè#,_±\L{¼1nz°gËm¾»çÈ”0'#,Kœ¹ÞHëØÖ…Ë=¥/½Z¸s¾³W¥Ù'—·VSÏœù?×8}pžý½HŽ4gµBÕOí¬¦y>÷ù;Ùx@:È^¯¨[…¤—ùìûе—J/“}.ÿìÏËð·^·÷¿ã¤~sª3³ÌJ•”ö>@z¡ýÙ/~ã ÊÜú&%³×¡÷®¯WÎöÝÒ÷ìÇØòtŽ4Ñ}á™^F».qéÎK+|æ;WÚoO:dø[Ÿú ¯3ÊÝ}Ñɾ†ýã»w{ ýÖþìÚ u¸ÍçQ+se:G}¨EöþXMÝ¥ÜyÉŠ\L ïÞ‹ºWè!ÝÒi>ûœS÷SŸ}»Ú&jfÙ×nÓRß¿´ØÖÒ«|€tGß_8s´ïùî½ÀH—Û† {fhí‡>Pœ¹ÌvMo´LPm’†£1Ò ²‘§©Ô¢º<šç±NÍ8sø‚k4]‚—©¯Ø¾w½SË¥î<]Ù²ÔöG¬“2ó0;£Uê…ùØ´W|w6©e§Œ¶¿Ù3µ;äÕC)~õ¿„uVKm¤FTÜçÞÒGí¹å†r©0´Ï¾‘î|¸ßwé¾ó|´é]Ûõ­¬|dïÊHýààK×­Ê¿ë¤;Ù/ƒ'e¨ƒ£‡å[‘6ýêûFÂÿÙ°P®ˆí»¤OÛf7õõXV¤LtÍÄç•gfXÛ࢓Ï\üµgK÷ßè¤/ú¾ºl˜õªr±Ïõ­—Ï£”øÌËCß|`úÕ?©jFüÙM­T\ËòÒoÜ^wWðJÖܾú‡Àüj»^άi¬0(¶¹Z' õrl“ý§úûéög=2Ô`ª½ÿÖzf¼lé Ø×jýäáhl€´l6òŸÒþ6hÕ­@ –SÝר‹Ò¡™û´:!ÃlRÝ”gÏÙöÜÃ6¾O‚ïûøòÍ6<ý^”;.¿ûÌ}lè ¤Î“Þ¾­í‘Ñò&SaúÕ‡zVK5PïzføÃ+¤\½‘ñ›/jDK—ùi@ê‡/¿ÚûzùÞÖ¯œ”cÓïKÒz¾uÛþþ#åÌvš@Ú2Ç:|SN§X _¡ëÕe·}ïhöÜâÐdr„\%ó|dUúÛê–²}gß}¥ofÚŸ<êšMõË µlûétÍøÂw¶Èqõ/÷ùiW×–Ú¿΃øÿÊí㾦_ýµ±¸Cî§æ:§æésïœXOÛóÙÞóøiÜò4 mçûÛ}ì‡4¿âØ-¯H3éœQãJ£çn礽|ÿÆÖ¶aƒùÒHËg¬CŒ“©å 3 Ö+Éñ«)ZóxžÑoUHïµ'ü›–{S̤~7›ÃÜÐéÅ/f£c¨?0ÃßÚ>ÍîÅR¿ EÃì}]}ö/:AeüCÊ̵÷Þœcï¼kúÕ‡>ªŸª¡õ4kpÞ@ª\óˆÌWúþéÙi@úv ½?G =$džߑ¤›øÖýåèg/®—c²å™I±@Ž˜ÿ³Öª#= å‹òzùÙ1å@›i¬ÌT>öýÓŸ¥©ê„Z6¼ø•¬}»Ÿäê ÜÕHýliÿÈ2ïož˜@z`ŽuÈþ¾k\ã}w©:.6wÈ6j¢r=ÂgWH/†Ìïyûþé»Ò€ôÐ,F›úîsœ#—§”'ùé+YæýwÊж)>@z[Žu8¥œ~Éô«¿[UK&ŽÆ H­æT*¹¤«]·Í|æ¤~Ïþçù¼- &&;WÄþÖÉö¬êš4 ÝØ÷ûgˆÉ¾@ÚÌö$ÌT6Ê2›R¶DdVK]®ëjkVŸ<ô7:ã¾-œ«Hu]ºd•i@Ú:«9A&›Ôk|€t{.¢K92:cߦX GÐÿYëT;ÏB9Û÷޶-·óˆôY(€ôš›}œÎÏbÃÚ£œ«µ ¤»åX‡l÷†%f.²Ôn±º?®¯¾ñ"½üž7Ö”LÑ<ФoäØìæ>@ꨰ¡}•Î4÷›¤WTà1,½Üî}s¥:+©0C ­\6rç×ϽյÏ`PŸ;úYÃÜ’vcH·SùÐø¬Ô©Uk»Én@Š9i@º·oÝ.·÷ú{î´_‚nï3[q|Æ:ì™ör$ÓìÃåÕRG©ÅÊu-Zž¨/eNT¼Ý3d|)}}dyA€ô´œí3sÒ!¾Žué³ë&¶òqQÈÏÙÈ-;:šþÏ–…òÈümßÒ‡í_Ö͸*ôf÷ÜèUç¤S³ •ØÏ¦é¾ö#ãÃåÔ!³áÍ 1žlƨֱ»?ÖUõ|§K²ék®½ö=!Ò÷Üëì£ýtÎ@ZêÀ¼E†¶ñÒ®§Hö:¼—±oW¸×c˯~ÿ$ãh ´¬gô ©cówX¿ê3|,þÎKÒ¾¿u^nùþ??¿\ßhOHÒÓ3Ô=³Ÿ£7Û¹½ÃByÒ²œ^ìæ)Šâ¬–j§¦+ײmQ@Z’ÙI.×.¯—e…Òks¶¤Ë Ht8)Ão;øi‡,/kó´@þ(ºþÏ–…ò{ùÚ¾{NM}m³¢Ú\£–臎tYR u¬éžÉc³é¢ žeëM õsŸË§ŒãÉæcÕ8–·üjênÏçþyéùÚ½;ú[ë«q³ ¤Ã|®må•ì@º£Ý+þñtÞ÷RçÍÖóԸٶݬ%UÛdãh,Ôšw¨P6ò÷Êè,ßÝÝçï—¤µ²éÄœ€tQÎ ›¤þ~ÏN¨çL³|íÓ€ô½k°^ÖþùY-µ±7£µsF ÷Ò88È5†ø§¨@ú–Ï…µ2@*v¾ñf¾NwÓ\ë¨q>·¸7*pO±@޾ÿs õ˜çsÿV^@Z0¼ºý2õ• ôd@:(«ÿrþ@*6n’á·§ù©£U/W>ãÉæé¨ëUV;Ûó¹¿=/ -IZá.¼¦B½:áæv HOÉ:Uó¸Ïuóõ¬nå•ßí9–|«š%Gc ¤Ö¼Ãàü=£ßsÍèw‡H— -ʵ·›š¤×d½”g¢Öi@ê<nWN NÏÒ»#݈®QžÕ2g´6Êâ5Ÿ¤%ñS­ó $γQc½<À8;vËbEê ›@úýÙ^ygø2,ׯÃÿYkÕ•ž…òÃyiIûšö/ûHçØh¼c`@z¼½wˆÏ¾¹nP&Hß©<åe®[«®ý5O¹ïÑVä¤%Wòf®QÜÚ"©È¶ö{Èùé²$tðÒíwí* q_º!´´ TuÁÑØ©5ïð_Ï3ú¼€ôÿŒ7g„t‹¿w ¤bG[Û.UϤNÎoÿÜ«í ³ ¤ÎË‘ +ló÷ž9«õT”gÌ­åÄ,›Ë~¥kO¹YJ‚ƒªR/ûr×€€ô;ûx­Ó2äL2Æ}\Š-qÛœ<žËZ _Kÿg­UG{ÊåÙ¾—M:Èžƒ®¦¯AÅR±Sçc ’H\Q~QR½xÉ&.°§êæùÒ~­\gêÕ ‰¸õ·U“”ël9' -¹â´t¯+kФ×å}_ʤ“퇪 |Þõ|ævݾSSòì‡þ¦_ý=Éõ«OVÄ3Ú ûô£ìê X@º©Ý†Åio×D=›E  ¤ÙŸíVA}̜պ.N3Z52&¿ôÒÜwæ§[åáå4Nçqü¶  ¿+c ˜HÅûÝ2ÅUêk1ßBóÅ¢=¬ÑúùIÊ'‹åèØú?kÚEý­\ëõ…yiÉ|sÝrýŸ ¤N<Ú¦9fB*HW¸ù䤽miê ¤ž¯ÿÞyÌÝ-ãÉf¦Ú=17ÿfê[gc+_†L@ZâæêX\’–\¥ê€tšû(öE @ê]Ç./£?ÿÊ.€Ôñõ__'÷ÒÓô«?M¦zF—Ÿ<5é/îÍô¢ ¼˜HO·?û* uüžå»·¯:¯ñë”ûr')³ZæŒÖåg Ò´¤?Îs0Æ H½h%ù†q)]%#ä^ËŠø”¼€ô/÷®‘þåÃòŽôÒ·ù:pvÏàjr[‡ý5ºz[Kd¨¾–hÜÆ·§·„8ú?kj¥~ñ|îÿÊHE>/×ÿ¹ð@ºÖuÜÛÖ÷5û2yVŽÏH=-­¦oàÞ5ø-kÖ½¾/þ㦦<Â÷ú:Ké–2¿èà Á¯jÓDÝþëªW•ûñU@Z•s ûÓcóº+ ¤"7¸8øœÏ]z~T; E»ÊÒßÜŒõÇW¤±VºÔú¾@ºÖ}3°½//Õö]ËðlßÎH¥\ÿçÂi‰›‰—ø$“?éób‚áò”> X¨ššYÅšÈr•Æ„íyõ¾¾@*«jÙ{êêëßúìX©³þÕái8©QÆíjŒO6Ÿª&‰€jê^g£NFg0? -yDtÌn×ÅÒÕ®«¨ÒçÅÓzôK<úÉxÝ– íöyy©H÷xuõ/ÕðØÖ¶ý_gÐFÌS´Öÿliý÷¹L@Z2ß×ÞµÚ]X -)Ošq‰Sds]÷yiIh«}Sr/+…ñè,þ‹õË_ë«Énú æÌÞJÿ‡_}BÔšwx¼|Ïh? ­è‹‰`t­‘ÎîhùÑð\ Ÿò/±fPßÈHE¬s¼­õÍ«Ÿþ»×Û7º½m¿ÁíÓ€sK÷–pünXäÎЧùIÖé8Ëø~ʬÖ3qÕ2g´dÉÇæ²RÆÉ׺îßË´>!׫cÔgã’´1yVn×òh¸Ûm™ÃÈ̵¿ûE†ëTÉ>ÿH±OXûžÊpÜuíŽÓ6u­«EkÙSNÔŸ¤}`ÿýååÖt¸¾FõÒ7ö·]{gÇzp|ÆG£»ä?5­÷:[Éþr– pÑw­û{-ËÔ‰‰mÔdg£“Ö‹Ôò‘=NK}(ï²÷Îéjð õÝþ¾ûúYûñÝ7Øþ+«3˜¡õ×㻕eR]ZêGŸ.ÒS? •-cì£üQn=§È˺®½å5÷»ŽÓ^¿ ¿˜%ÿµLJL­̰àÚ§jé…_}‚TròŒöÒ’;º/&HKn؇§¼@ØL_`w—-Œç¸ü€4SöôÍ4”ìa­µOûÅï)3µe;}ÁoïÚÙª M™Õº!)3Z5ò b”Œò£­£çVà·KÜ$ÉòÖ:µ«š¡ÜáE‘ý¥pÍ­”jFìû—øÔʈì4KíhÛ@}ïMRLˆ¬Æ­ÈÓ¸.÷r¿Ý;¿THëïôîÕ«Ô¹è[âT7¶‹7ïàç HK^Lì꾘XP -™+º+ã«°’|&ù¼²wÚ»y™— ]-{›RSÏ|Ÿ†O“Ìv;Û/mRfµ–©®ÉšÑº"o¸x—½szIæWþv_j›,ÿg­S›¨ÑÎÆNy'Rˆ{c?ðXßÎ4Óšü¦6l«§^óœ¿FÉÊ<Öll?åÿi…›:DË|u º¦yF•6Ûù½mo7ÄÅN´÷^ŸÓ3ÿ]Öwý/—/ÚGúÛgß{ß÷^ Ü"íR¬£êé›Õ-i9°ØGy¢œZ®’4š'‡ÉyúùoŠý·ôø'd¼\Pdë¿ÔÛõ¡^œ:«Õ1y3ZGå16>å5}^䯨"oLexlÇÓ¹\j¤>r6rÏ7—ŒrˆÝ//åý˱b<Ù|ž<¿ú¬WMÝçYr@ÍŒr‡Ý/ù'‡™k»„Z2Ym‹ž¤¦gô¨ìžÑá/óäù\>ÔÿÏ)ÀÑÏêoéÍ^ý¬aö3Wæubʬ֯j³DÍh¹šµKD5«2¥“ÔÔ1ƒô ‰õHôƒ<å:µUÓº’_ùд@~6Î~õYuª¦zÊÙÈ5ß\¼ÊòdšÁŸn(¹ ó°ê/-Ÿš~õý’êWŸUçÎS«œ³ö®jܧÒ3íê½H®¶gäkȘ<7AÚxû=~õižžÑI/Ž»éøí/æ¬ÖgI›}PÔ‡ÉÕ¬N)sæ©]sožÇêkZ ÷Hø%ú:ݶåxŸÄ].·^’ž+½å-®kúHwÃp)ßkÔ³nŒRݧ7sûÏ qªùÎF7Y™0¨[]SŽ‘^zm˜|)OË5Æ]í¶<ö¬ïuìëªÚúyF÷ºgt¡Ê; aÓ¼çJ|fY­ç“8û jª'† Ó¬Î:¥•Mv¶²Æ[š ärµê8µÔÙ¸´ q ¤þRSžÌëHkÅx²Y®NF¯²hÜvjг±šÏ}ü4“tÏÓaêÓ¯þ~üêÒÌ ¿ÆóŒ~,à¹Xúû[+N‚ðë*ðr͘պ)Á§Ôµžf=ž Z)_È-²¯laÏÖ‘ÖrºÖ²üfX–Î’c\®NuPÿ8GDØç>ÿò¸oþ¯Lú&¯ã,#²Ó¿j/tªk®†9m}ÝoãZ¾t“á¤J›Œy¬2•»M¿úóÑ)€4{Óõ<£¯Lœgô¿–'åõò¹L¶æ\¦ËgzË™ÔܤœPÕé³ZW›³Z'%ü¤:֛ѺŸû<Ê ÷(iÈåêTk5ÆÙÈžo.ne “{¥»/´v('Ë#yÛñÍãÉf¼Ú}ÊAãê©×fòm¢®Cãå1¹TkÚ²«ì'ÇÉòUž×ñ•r–וóÕAè@Z~ãwó<£N˜gô¿)/¿RÃI5ÍàãŸyVëï׳ԞœVZ³þIªfU¼Œ6ç&>Ãÿ¹ŒN5VŸ8ásŸGùMŒ'›¡j]t)G«¦p6êÊ@)ç2Oö÷ºqŠÚ]Hsk¾믽o¦¸–9Rë)ýDøG^GúÛѪeQýÒg´vI”fU´|$¼î{ÿg_ åg<Û÷Á¨LNå31žl^RµÑ£¼tîÏçþ”)§2ÑÍí¨åÕ-Hsï#Ö_«r0Ä I_“ 4,µ’z¶ÎÎrUÞ¯fR¢Eaö!E³>N¦fU¤÷™òÍQÌò¼O6·¡?иƒÕgãìÄùÜç_¾•f^ç½_=@šÿ¼ƒë¯a…‚%·¤D‹|Y­4ÍzÚÓ¬÷Q— e­\‡rîZu‚î#{㲄ùÜç«WÆ“ÍruºSAÛÞó¹?ÀÊåGÉT^µ£ÔXò ~õiźá:oÞá ΪK_sVë´ÈW³®G³²—¥rœiL^ñòuª£šél™(Ÿû|Ê21žlf«}ЛJh\ õƒ³±LB¹2”{M¿úîè @ZñŽ8ÞóŒ¾ÏèrKJ´Èåê4(ͺ Í*Sþ‘^W‘W!¯xetª–zÎÙh ï¢NnbÓï_}€:×Ýó¹ï…¢¹e’lãuÒªšÓF¬¿Md4gšOI‰ù…Z­ÉW³ZÉ(ÔHž1-É+^1­ºÉ³P~•²Ê ¦_}Ot$`;Äó¹?7ÏDšq-ßË^ Rë %ipRS=ël4’9ÛÊ””h‘DõËO³Ü(’Iæ°Vn0-É+^q­:Éó¹¿"ñÊkÅð­\¡Î@?  q;¨?óÌèÇòš‘þ£ª£!iÐÒÙw¨)O FyÒ›Õu;š’·fQ$û&V‹–Ê X §S{¨YÎÆQ‰Î ¶\ ßÊ9j_t£@×Býèll+“}G¼Ïó«_­.D7ÒÂtKWoÞáZ<£­²F÷„1û@T¿JkÖ5‰Ô¬™²»×ãÈ+€Nm®~s6v‘é ½>ý+Æ“Íjkô¢€·Žällg¢éø”Urž×% Õ¡è@Z¸ŽéèÍ;'K£)Ñ"çÕ¯RšåF‘<6q>÷¿Ê¦^WÙ+(ZW}îll,#x}/†oå×j}t¢ÀW]ýÇÙ¨'¯'PãæË^wLU; ia»Æˆõ·›ü“hýG÷€1û@T¿5+IÑ>5󊿈r€:UKõó,”“–l¨¾•Tô¡Jtî"µºtµšÜŸ0›"Ûyñ“j‰6¤…ï#Ö_k“XM‰ùQýѬOäDsxÎó^«nE ת›= åÇt}zIŒ'›;у*Ô¸ÃÔBgãüùÜ“æ^'¼…_=@ZuóÏ;äãDâèǦ_}fÓ¬ç<Íú(öZ´VnôOf¯BiÕɺoí¤d»Í´l?¨bÛQMu6’‰Ð¸7L¿ú‡ñ«H«¶‹nò|îŸNŽ>múÕß6ªY7zšõT¬µh™œè5¿úBêÔžºíøg[.†oå\Õ‰ñ/‚ƵT?;ÛÉ”Øß0ýê/füÒªï$#Ößõ²610ºV·Ö˜} ª_ðšu¢§Y×ÅV³fÉ^“ÇãW_`ÚBs6âl¶¾•TƾHW_½íl4—b¬q«ä¯Ù ÕaŒ=@Zœn2býŸŸû¥º¥nÕͪ`ùÍÌ+>”Ì^U Sëª!ÎF|³‚ý.†o%~õÅոꪷ³QOÞˆ©Æ-ƒ½&ÿ¥vdÜÒâu”ëow™{M‰IT¿*Ò¬±‹æð9~õÅЩÚêg#žYÁ¾÷ò,ÛC s—8>÷ÕåÁjÜŸ²½×ØŸÕ†Œ8@Zìy7Öߦ26Ö8:ÖŒ‰_}¡5«‰§YñŠæÐÏÌ+~#]¥ZuK|³‚õ7ýêïRÕíPhÜáj‘³Ñ=f>÷?J ¯¡ï¨úŒ6@ZüÎ2bý5–Oc‹£ŸêÖ¹~™Ù‡ªÖ¬Obb|³×ÀåêTF¹ÊµêÏç>NYÁzzM\©º1Î!Ò¸Ô_ÎÆ!1ò¹SŒÈNàW†§ÃÜXµäÙXâè³æ¬QýŠ Y5å™ÈkÑr9ÙkÚlµ7ã[ÚËó¹GV°bøVÎUû1Æ!Ó¸ Õpgcù3wćÄ%ÐÕêRÆ W—±þzÄÌ3z­n‘ÛP¢úU½f¹>÷7DZ³þ•=½fW[2¶EÓ©-tÿÛ»F>+Ø1|+'âWJ«¯Þq6ZÈ׸Õr¡×´EêÆ _§±þºÊ²Øàè2Ýcö¨~EÕ¬"«YãĈìô~õE·}êlD;+ØbøV~ƒ_}h5®ºêãl¬#ƒ"¬q åP¯YÓÔÎŒ-@ÎnÛÜ‹õ×QfÅGgé–¸ œ€_}Ñf´ ÍŠb4‡!²®×œ—ð«NÕV/:ÑÍ öuª_}]Æ5Ô:w©çsÿŸˆjÜTÙÁkÐpµ£ †yÞaˆ³±™üyýU·ÂmQýЬ –MÿçÛÍÐhÕ­Îj4³‚ ÷ònüê# qGx>÷ÉêÈiÜOÒÒkÌ`Õ€HÃÝuµ¼XMä³Hãègf´ÈøÕ}FËЬ(Es¸ÕkÄru#*­:Õ³}ZV°;½t+ÕYŒeD4ng5ÍÙ8TFJãÞ2ýêÿ‹_=@î»Åó¹>²8ú¼éWOT¿ÐiÖs‘ТåbDvš­öa C§S{ëq±7Ž‹LV°bDvš«ög#¤q©ÎÆŽ252wćM¿úËG€4:hÄú»)‚žÑku­ÝƬ ª_85ëÆÐkÖl1";ý®¶büB©S[z>÷ÑÈ 6Wöóª?Qµe #¦q Ô»ÎFKù9·Z.öª¿HɤÑêB#ÖßIóŒ^¦klÌ>ìÇh†U³N µf#²Ó—øÕ‡X§ÖS_8áÏ 6QŒÈNߪfŒ_5®†ú¯³Q_Þ¹Æ-”üªOSí?€4zhxFï!ŸûYf´È Dõ ¥f¹3Z{„V³¾ƒ@ÿ‡_}Èuª¶zÉÙwV°oÄð­ˆ_}„uîrµ¦tµºô±Æý%;y•_=@Õn4bým.ã"£ãtMÝÕ/¼3Z†fýB=ú~õÑÓªÛœÕðf(Þƒe{Ä5î(µØÙ¸$¤>÷?ˆ^…ßů ö¼ƒëo]zM‰ù ~õјÑj"Ÿ‡L ]¡Ng´"£U§yÊaÌ véW6ãk§¦;‡Ë¢ÐiÜ;R߫죪#F½3ÝXµä…Pãè ¦_=Qý"4£UKú…ÈÿÙ Ð9øÕGL§ ŸûBås¿R §`¬b¢q«‘ÎÆNòW¨îˆx~õkÔŒ@î4<£o ©gôZ]3åÍ>œÅ¨EmFëæPhÖ1¿ú(êÔ–zÜìÝC“lž: ¿úXi\õž³±¡ _ý¥^«£'€4>º—7ïpŠ,Ž.×µr+KT¿ˆÎh\tÍúC ýR5e|"©S†Ïý¦¡È 6I ů>~WC=ælÔ—Á!иEr„W½éjÆ Û¼ƒë½—ü*ýW×È­(Qý"<£µgQ5ë+3¯x,#¬SµÕÿœâgûN }¿ú˜êÜ•žÏ}Ÿ"kÜ4ÙÙ«ØHµ1£ÆqÞÁõŒÞBƇGÇëÚ¸•ü†Ù‡HjÖ—žf+šÃËf^ñžŒJäµêvgµ¸YÁ^5ýêïŲ=Æw´çsY}îGˆÙé}Õ‘Hã:ïàzF¯'CC£CÍh‘Dõ‹ÁŒÖºEѬ;¼Ê¬Pg0"±ÐªÓõXÚÅÊ ÖËô«?‡1‰¹Æí¢þv6Ž,’Ïý»bDvz¿z€4Þ]ëzF×–—ŠŽ£/™Ñ"‰êmÍêéiÖ‹UìW¦W9j_Æ"6:µg¡\õYÁVʹ^Uð«O†ÆµR¿8ídZ•ßÿ+.®QW1iü;÷TÏ3ú¶¢âèm^¥ˆê³­[«Ð¯¾“W…?ÔÖŒC¬tj+ÏB¹j³‚Í—Î^5&©m‹„h\Cõ¾³±‘Œ¨B[#—{ÕX¢º0i2º×ðŒ>­HžÑËõ_6fð«‡fí«æ8§V‰fMƒ@¿Â¯>†:ÕÔ³P®º¬`“e[¯ ß© ‡i\ õ„³Ñ@Þ­"[,Fd§¿U{Æ MN>÷ûÈì*ÇÑÙf´Hüêã5£õ‡³±wÁ5ëk3¯øËøÕÇT§j«þÎFÕdû^ ý?,Û¨sW9>÷5äÑ*иéÒÎûã£T+F MV±þ¶’ß«G7£EÕ/~3Z_9[T³˜~õwÐó±Ö*×B¹ðYÁ^“zÞî…e{B5®‹Zâl\!k ªq#ňìô~õi2ç\Ïè¦òe•áè—f´H¢úÅQ³ê¨—õä‹éÑ]žÿó u&½{­2,” ™ì~Ó¯þ\ú=Á×Þó¹?ZLãÞ3ýêûâW&·£owVkKÿ*ÁÑþ¦_=Qýâ«Ywxšõ¿ø?wóþÔÕ‰þN„NÊ…É ¶JÎ÷þÜ|Õ™>O¸Æm¢F9»Èô‚Ü3ýꯦÏÒdwõiÞ¼CÏ‚ãhOïÕ/îšu¦§Y·ªEsÅð›€_}‚tʰP>+Ø|9ÈûS“Õ¶ô7¢©å—Àýê¯ôþÔu ý ÒÙF¬¿3dE£Eñʉê—ÍêäÍh˜fM4óŠ­Ö§Ÿ¥S†…r°YÁ¦ÈvÞŸù¿zÄÖ¸šª¯³ÑPÞÔ¯þhïÏÌP»Ò×)¢Rcýí+s -ÒˆWNT¿¤hÖÖjB°šõ­™W|~õ Ô)ÃBy]Ðõéiîý‰×T=ú1tîÏçþ‰€4îo1";V›ÐË)ât¸‘|kù#pýÃŒù~õ Ò¬õÕ×ÎÆV•Ö¬f^ñ;±@N¬VÊAd{Ãô«¿½BÒ4îXÏçþª|î#²Ó‡ª= "f—§øÜ(Ž~eúÕÕ/y3Z‚Ѭ{ð«G­:#¸¬`zzµJGß"¾·›šált‘%•Ò¸Ĉìô¤ªIï¤Hz·»±þêÈ€ÂD‹$ª_õªšºËÓ¬—+èWoä–«ö£W¯U‚È ¶Jº{‡œ¯¤_‘Œ·‰íl´—¿+|GìkúÕ_K¿¤H¦ŽwcýU“;ÁÑ;ͨ~øÕ'W³ºéñ·7îÈ[‹æ‰á7Aµ¡?ËB¹’YÁÈÁÞáð«GÊÓ¸Fê#g£•Œª_½Ùi©:Ž>H‘l]oÄúëVIÏèf´ÈyDõK¸fí§æ:gæ¥Y“Äðïñt*%+Øø<¯OÊöÞ¡¾WÍéO¤\«©žr6ɇyjÜ1";ý£v£?R¤¼Î7býí's+-Òx¯Š_=¢T5ÑÙ蔳f}gæ¿z$E§ åü²‚ý(-¼Ã¼Ž_=’³Î]§Ö–®Ö”'óиbDv£ZÓ“)’Û¼ƒësßF&TG'ˆñ^õ;¢ú!–f5Sßä§YÿgúÕß…2â£Uw:«¹g$ëx¸½BòÒ¸ãÔRgãš}îG‹ÙécüêR$Ÿy‡þÎÆúòMÞ8úïUñ«G<ͪ«zšõu9zÔË´@îFï!´*Ϭ`ÿ‘êÎOW©óé?$oë þq6ŽÍÁçþ#1ôiüêR$ßApcýÕ•WòÂÑWÌY-¢ú!©zUMÝãlÔÉ¢Y«ä\ïGøÕ#Ùµª“g¡œ=+Øj¹ÈûÙ|u}‡THãZ«1ÎÆn2#ëñ)q t­ºž¾H‘Š ÃžÏýÝ9ãèÝfT¿séEÄG³Îv|î«É]òŠx&âW”«Sm¼¬`ûdÌ ¶Põ~2EmG¿!Ö¸Æêcg£µŒÎ qkňì´TO¿¤HEÂð¹?[Væ-’¨~HfÍ:@Ís6ÎJÓ¬ÉbDàù¿z$'*7+ØTÙÑûú0üê‘Jj\MõŒ³ÑH>òѸ¥bDvúGu ÏR¤2CaÄúÛ_æ•-rï‡DõC²kV[5ÉÓ,Óç~˜™Wü,‘œu*kV°Ÿ¤¥÷Uüê‘`tîÏçþé2÷:VmJo¤HeÈõ×V&e‰ÙÖûÑ÷øÕ#åjV3õ­§Ym=zÝÌ+~7ÈH^:•1+ØÛRßûÚè˜ÎàùÜ_'k]#Fd§OTcz E‚™wxÙÙh&ßúâè·zûƒ×˜}@rÒ¬ºêÕTͺßô«?‹B* U>YÁz›~õÐGH ·»šél/K-ûX }¿z€ rHÜXuåÕ4}Õô«'ª’όֽžfá혧ö§w jUJV°¥r‰·k~õH4nS5ÖÙè ÿÈ3¦_ý ô@Š=(gz>÷÷¦à车_ýyô’§fãÍhÙ2Qµ¥_Jè”ás¿ž÷1~õH¡4®±úÔGã–©®ô @ŠbX:y>÷çØžÑ+õšûüꑊiVgÏç^Ë·ª}‚TR§Ö÷²‚Ùò~õH5®–z¶ÌG3UGú E 50[{óe¾–ÎÌ> AhÖ6j–½:¿z$ª£^16ßÀ²)¸Îõp|îµüª6£GR¤°ón¬¿mÍh‘DõC*§YØ+X #AéT5Õ×^}½BªDçºÚHú›jBo¤Háç¤}HT?¤²zuré =¨Umí•é ¤Št®Ô°íz EªfÞá®”ð«GR E¥þ±Ò)RUCÔM­±VÖªóé  ERHR¤ƒô¹µø†ž@R E€ EŠ3H­Å[ô")‚¤)")‚¤@Ф)‚¤@Ф)@Ф@Š )")@Š )")")"@Ф@ "@Ф@Ф@Ф@ ")¤")")")"@Ф)‚¤@Ф@Ф@Ф")@Š )")")")‚¤@ " Б]Õ“9˧öÊÿæ¶ô,")")’׬gÀr(=›h­ÚVMÉY¦Û?ú»Üo^DÏ"unT:·ÚúÉœr¿÷4ý ")"ÑÕª rØô,’Qçæà Ü;R EªH«I§œdK2ïß ER EÒDtü…yØüM°~2¹Üï=L¿&HëH0e@Š@zµô Dš¤HN@zB@·#@ "9tü[8è|ú H‘ ô·€ôª5@Šä¤ýÒ¸“R€H€HR EÒHi³œ,þöµ,þöÍò6)@ ")¤)R =) Ó®@ ¤@Ф)@Ф@Ф)")@JϤ)")@ ")")@Ф@ ")@Ф@ ¤@Ф@ ")¤@ ")")")¤@Ф)¤@Ф@Ф@Ф)")@Ф)")")")‚¤@ ")@Ф@Ф)@Ф@Š )¤@ ")")@ ")"@ ")¤@Ф@ ¤@Ф@ ¤@Ф)")¤)")¤)")@ŠV¤@Ф)@Ф@Ф)@Ф@Ф1Rõª©å%F -z;Ÿ´tq0#¤@ "IÒ‘Öâ;F -z;?±q€ -\»NQgiéĤ@ ")@ ¤Åi×ÎÕôßiY¶\tR€ H‘©†¾SÔ+j¦½¹F Ww© Ê|g]õ¬zAK?µ¡ï1®²ö¾ Žµ¶Ž°ÖçZ»fÙ{i•óí¿›ú\­¶6V©_Ôµj=ëó¾ÖQº§|·±}쬭6êAõ§Zkíz#å{%{~s7gê_èû·ÛÚÇëà»·«½·^ʧZŸ=c­7Ñý1B׺dc¹z_V, jp:V½¨¦Û›kÕuYQõlxAmí{Œóì½gZ[ûZë[»–ÑÅms®WWõZi­®Öt‹jn}ú€u”kËèmé±Ï¶¶6ÕçÒ}N•l|‘ò½MÕ=Z«×Ú›sÕu´ï_ÞÈ>^gß½‡Ú{SÎWušýi]½^_]¬†Ùu_©>SgyXªô 6EË.¾i íÚJ_:Ûúß"R€HËRu°çóñBuI™ïÝl¯ MŸCUÇØ+¿«†Öv,p§œúb3õ]Ú‡©=ôž…ÖúË)ßna¯ôÒ@s}ó-•wÜïÔQO؈˜*§#²ÚÏ^9É·n÷Ù+S>`-VèµÝô=¾ì†ªÒrk³§Æøô—©›Ê|ï{e„úöÎîŠéã¿öU!¥”J)E”½”’¥¬!Ù"¢²W(dË?[$JBÈm’TöB¤-¢´Hû¾¾ÏÿÜsÏÌ;wæÌ}ï}ßûî¿óýÔ{îœÙîÜY¾sæ~ Lô½36ð‡þ;'_ ižÚ>R ))„BŠÍå;ʶ˜OC|5?SMv o5L]èN41C²(¤8RÉgúÇ­îÅ{g ¤úf5ÑŒQÓ=C÷©9¥Z†z©R¥[Í ]n.Âo8ˆBšÁYuWŒ†zo,^‰­cÆ[&óœo•Míê>œn†dQHqXÌ Ûv…˜šÒSâ )nõÍj–Ûaœghš;Ǩè6K¡6ÄZ“ÛcŽ›(GoÂR&¤ê.àiÍÁ÷á´˜™³ü—L5汊èÕnžÎ§S:w?¥×Óª rQµŒãp¯®ß\hTÒ.¤ßꇣk1硦¢nõ ŒRœ‰ zŽ·»Â¸$Z³›!Ý£k¤Ò0'¢Š©ï{‡+¿“)¤qkêÇ×_àlu“ST‰Û±¸Ï•Ô»cÆÞ‹L¶SÌÿý7M8Pïu³L{쾘@ÛM÷Ff‡ú%UûfquSt›þMW*} ÒïõšoÆ tR7XÕq†ó \]ŒF[ ö’Æj?_o†­ö=~ÏŠÎÓÿÃÉ(£¶gµGKgEܧNH{›L]žÆ)¤~ûÃÕÅæ*\„Âdg]HÕ©à8uàuS§•#“œ²Zâb5å’²h…θç«5/‘Û§ŽWëÞU2*QHIaRÔ2—§èì+i‰5¦Æ´rÌðÓMk¸éÃñ„É|˜&ƒšÔåÞÑľ¥¯u?Ø…T?µ<‚¿Ädv㪘áÇa•ɾ”"!Ö_SÒÊ•.Òõ¨lÚ0ïÅ5£âo£úGÄ obêÍ7 ¦;ìn“™áoV’Ù &\i2+qBÌðføÇý`Ò¨Z¹ªÛ!§–¾wÌðz¦¾U0.eBÙž=|[s¥ÉöŠé“«¸—BJ!MÉ÷úY·Iêú—æ sßé}TµQ©gÌ]!ŽÖãnt^ýå¡G‚kQýb±ý=©à9=—¹¾±'顺¶'bœû`f—ï$>1æ±Ú&Œ±ïíø]ÏïÖ²Ì7é3ô3ô$}"|Þ½\DNz“Ñ”BJ …¾d2·Zʺ™Ìßð&óqô*:˜z­¥Ø?5BŠ¢n Ì&²‹3Ò±¡F¦|NØ7Ec³þ»¢a*)Ò^):˜Ì\ iU"ZÊœ­ç¿ÎÝìÖö•0-P£m„×£*2-¤?šL›@Éé é¦hžoº)aß5MmpšWc³(¤¦8Þ½ÖMÝnD!¥",ÊÞmuSƒÝÇ éüì}$€Fqfß7¡u¨Žù–Áï¢,ÞÖ¹õ¾ñrN͸7¦ÅÚ^àŽ±.jî·,?ª­£Cš¶D3—ZïzÏPÂúO`¢½ Ä%SHI>RdŽœÏ­]#Á÷:óG@ܻԧj¦ÎrwlýQ–„´µí2ï.}QBj¹l£‰û0´LصAqgŠ„t=ÊY«¢™fRËZ”1•óìÏðŒÄ­óï©xÏdëGó³œG3'¤êv%š™c->`½ZF¯È[¬²:"ýû¤DHwØuºGp; )…4'…Ô9qnS‡Îoæa[„O=ã6T§¤nÐÀ&ýÉ¡OkPKÝÛñ5>rot|!EwÐ:,ÄV¤¹::Õ3ú"LŽ©}}>…BÚÕ´Ú«Öf\Ldì-RRÀ…ô2“¹9ƒ:«Ê¾áUÌÃÕ=h…/ÍÀ;¬sÈœÞïhÀ-q…tS0êZwIçÍMfJŠ„ôѸ~ï¢Æ¹ ˜A#Ž£}Ã+šÇÜih¯®<Ñ[ç9!½Éd®¿ „ é¾à3Uz…É ±Îñs•þ!EB:Â:#¢ƒ)¤ÒœÒeúÿi‘ Ó"óWJÏMURLp/8w·8Üt“±.ޮռ— š©ûèàk“&ä´éD5OPÂÕ)ÒuúTÖÏiÄ€CÝ“Ún4¢’-¤ÃM&dOw/§JÚ˜óˆn2ÉÞËf&…Ô©±<,îmÒ­Ó¼m2'…,q»9ƒI‰v´NSÛdÞ£ZÖâ!ç™UH¹£P-·»böÅoBêX3'¤#â#¨WH—Y§yÎ~vË—™*’r)Ò«­Óìg2ŸGZ¯uS´õp‰ÚÚÝÄÃð…2ÿê²Þ/z¹Ž©ÜŽŒëìü÷èOÍÞÐåÔEyœ:aüÙêp½4ùˆÌ|ŠøN?‘a›Ou•¼|‚_Ԕ߾ë Ñ_•ü¬6é<õó߇cB–}¡÷Ûîx¢ks'„ÃÌÐZæQÓ­Jƒæb)¾ÆóÁÓ%…Ô“èû-o0™É©Rœm2û~±âxÑý`Ò_¡J`ŽMÍ#ÝÑn=ËÀ–ØÐ£,iä4[R̽LC!%ZHg˜Ì«¾Îœxåó-Óò|ø'¬;£L é,sü[ß΄Zq…ôë4_›Lµ%þî?ûeIH­ÝZ¡¤9¯}O!µ¬Å»&óvȾèܤ\§r.Ú\¢zÈ2'¤Mfÿ¸bgÒ/âÞn5 YâW&S'%BÚ:Þ¯Ž¿B¢ìMDã$b1Ù7íöØ&Ò1ñ„~žJp3w0žÎÊHGj>ÅÜc(ÉÔch]|+fªô¸Ž»¡½ŒCmËÒÿ¶)‘);ÚdbŸ£mºV G CÙi¶¥PHõÝ¢¿ÍM ³õ·Åžî3-¤Ãi ”¿&¤»lïlÁ(“f)³> Ì’¾˜¢˜Ûá8 ))ÀBúS‚«ÕÕ2m1·«§½áU™Òè5òÏÒ¦vÖ.¤Ö+ ~1M ŠÅ•ñDkgEH YJô|¸ˆBçȈۭSìfÏË` É iT7‡–¯#¤¯[§˜i2!ý¹¸{Qó”éQ!K‰vµ1!E KˆE„;S)¤¸Ñè9±\_Hq Û \b7*Zxâ”cY‡©RÏã\/«Óï'(¤!µ4 w·à‘YRuƒ½8,µ=&Á]éóÖËÍ6sñ¨çÁͼ iš­É‹»ù …”`!]”àju³L[Ý=ãïÓɤF×kaHi1Ò1¾uš%¶ó§ü Å,iZ¨öFߘ³‚BjY‹oÑÒV¹Ö”Œ…Ï¢~£ÿl-ש½ýæì ö’çý e²$¤!ýÔã S%WHgšx<ú£#:á!·®fWú›$P½NOÿèO <¾v»Puß8íÑWa˜¾¨ov;$° é*µ^ú£ÑG]o×ÑG5¨ƒ-n…ùðHÃ5ÇÜÖÿE? B­Ø_ƒ'qár%ЛLÉ’ø]ÜJ!]kÂy{DËé&3ÀZz`BjyhávIñNÈç›ÃùÀ”éTë4eÌþü…”`!cžÀ›?uÉýÆóqI¬˜eYH¿Óv„´K­÷‘ý£që‚Ëdð˜´j‚BúxÜÒªq“þF!µ¬Ås^o‘Á¾X90eLŠ©šª’R!užWïg--÷‘ýˆ¸uÁÕ2hA}l‚BzG\!=6d)‹õŸÿâ itOcWàn—BÚM$Ô„òîÃúw½-@•¬ôŒfRSkäï \mþïMv±wmP×íæuŽo~™Ò¿G[’š’FæEa‚g)¤>!e¢³É´O:{áåqh„ ©å2æF5Þ›Áv)Ò' ¦‹–Ý(I!%VHóúI¯§#dÿfpû˜9!uãki«Lé »<_¹sM,8oXÏln§tv!=1n{ï)¤–µxÃdZ$=å¾}qZHÛãÌ éh“i`-­› !uZËž÷6QPÃâ„Nõ²ŽÿT\!µØ1ÍÿÈHH÷úP9˜%!u.ø­ kyÞ#&¤÷Äœåþ¦í8Ø­J¿(eBúÿ~5ÌCÞ½ö¨ÌB,¤oZ§hgkm“I!ub#OŽ{A² ©õ1†·[‹yXÖ5%BÚ'ƒ–±U(¤¤À é“ñjã¬e;ÓäëOuur*#nH¡:S§YK¯Ï„3{ mqÓà/ϰãâ¶Yü>®ÚkUÚ«–(¤z-úÅ«Œ3] óZ•q˜[÷xo …´·Í`ÜÒ 2!¤šÌe!K\k|¯hà™À£qëpíBjíÒ ‡˜Ì¸Œ„ÔRËçFýÖI‰:µSÇ[ÊFf ¤ÿXûx›îïèe2ÓS&¤–^ÜðLüZµB+¤¯d»:mŽk†”Oˆ#¤óãÞï–Á Õ-)Ò C–2ÌߦŒBJ œv4™—“ZÇÃL»¹Ý‘° Ô6¦vØ:Æ1Aˆó’Ü Îz½çéErBêï#­¥—›Œ'Åí¤ÉÒ[$Jºï· é;qoµ¯£ZÅ2šù(©©0}G§á,­Y«LÕ”¥‚è?+3¹^öˆùé™R§_ë6ÂY&ãí·‚ɼf­ë\WH­/©GO“雑çŽðì¬ ©Ûªo†µô(7ØÉ.¤·ZÛÛí6M ÊZÛX¬1»Hù”éfëcÞêæ…aßRHsXHŸŒ÷@Ã=`CßÔd™Â9Z†ÌÑ9z§DHÃú‚{&þ÷¢’ ¤åLm̾°Ç‡–iйQÂæé‚û2ÏÁ÷™>YV%¹J`µY¯z²ôˆd„ôs•ڼªKú/ÁZ0%чš–ÎßÜ`Í0!ÝëmTæÛÖ{â=Ç+ÄBšÞ·IÇ$¦r#iÌç3Mí?ÁvseÙco™º„"ÆØ,ØÝx‡ä„´œ¹…K³Uw˜`#ÁM1CW™ÆŠuÇ5n6ìÕ¡M-Oœm]?¾ZímU¼™RÇ¿†”¯Š+¤Íã´èy5dޝ–dAH'†,%Ú ~gœ6ÒìÒûl7KžòÅI éƒ6´”wKPHÏŒ+¤!ˆÌÝthx…”ä!õ¿ lÕ N-”õø›`y†¸˜=öÙ*Zân‡&ócìˆc̵#I!Uåc:Ò@S5§gæU±Ñn·V>A57„7ü]ö¯–â|£òÜ>—WÞecX£¼Ð}Ñyg×Wé¾â6"›øîΨ’!ƒgrøžQ¡ŽšL©§küü‘öèd2[bƒ¨Ü—Ïœ¨!þ7C!XŠó¾Tù¸BjêE3[U&…Ô©_º6¤|v\!­l™Â1ô‡Bæø@ðÒŸ!²ç¶GQHsTH¯ŽÛ‚¬ˆi•œŒöoš¡ËGն LÙuq…ôñ¥ü`îaËPHIÒòníà_ÁîïQNíåßౘۻh Ôro J»Ñ»†\ÞLò¼R Mv%:D/Ψ¤ŽÚˆ ®0u—É éÑ&Cð–·áîqŸ ú¥»¿ýæ­§ÅÙn¥M>ó6¹@SsuYçíÍÅÜ:ÆÛC‚L²ËÅ©»uZ ¯ÃEÑýû£³®ñ^·Ò0!­f®Úʼâ‰î¦-sàˆuŸBüåí'{^Ú.¤‘oì}>}›ÙãÓ"O!3ìß²úMMæúé&Ò[—fÒmÖ)úØÖÎS~m°}g„4ìá“Óòð iŽ i£¸KröÜd„ôÔ fç+ž_¦{+Ÿ'â ©½•ZQóÊÓ8´PHIþR]ÏóŸûáuíŽÓÑF]ÀÇæÉÃcžßN½V¾¹Ô57…[c²+ÉøËdw¨+ß—˜©I ªM<=[ïSRèôزG]†7û[|f,¤žz¡H}ð³¸çà!W^,gO4[1½Ð ÿ3M–»·Å6!Ý…Wõßùx\IT; t_ç!¸?îsyDHõo˜þºž9ê7ºI݉ qÆ›Z鑞v•‹íøQÕ4ŽØ'ƒ"&Ä.ò -ÂWf_<.õªãé(MÝ*-t•î\³_NIFHµ`:·Ab®B{ ð¼`†¿Ÿ”powª½ë6tUûÖ}s¸Îw° éKúÿ…¢¶cô÷,EÇ+å¶Þþð]—ˆ#¤+ãÎñœ9¶ÖEeAH»…,å®`à iRBêt¯[3©¥7ÿ.ÛÃm÷­^ÉiYÓx³íemê2Í,‰:?8Ì-û&®î´Ý-«ûàhf…”l!Uˬ™Á›soÓ¦š!ý,séj2?G_Bí9–vFnœÐ¶8Îtgïe=:¢˜™ã°ä„TWž¤…½mkì¥dh§eÜujÍ‹+¤åB¶èKyrŸË3BªÅ/ãŽ02ÐãIË\:¸½@Äþ>­ÝZòtZ'´^u]!Lgk¤K0óÈüÝä„TÓÍ\ç‚L±õ¨Žfn —-êö1~Çø•ð¹u)ïEäç¶:Qï燔œ´Þ‘*^¼7̂޲§_¼)¤™ÒKLæø$—ïÔ@>e¹¯Ü›¼zÚoöÓ4cE|ÓÐ00~g7öêÐA¡ì%ü­RRP„T?íÓ uzÍä—êrg:ðF3tjH‡õN­á áež8$%¤z½î1o7аÿ‹¼§5lꙈª±N·¨â¿¸.,ÐEÿ§oÐ×zâ ©¾­í“ß-è™\8MaR-s×»µê^Òðú:¯Gw3ô;{Ôˆ{Uz×7üP õ<HXHuå­ž}gž‹4T·õLDHuHÔה׿¹çXˆ27ò<"!­€’xÆ4´·¦¯³”ÜÒ+mºç)ÿ5i!uÚèÝ2ÇþÁ%šnŠuü–q…ôé¥8uqGSH3)¤-‘ÐÅ+0·êæa]ZìÛšÐØóø%9!mm2kýñ¸Ìœä÷F. –Ö^¾  æ9µ… é†ÀRœ¹ýïB!%EHõ’Ká4<€wð™ºÈOÀp܇+b_;ˆVjŒ!z”„EËOµ½ep¤;‡rIÖš5Åq8Ø9Ý·ÃÝäkw­ æV'b º±ýßb¼ºlw ç2ó½/âSÌÂ8¥9ævµÍÒŠÙ…ÔŒ3ïâ+µ¤±J *åÙ}.O ©º–¸WmµOÕ¾8 /©íØÍÜv‚Ùþ‡êã©fŒÒÖ}½¶ZBܽ9d¾•ÕÚ\i­y¤ŽcLÁ_ã€è¼Jà)@uÌÍT{×D C'ûû <­YÏÅóÊÆfã#<…S¢Gª™¥•± ©§/ÞÆêóÜâ}*˜ÛBÚ4¬>ˬ[’Ò6´ù{Ñd¼ïŸŠF§}oÿš¸B£h^ý¶7N¬(…4¾îo:£XPÓÖßlœaNö#tA ”ò>½'m4½$%¤ž7KoÙkÒÃúÆ?À}34ýŽéz™-<²,¥ƒ§d›=+î·¦’$¤ùhÝ¿ÙY.®YŒæ›í™ç„4m;'¨·}ž[³!3^Ê„´‡É4Lj5K˜V1«¬]Ü_äfÒ ¦:x‘í…]Jqšú3oTæ¬8ó{<®®´¶õÙßhÇÏq¾9…4®zê²½ôM` Š¸Ý¾øø] îÊ"¹5I énßo‚ex`‚çAË´àC ·ûšH¸Ÿbf˜‡„ëÜ»Þæ¾Bm´ºÿÐôáù ¾5…”PHs~ íoZ­o ïâBJ!Í‘-WÚ<ÛÞQUáÒóLæô$Wôm“±tÔã¾E5 !uûµ¾õ&zÙ†¯C+³û‚?#Ê»o¦{SS7ËRœ×Ž ¡fAH˹/ÌLJHµ’Þh.þŸ~wFô-^‹’RýhdvHÑXë­T Œ·Œû/ZfÐ1þèb \xÕþ>d )¡æÈ–9þð¼É<›×™BZ …4t_túä}+®sŽ ©ó®Ýk’\ÑSÝš§#}%·x>$#¤N+Ò°–4ñÏ‚î1Ãï1™À«>ñ¨› Òù¥”s;‡­G!ͼ겕Š}ŽÅ¦Ã—…TOy8îÃ÷j?Þ‰?•†ÞmîoúIû!Y!Õíenô´Bu~ûsã´;º7&qÞt •‘êã+¶qù?¸"ïK!%ÒìÛg`®òEì×à “Ýæ„·PH)¤Ù¾…a®º¢Åľ£†ÛYý4¡Fê¢-ê¾K®‰¸šò}“Y—þ¾puùÚ´ÚK^H˺¡#±Ý¼îï¾éõïØuÄQn-Ú11÷ïñt…&¤‘¥”ÙÎ5Þ‹û­ ¨æé¹„‰³3Óshˆëу՞Ñ-ãKÊ(Ù¾ãn\ÖÜ=(¤ús]u[uŸºê¥¶hBkF!%ÒìÒÈŸõø/¡/ú©åÙ¦#ô=òä:SH ªFþlŽt̯®Fwc¾v«lBù ™zB?Vãuu¨i›ÀªVñ¼~m©šËSê¾3Z·ô^H^Hõœnæ¡;£”úwƒ±¿§¦poW§šSQ-ÑÃ&MŠ+¤Ñº¬ŸÐu”À6ÀµXdJ6ÆG0…4 §.¶Ož[3ŸfbRB!Ín!µ±76¾žBJ!Í!µ‘†þytsAHëXºV}*¡•­eér8òn„ÃLxŠïÊHHÕWĬ‹·¶Ý¶ ¨= |‚†q…ô2|äy(›>ÂöôÚ^ i9$й ×§RH …4‰-q8Fz*NvbLÞ{ù&…´€ i% s+¾¼Â>Hä=O…FHUI¼ïë~÷©W·,uß§aú\ÊíM²Bª»^`ü[°vÔŒßÿ>‡’tŒ)J»I¥ó'NÉðSH³oç?Òª£N{àÏóàSH …4ïo£Ð ÝÑO` z¡ul_‹yð¬ò´âÒ‚eš8×á Æ胳mïSÊCkÛIï‹OgÔ#ÐS?Z¿Ê7øN=´‹u’CÍãø¦!w’§ ‹™>¡Göî”%•æÝˆ¾¸ZÉd 3,ÚIÓ/úÏȼoÏ`~Åq¹ºG\¦[úìÆrŒUÓ+Å·ã ÝX`+Íl¡‡d¾É v!ÕŸZa”º{Þ­Ûu|[iEK!ÍÆ'>Å黾’ÑVî[-ö¡%…”BJ(¤„BJòدŸ§W®´i6þZçS6É¥føJµX!5ÊØÞÂ@!Í…ýf—QÏåêc<~‹y{õ½y´.ƒBJ(¤„û…”BšGW®¯Éä¹F›&9 iv ií¸1Ï>\£ )á>G!¥æÉU;ÄÄÚïÌ{ïÝ¥æéº-†`žoà¯x8~×KR )¡ ))ÔBŠa8Ëÿ˜mÜ8²‘yRy(¤y}×.‡šh†vjOj–wUÔ¬ëá8_s…”PH …”BJ!Í­•XŽÈ»çGw%MÐýÜ·DÞSs…”BJ(¤„BJ(¤R iö ©eh”G SH …”PH)¤„BJ ~í°/à<ºÙ(¤„BJ(¤RB!%IHÕj”Ç9x£0ß(&á%tAå<¼ÙNÄ\M¦/ØRB!%RB(¤$O i!ÜðRB!%RB(¤„BJ!%R )É+BŠ ¨©)‘£Oçjèeî()ŠÃtI î …OHÕ~ÝKåàw/köÅ@G—(‚*f}ŠRH …”PH …4{…ôn“©—ß­îÁwøÏð§¬Æc±Ù°‡{B!ÒkM¦U|çbè‰YXíî)k€÷ðvºöϵ_¦â\ôÀ•h‡C)¤R )¡ iÖ¿Y·Ÿë€bÒbJrMHuÿă0Ë•¬DèL!-xBŠ£0ß7ÈR<Œ=¾²RÁI‚%¾Á¢¤RH)¤„BJ(¤Y©‘úÉýð~×ô2e]Ü’]ê")ù%¶|GL¿ÁWPH  Nw³ë;8Дœå–ìV-Û/‡»°"´p5:RH)¤RB!%ÒÌ~¯î&óšÿn(m.¿«Ð67[ëá ë` iRt2™qx fò÷èu X®ý3b>îRGÆ>Ïç}èJ!¥æQ!U§ónšãrhyÅP§â ½Ì‹|eÕåô³>µ)¤RB!5Ë«ÿ¬A™°oŒž¹¼å½Bú7þ¢X!ªÿl>þFU“˜«¿Ç #Å/â"µFEôU¾‘Zë½f„½8†BJ!Í›BzˆÉ<–Ë*ƒê0N°ÜSÖsbF¾<—¶GœŽk0cðžB4ÐRB!ÍU!ýAÿùÌRr¡É4Ïu!]†ÐmqGO)¤OH?ÖæYJN5™ö¹,¤ ÑÍzë¶Éd'RH)¤…\HQ K}ƒ\!U§qÿè9.¤jý®ÆDì üîÙ!¥RB!Mø{mÐÞŒ³ÕsyË—°Ö—RH žFƒë¦ÆYƒ¦¹ú{´ k.€ËLfO°›* )…´p ép“Y‡)¡> Ç£¦ä(W¿Ç›ºd8ŽÏáíÐ5ée¦¿å…”PHíBŠÊ¨–êtæqãë–’A&S5ÃyUg»Á𣠦ªˆÚ¨éøÒ\RTBuOñ÷]¥ÿL±”ô2™cØ+£&Ê%¹äýÔ¾˜¥˜}·)ÉÙR i!Ru EVO žÜMû°4´ÎÅíp»çÃ|·ñ –y:“™—ê3(¤¤` )J ›ºu‹ö¿¸ßàf”´®áhM#ëü®4¥æ™Î4Ÿ£GâóÉËÏfÒw<Ã|ÁMhާ°ÀmC·/ãDëÒ/2ÓWЭÝ/Â$l5E'SHó“*á»TÁ£ Äöaî°ÉŽ4¿÷IÖù]`JM”<Ž7Ÿ·›½(¸/Î6“Ž÷ «è›ë1x\í³NõÇ¿xgZ—ÞÎL_UKuÄûØlŠ.ÈÂo5Îdº¥ì×WŽapŽìjû© õH|†…z™ cÊÎÀµ‘~ÄR]v-…”Bg9˜Ì)–²?ôŸÉ¹º¢Bú›:qÕõ m Úùð…”PHÄÕ1+P¼ øðçÆ«£Á0“q„´g¦6ƒ§~Uñuœ·P>°ôÇLæTS7¥Þ¢3(¤ùGHq°ç¼íðWðÚƒMÆwŽLÆ<Ã%™Ú'RB!5™Ýâû¢‚â6Ði›BŠc°Ü3|/¶y>͉„Y…ôh÷ѦC’Oo(¤¹(¤gažÉ¥™¶ÇQ¶¢En )j›ªç8ÙêùôG  )GH›ãß/ÍÂoõ¹Éœ–ï„-|o¿ð éôÀè9.¤(®x_Çœb¬±oÒ< ¤Ï›L =7™{su;T -)cú–§#n )¡ú„4ªoâÌÈN…«ÝNâǶÚLRHRß(Â>S“tl€®†¸ÃܹŠ5nýØ•J3K¢ŽÄU¦ `rl¸¢+¤_ëÿ§¢NB4D¯Œ[RHóŒF÷Å hio‰ZèâÞ^¬ÆYÒýÍþµÖì#Á}q°™âϰâîÔÎ-Û*\¯ö©ÒzÝ.q‡~{mt…4º/~…[pŠÚëã†d›Ä<-ˆ6[ÙYz¾R5mìÄ#h‰jJ\¦ìb3Ò,\ ò*º¬lŽ•­ƒ)¤YR4@tCûÔöŠ‘Á‡in;žh¦s(¥îÏÅ5è¤n•J&¼Ü#ÑFݸ\†:YXwç25‚BJ(¤V!0 ¦¤6šìã™Rwx¦‚šð™É<{}Ršû©É^e=Ò#qÈwe¥o i. i„g¼íˆÕ‰óž¯—3/¤îðL5ám“y#¶òܧÍwX…4RÏûHj‚Ý£kd ýÒ£MæQKÙGúφœGk!ÝG!ͪ¢îŒy_ôOö–&˜«[ ?g-klÚ8›NïQÖ|ÞâÖTøùÇ”lð »Ä7×3Õý®·&|3Þ´)&Ž0Óߨo©z¸‚ÑYØFΣš(¤„B"¤/ÊjšÐ-ÞÖš9'¤êV4šù<ø'T1!"óC„ôþ,þÒÜÒñ²Jæùž˜69&¤hb2?+S”’®Ö™åޮà Já¤ô1n})…4sBú„Û.ø/ŒS÷q{ÝQ> t¢’Œ–S§«NO£lv¼ôaWºó«î¶àŒ¼¬íg%Èó==† Ò~îû|Óð'–©)ÞÈÂ6zľ4 )¡šÌ$ki#““óBвæœóyÈš;uV7Y„4Ë-ê)¤¹(¤ßYK7ûÐ'9/¤Ê™Öé̯!k~x°^ÞÒ—Sò•3M0EݦÉBú°Éìû9­4òÔAA!ÍšF˜œßåÑ× jë•y!ÍZR”t£%hï:”Rë¶Ûèf;«F›LEËhr8 Á‹)¡fSúþ ))@BÒÜ4ÕY” Bz¦É<²feL3¯ç,BÚ•Bš…ôéò¥ÑÚï\RçÆì…ÐuVËŒµém)ø…ʸÕMóSÞŸ¶WHÕûB¼…Yø s0Ûc§p>º)Ú…˜s7[•Œ+ôç æcoSžÎ¦äSϰ@E4â.ŒW:ñ7~Qcð>‰ŽèôÍÍ¥¿^ÁJ>ÙÚ®RH³QHõ©9¶.]L]äV'œ-Ç…Ô©›çß³ÑÜtÀ½8¦ñz=Ï([Žej Ýf2³R¼å)¤¤àiˆÂá·hóš\қഠBôIß!=…Bš…4DáÜ&ir\HÓÃÀÃöÅhWiS-BÚ)Ï’§˜ìâÔ<þRµ9—ùJÿÄY–Iè?3­³«ß)Ǽ‡ 9úÆÌ³±»¼¼‡j¥h2CT¾™•©+–)¤9,¤‹-Mÿ_†å‚”SBŠŠæ¾q…í®Î­Ã?ß*¤ÓS´}ê¹½ÅJ!%Ò!=-¤Ü¹”Íq!í—à&›`ÒÒ|,¤…”;-š«ç¸vOð+̲éqYüuJ`¼É.6ÀL¡¢½yl˾`?þ9+¤êB»+d¬þ×Å¥ )Îñ}›©ÒÒ›­:}l¿ W„ôV“¹Ìº¤²FWGY…´UJ¶ÎîMÒ³)ßòRRp„4¤ŸN¼e2•r\HIp“M²é!Ò|,¤!/1ÀP“©—ãBÚ'Á¯0Û"¤ ²ôÛÃ{&ûoú÷Î!¦[ýíÅh\§NBç¨ÃÒy ÀîèðL éÍè­púi»_ò✂¾ð s—ˆËÝ–‡óÐâ8œ§æ±Ä [í{ø{ [{mÃ8OãjtS'“W(¤9*¤Ûì=ÉšHUñ¾Ú,Ç„4Úl$-øZ5Sþ­þó»EHOɶ)åöc8'Ø×…”PHÝ«Aû “–HPH_H™:kÖÓÒ¹—ZÒ&¤aÍGœþ>MPHL™:kvoûbÔ )Šâ “ý ³é×÷ä·¢CÌή –ȼúî/ƒAMLÆÔ„ºæçvÜî}ïÊ»Ç[…4œ›…ÍB!ÍšþRþ?“9+§…EL»š_B×=:Ï!œ‚-S³¯å …”(!½!¤|©þ³Å3¤£Ét´Ž?>eBz­9Y[BZ„4äøÎdÜx´°_­| Ö².¤ç§7MLøÛdYHÕô“]Ÿì›Æ2'¤­—ÐY¶Ã …Ô9t·”M5™FV!݉£²´Y(¤YÒ6»îcó®9.¤Î~¶ÝÒ™~”öiŽËòv)ŽLöŸìhyC!%JH©¡ÙxyšÉ\oâ” ©Sûõ…´ éK!åÑ—Ù.ñ iï¦_¥LHë˜Ìç9'¤JG_2Ùh–¿¾›ûÔRØÁöÅsJHQ×<®Ÿh]R=srjÒ,¾€BšE!}1¤ü“¹5Ç…´zÂ_¢r@Hûeq«sÛ¾­Éž–7RR „t­wCt žÝ5Œ}Ѩ)+çö1œu!-nºœZøk­)¤BHWÚ^-“Mæ-ϰj&óœµJb]ª„T•,7Ï´ît)ËBúœÉlF‹lýõÝ\[«Ï7íî*æ‚Þ|¼Sþþó³EH÷à0 i® és!åλúä¸:ÃwY:Óå €Þ¥mR£sàQ…”!œc)m2­cšo¥…^йÙT¼©éµd”RH „ ®¶”N2™KcÎôÑ›–,ã÷v³©Ò'M浜Rwy[SÞ›±î´÷·ïž­sAH£! {½o.Ž)¡ÿìK¿q…tq–7 …4kBúQHyà!WHOJ™:ûÆø$¾M „T¤^Ì‘GRR„tV ¯`§åÜ¢Øk•é›Tü½W£Žé[8UBZߨï>œºþPH  .ð¿ú'›}aMlí©twšoü*nƒ°Ôéaî¾}N‚ûb„ÔÝ·ãôlÿõƒ<¤ø“é™ BºÞT˜w á=ÿáîJÇ4 i. éO!å#ü.ЏÄÿœØÔ´ï 8Qmd™OU3EÜÖÂjïŽQ˜¤öüx]íû=Ñ<¢{d%µZf°F82ÕBº%¤x€ÉxƒP~Ô¬o¼Q'…Ô é<û…4iôÑü ˜:Œ­&û„ošòæ-ò‘Ö:éo½8O· Û’R!=ËLö?\ç‹Õ­ˆËÕAæmë™y!ÅnvΡ5…”PH É‹BJrå×wsM­Å3‚̰ Öñ¦PHÈÂó(¤ùNH‡šzôÕê{À«îSsƒ÷hîãÁZ|†ê_4Bv#º¦RHu›è5že}©–uÇ+j­œd©Ò×iqJ·<…”PH …”BZ „ÔÖ„æ6-s»ù®¿´y×pj„´­Éüÿñ …4 éc¨ïÖFz™Ÿþ$ÏTŬ! kqzê:Æ÷<º˜÷+´ RH …”PHIî éžHÃ[_á8“¹%f¨ÓÔ6ð¾a÷ÝJ©z—ýO&3Bšß„Tå+á ìònUšZ:dºb¸ÛÓup¤éG‘žßR/¤ºÇÀ‹ðC >q¦¡'ªÅŒI!¥RH)¤„BJrXHKq´§ ¨ÛÁÅ?±ñd¨a¢•7xó«ËümýHže–´ƒm {ѣБBšw„4ð-+¡ îÂãê_§ŒšS£4Úàv%­}Õ4U²}ÍRkÔ ª=ófœæ¶.<òÕL!%RB!¥!ýAËäzôF3”@5\ˆÉî8ç&r°îÄý8Uq"nÄL=dR*…Ô«Ñâ;ÐÕ•ø–Vëwá=¼sª„=1×e¾¸Ã3l.ާ )¡RH …”d—ŽR:–þðÒ;Æ}–‰‹y-[:3Q'µBª{¦Û3`Ÿo„Ô éÃŽr…”PH …”BJ(¤$Û„T¿‡u‡¯t[lëј‡åŽ@éT¾ËÞãÔÐ0”õx GPH)¤„BJ(¤„BJ!ÍßB:P£»VB- Âoº¿ÈX $²fœ K£fè®Îwa ^޼Ï@÷æ_+ߨgšá– (eJâ^úÔôC0G hšÖÐyøϨaÅ}c•1óÊÔ›qp†™:œZRB!%R )¡’”ÿúÖe’˜Aéà[²qu‹…Åiç» O!%RB!%„BJÂ…”PH …”BJ!¥ )¡RH)¤„BJ(¤„BJ!¥ )¡ )…”PH …”BJ!%RB!%R )…”PH …”PH)¤„BJ(¤R )¡ )¡RH)¤„BJ(¤„BJ!%RB!¥RH …”PH …”BJ!%RB!%R )¡ )…”BJ(¤„BJ(¤R )¡ )¡RH …”PH)¤RB!%RB!¥RH …”PH …”BJ(¤„BJ!¥RH)¤„BJ(¤R )¡ )¡RH …”PH)¤„BJ!%RB!¥RH …”PH …”BJ(¤„BJ!%R )¡ )·,…”PH …”PH)¤„BJ(¤RB!¥ )¡ )¡ )¡RH …”PH)¤„BJ!%RB!%RB!%RB!%„BJ(¤RB!¥ )¡ )¡ )¡B!%R )¡RH …”PH …”BJ!%RB!%„BJ¸ (¤„BJ!%RB!%R )…”PH …”PH)¤RB!%R )¡ )¡RH)¤„BJ(¤„BJ!¥ )¡RH …”PH …”BJ!%RB!%R )…”PH …”BJ!¥ )¡RH)¤„BJ(¤„BJ!¥ )¡ )…”PH …”BJ!%RB!%R )…”PH …”PH)¤„BJ(¤R )¡ )¡RH)¤„BJ(¤„BJ!%RB!¥RH …”PH …”BJ!%RB!%R )¡ )…”BJ(¤„BJ(¤R )¡’<#¤E¥mJ8“BJ(¤„BJ(¤R )ÉŒ¦ )…4õPHIBšb(¤RB!%RB!¥ )¡RHIáØŸšax¬Փü”À˜õ¹e)¤RB!%Ò/¤URÔæ¯!…”$¾ïÍÕã– ì)q~ÂLדü˜À˜u¸eIè>×!á=î=Á¶Æ<Û•BJ2ÒC!%R’;{Õhýg ·É¡=îF^õ(¤„BJ(¤„PH …”PH À†¿UÀã=3Éø ÇÆíJ(¤„BJ(¤„BJRÿÕ4™c¹-…”PH ¡RH …”PH ¡ )¡RH ¡ )¡RH)¤„BJ(¤„PH …”PH)¤„PH …”PH …”PH …” )¡ )…” )¡ )¡ )¡ )…”PH …”BJ…”PH …”PH …”PH …” )¡RH ¡ )¡ )¡ )¡B!%R )!RB!%RB!%RB!%RB(¤„BJ!%„BJ(¤„BJ(¤„BJ(¤„BJ…”PH)¤„PH …”PH …”PH …”PH ¡ )…”PH)¤„BJ(¤„BJ(¤„BJ(¤„PH …”BJ(¤„PHS´ŠhŠ*ŠiŠ+JhJ*JiJ+ÊhÊ*ÊiÊköSTÐTTì¯9@q ¦’â MeÅÁšCU4‡*ÓTUTÓ®¨®©¡¨©©¥¨­9Bq¤¦Žâ(M]M=ÅÑšúŠš†Šc45Ç*šhš*ŽÓ4S4ׯ8AÓBq¢æ$EKM+ÅÉšS§jNÓœ®8Cs¦¢µ¦â,M[E;ÍÙŠçôfߊ8GÓQq®æ<Åùš 4j.R\¬¹DQŸÇ.…”!ÍÒix?ž†“> ·×tÈÂix &+ãR\¦é¢¸\s…âJMWÅUšnŠ«5×(®Õ\§¸^Ó]qƒæFEÍMŠ›5=·hnUôÒôVôÑܦ¸]s‡æNÅ]š»÷hú*úiú+îÕܧ¸_3@1Pó€âAÍCŠAš‡hU<¦y\1Xó„âš!š'OižV<£yV1TóœâyÍ0Å šáŠ5/)FhF*^ÖŒR¼¢­xUóšâuÍÅš75o)ÆjÞV¼£yWñžæ}ÅšqŠ5)Æk&(&j&)&k>VLÑ|¢˜ªùT1Mó™bºf†f¦âsÍŠ/5_)¾Ö|£øV3K1[óâ{ÍŠ9еút°?)æ*~ÖÌSÌ×ü¢øUó›bæwÅš…šEŠÅš%Š?5KiþV,Ó,Wü£Y¡X©Y¥X­ùW±FóŸb­fb½fƒb£f“b³f‹f«b›f»b‡f§b—f·bf¯bŸ&MÁKT¾¦øSÜÒ¼É+ûNË28 ¯0®Ê–ÓðVž† !„JÞä& æM~ 7!y›"Š¢šbŠâšš’ŠRšÒŠ2š²Šršòšý4ûkP¨©¤8HSYs°âMÅ¡šÃU5Õ‡kªkj(jjj)jkŽP©©£9JQWSOq´¦¾¢¦¡âM#Mcűš&ЦšãÍ4ÍÇkNдPœ¨9IÑRÓJq²æÍ©ŠÓ4§+ÎМ©h­i£8KÓVÓNq¶¦½¢ƒæEG͹Šó4çk.PtÒtV\¨¹Hq±æÍ¥ŠË4]—k®P\©éª¸JÓMsµâ͵Šë4×+ºknPܨ项Iq³¦§âÍ­Š^šÞš>ŠÛ4·+îÐÜ©¸Ks·âM_M?EͽŠû4÷+h*Ð<¨yH1Hó°âÍ£ŠÇ4k+žÐüO1Dó¤â)ÍÓŠg4Ïj†*žÓ<¯¦yA1\ó¢â%ÍÍHÅËšQŠW4£¯j^Ó¼®£yCñ¦æ-ÅXÍÛŠw4ïjÞS¼¯ù@1Nó¡â#ÍxÅÍDÍ$ÅdÍÇŠ)šOS5Ÿj¦)>ÓLWË»C퇂Çx~ÇôBcžî(¤…[Hça½p?uJ÷ŸrÏ6emÌI7ÑSn×N¹×»§Ü8åÞšÔ)÷î¤N¹óÕ)÷õ¤N¹ï%uÊ”À)wZÌ)wºÌÐÌT|®ùBñ¥æ+Åךo4ß*fif+¾Ó|¯øA3Gñ£æ'Í\ÅÏšyŠùš_¿j~S,Ðü®ùC±P³H±X³Dñ§f©â/ÍßšeŠåš+4+«4«ÿjÖhþS¬Õ¬S¬×lPlÔlRlÖlÑlUlÓlWìÐìTìÒìVìÑìÕìS¤i˜˜˜ BÚ Î’ª+hõàUöO¼ƒ;q:*ÐE(¤$ºÙK¡®Ç ˜…íþÂêJIPвÜZë̽÷?<Ë011111YÓu3>D.•#¥ˆÿ’û>D´E%Ú…”„ÿÅPW`¦cƒ¿°²œ%wËXùƒu7LLLLLL´C¾•¡ÒUêKQÿåu &ãtDš…”$ûcÔB'<„‰n“Kyi%·È(™+»yþabbbb*Ôi·Ì‘å:9VŠû/¤0 ¢3ªÓ)(¤$?Ë!h‡¾x‹ýñì%¥©\+ÏË7²•ç$&&&&¦B“öÊ|%7ÉñRÊÑÜ‚/0—áH¡APHIöü@p záÕ` TQ©'—É2MÖñ<ÅÄÄÄÄT Sšü!c¤·´’²þ äÌÂP\…ú(J[ ’œû©J£º'ÅÄÄÄÄÄ”ŸÓRyGî’3¤¢ÿb¸sð"®Ç±(N3 ’Ü„jÀ@(&&&&¦‚–Vȇr¯´•@@ü^ÌÇ+¸Ç£-€BJòr ÔÊ`Ÿ¦ „bbbbbÊûiL–¥£ìÀþ¼>h…r¼ÞSHIþ „z—PLLLLLy?miò˜t–Á ÚRu-»g "¯íR’ß¡z‡Bua S.¥­ò… ‘ˤN°ûø÷¡âuœBJ ^ TsB11111ånÚ!³d¨\% ‚Øÿ‡ñ ÎÅa¼fSHIa „šµPLLLLL)K»åGyQ®—&Áì7â3<† Q“×gB!-ìP“¡^a S&Ó^ùE]Gn¶u`¿_âItÁQìÀžPH‰?ª¡˜˜˜˜˜²–"Ø¿!}¤•âwb6žC74D1^w …”Äß*âTB11111%—–Ê»r·­û=ø /¡;𢝱„BJ25;UÃBýÃs0S!N+ä#¹OÚI ~~ÅhôD ”æõ”PH ¡˜ LZ% |,’Õ²ƒ†‰)ÇÓò±<(çJ > ‹ð&nÃÉ(Ïk'¡’ì„Ä@(¦œO×ZwÉ"RG.—‰²—ˆ‰)›ÓFùL“ Åÿ7ÞÃ=8ûó*I(¤$ç¡Î„ºNBmãœ)›…Ô¡žÚߘ˜˜RŸ¶Ê—ò¤t±u`¿ ãq¿ºTæ‘PHI^ „š „:šPL9"¤‘¡O¸‘˜˜R”vÊ,yNºÙ:°_‹)„óP•W?B!%y;j8¡˜²OHGÊ*ÃR™)/Kw« k¹™˜˜²"Ø¿$ÝÕQˆß„錋P‹W:B!%ù/êÉð@¨·Å”)!}Ï7 }Z©ni%¿¿©3ñ-8exe"RRØ¡n` SV„t8OMx~š)ÆÉb9&°c* õXëå4K<ÿ¤y>/öÞÀO¶„[¡KW¹g%¶BÊ©åÝ!XæÕX–Ê3&ÿ[`®»å†`T´æ&öÕZÓ2E’ó‚Ø c,nÇ©ØW!B!%$u%¡˜’Ò߬µ‹‰ ékR×äjÊIž÷pWWB¸SŽ5Ÿ‘–RÅ-+/Ë-s¼ÓS/Z]Z+•=ÀR7 ¤ŽŽuÇ©(GIÄ6Éní–SÊÜIÍ#WÒH Ò-r¦;M 5~i.éÕ^yô’éÀþq¹ÈÖý2¼¾hƒxÅ!RBÙ!k›@¨U „bÊXHG˜²ƒ“˜Ÿ#¤Uuç£"Ÿ¸»Ÿ•Þúo·%è|¥ŒÑ²nù wøßéiÉ:Kê™á­BºŸVÐÔm$&Ÿš×¡>â6byÄmY½Izéa†i3ü@yI m4í•7ÜÖ‚ƒ¹àéÀþ)¹\í£€øÕ˜€考ym!RB²õž-ê8BQHUZã¶‹»+BÑ·ïcJæ› úþêo1S¶N*è²r¾ºØÅ¦Ãœýä'ßr¶ËYf9“,B¡‰, ¬Ýj3¿R2ÁW2Òs Ä é[îC}XÖr©¦KJ«mÅT°ÒN™­Î‚ݤ¡â×á<Œ P×B!%$ÕP}E!MS¤))›DL»WHŸ ”µsËzÊn5%Sc†ö0C_¶,éO#—­­BZT~±LãÔÞh)ë"¤ ÂηLã4 x€;RH{ÔÏé.MƒØoÆ Ø}àuÜŠ“Ø=¡’¡N3P{íPŸ1*Ÿ ilPÓ§ÒÔUÒØ¸÷!Ö]bM@HÇÅÒGÒŽ îBz¨u N¼ü˜mñˆEH+&¸ŸqWʃi­L‘Aê6«jð[‚·q‡:±{B!%¤€BÏ@¨‚'¤‘NÀ›Z[ƒæ¤:;UÌ€GBz¸u ï™ù½²-îµiiSãšÑ:|Î])ϤM2]ËE1/›5,Ç8ôÃY8çnB!%¤ BÍd TARQ7åLÙמ¡å ›²EH/5Ö%ñ}â égf~ýC¦íjÒÃÌÍS^OÛä+yZ.wß¼åá_LÂ@œƒ*Ž`?ëî‘^!}Ó ëÊ]%×Ó>õ˼*·È‰ˆßŽoñ,ºâhå¹– )!‰eL Ôwö@¨•g„t—TOò±y*…4=¬éÞ é83¿‚½ËtðäÒ­r:•;K.¥Åò–ÜnëÀ~7æ¨óÈuhŒâ<¯B!%$óHqõ”=ª-¡r]HE^4åüR+¤_˜¡%¬o^Фm1!W é^i`æxRÍôôŸœìÙ÷bßeÿ°ZÓÚÙ~$­_¸#¥<-“÷¥¯´–ü§½˜‡Q¸ ÍQŠçPB(¤„¤öP‰B]È@¨¼'¤»ÝšÃ÷sAHEns÷ƒËe¥¯Eè·r»T’K’RQÓu›! —9²Y yVjë!­­BºKZšá¥e ïM;e‚\!%å9îH)J«Ô íå`ÿ)" ¿c z£%Êò|I…”œ8lÍ(ê[B嘊¼dÆh”Piª…t¯trÿÒ@:K¹WzJ·æ,9!ukcofx§‘‡å|÷Ý[þÄ;¸ §£ÏŒ„PH É­hõ ¡rOHwKÍ ÇÉ>!Ô„> ñš^™¤FÖñ0߼h‹J< B!%$/JÑ@¨Ã¡øF¨ìRQº•xiê…4ÖÒ-Ò"µ¤|!û’Òˆ`“Ó•––TÿZ¨½ç/=ôr3ßÖi>—61n&˯ܒH‘V¿OËR/ØýLƃèˆCyÆ#„BJH^?¨"P]áΒ»U@ÓùF†+ý»S“12C–¤| '꽨zœ1¶ÈÇ2TÊ]ò”¼£Ög5–Ó.ù^Ý\£njØoÀ4<†Î¨Á³!RBòãáUG„Bµ’[Å”T8M4Z¦37E o"~–‘r£4“’þÃw+¾À\†:(Âs!RB Ê¡æBýé/*)MåZýF¨­ô¦8é¶MQÚ' ä5¹UN v`¿³0W¡;°'„BJHÁ>èâBÕ“Ëd°L“µt†BœY‡¾obï+òÆ%Ói‰Œ•;ä4ó‚V»ñ#^Äõh‚•ÿè%*í’d®ú]'Ë·²”›Ã’¶Ë7ò¬\)G_µú>ÆC8‡ñÜA…”’‡l4j¸-êp9Wȇ²Œ®ÂT€Eý.×Jãà[©6â3<Ž Q“ç B(¤„œ:x£POÚ¡*Ik¹SÞ”ßÃT Ò^™'£¤‡4—@@üV|‰§ÐG±{B(¤„Ü<Œ#PíPåä$¹YFʲ‹VÔÏRšº¥#½¤¥yG•‡˜çÑMݔ〠)!$¯ÒUp¶=ª„+W˳ò¥l¡é0åéô§¼#wÊéRÁ¿{ïÁOîhÊì ¡BòÇÁ]1,ªˆ%—Ècò‰¬¡û0å™ô|(ý¥­TòïÊûð+^EO´@iׄPH !ùõ0/m¡f¡ªIG¹_ÆÉßô!¦\Ikd²< öÂ*þÝ6 ‹ðnÃ)(Ïc˜ )!¤ ðÅL ÔŒ` Ôr¦Ü!oÈB1e{Ú ÓäQé,Õƒ;éßx÷ 5öçñJ…”RðþH Ô [ TY9Qn’òƒì¤91¥0m‘/dˆ\&GJ ~Æã~´Ge™„PH !…ó4 „ê§¡Òb‹JHcé&Ï(ØL›bÊdÚ!ßÊP¹Jê;°_‹)ê¦è|TåQH…”BœBEœj¡öÄ‘:r±<*Sä_Si·Ì‘åz96Øý&LÇ`\ŒZ<â!RBH¼SCi47PÛý…Uå¹O>à -™i¯Ì—Wäf9>Øý6|…§qê²{B…”’ìI"N Ôr†Ü.cäW%"L…7¥Éò†ô‘VRοûìÂw†kp ;°'„PH !©9aD¡&Ù¡ZHyI¾g T!JKå]¹[Ý”Tôï({0#qŽCI5„ )!$»NÑ@¨wƒPÅ¥‘\%OË粉ÆV Ó ùHî“vr§Ø‡ßðnÁ‰(Ã#„B!%„ääI$ÕÛu¤\$ÈDzš—ïÓêw|HΕ»ÀbŒÅíj/ØG!„BJÉíÓI$ª»=ê0é ÷Êûò'Í._¥ò™<&JÍàϽ  /Úàîù„ )!$/žXŠ¡ „Úà/Ü_N—Ûäuù…Py6m•/åIé"G;°_‰€8„{9!„BJÉ?'™Z&j¥¿¨Œœ 7Ê‹òì æ´SfËsÒMJ ~¦âa\€Ã¹?B(¤„ü}ºq¡¡Ž‘®ò”Ì”ôÂN{ä'yIºKS)áÿÁ6c&žÀ%¨Í}—B!%„¼O4êU[ Ôr¡<,“e]1Ó>ùUFKOi!¥ý?Îv|ƒgp%ê¡(÷TB…”RNAÑ@¨lP‡J{é/ïÉúcÊ:°_$oÊmrŠ”÷ÿ»ðƒú®E#ç^I¡B ëÉ(u…=ª¢œ&}ä5™Ï@¨L¥¿•Öß#­eÿFß‹yx=ÐŒØB(¤„{bª…Nö@¨Òr¼Ü Ãe6¡2L«d¼Ü/gKeÿæMüŽ^8 e¹¯B(¤„’Ñ)ê°@¨bÒP®”'e†l {zÒZ™"ƒä<©ܘKð6îÄi¨ÀýŠB!%„Ìœ¬œ@¨yþ@(H-é¬$l’¬,´ºI¦Ë`¹Hm‰À†[Žqè³p ÷!B…”BRuÚr¡f¡‘³¥Ÿ¼+‹%­hè6ùJž–Ë¥n°û1 àTáþB¡BHvžÀ¢PCìP§JoyUæÉž×ýw2L®–c‚دǧxPû!„BJ!92‹B=d„j&Ýå™%Ûóuöse¤Ü ÇI ~ >WR~)Žä^@¡BHÞ8­‚vaPõå "Óe}¾éÀþ7yMn‘¥ŒÿkîÀ·x]QŸØB(¤„’wOppJX TMé$ÉDY‘'Et±Œ•ÛåTÙÏÿ•vc†ã:Ëì !RBÉ_§º8PK;é+ïÈ¢\„Z&¨5i#øW/æcnÂñ(Åß’B!%„ü~Òs¡¦¡*ÈÉÒKFËÏ9µZ&Êé ‡øW5 ` z£;°'„PH !¤ ž£PƒP¥ä8¹^†É·²-›4tL•‡å©\­¥xwá Tä/D¡BHá9F¡úÚ¡Ž–ËåòYJ¡6ËLyB.‘ÚÁUXq/Úâ þ„ )!„î“b$ª—=ª†œ/Êù'I Ý._Ë3r¥Ô“@@üLƃèˆC¹å !RB!þÓci4 „ª,gÉ=ò¶,ŒµK¾—äZi$€ø ˜†ÇÐ5¸• !„BJ!‰œ(‹¡~X Ô~ÒJn•Wd®ìÖºW~–—¥‡4 v`¿_àI\†:(ÂmJ!RBÉìIÓ „Zá/*)Må$ ÄïÀ,<‡«Ð€ØB…”BR{úŒB½ „B¤ûñ®G”à–"„ )!„d÷‰45kõÇ…8¥¹U!„BJ!9B®ÿÌä– „ )!„PH !„BJ!RB!RB¡B…”B(¤„B(¤„B!%„ )!„PH !„PH !„BJ!RB¡B¡B…”B(¤„B!%„B!%„ )!„PH !„BJ!RB!RB¡B…”B(¤„B(¤„B!%„ i¾ûÞ#1“ä"OóØ#RB!…]HçòÇÏUX'O(¤„B(¤üñ)¤„PH !„BšëBÚLz‘æ )¡B¡¦ éÝ”Óé )¡B¡RH)¤„PH !Y? \Ž¿4Ggq>ô\>ÊkK!¥RH ¡BòÎIàF“iœÅùü ÿ|ŸÍkÛÃdQH)¤LRB!%„PH³AHQOkZQH)¤LRB!%„PHsCH˜Lϼ(¤¸Sâò ¦j¦dÈ!R& )!RBHþÒÖ«©ž+Û걔ͪ&…”‰BJ…”’/…4—·…”BJ!%„BJ¡æ¾ WešöR& )!RB…4«BÚ8 ñ-…”‰BJ…”B}¸AŒÄ·ø ?âC܀Љ)ŽÀ-x³ÕT¿a†R²–™Rµì–„Oð3–á'L@ÔŒsº¡¿ùðºÊ§sž;N]3äëRJ¢^ÀWøKñÞVãh¯˜™K ³n§c¾VßðWLÆm¨D!¥RH ¡B²ã`?Z)Wì Íè_HQc‘<ÍÖ-}DHŸ3™A–ù4™s2RÃR£µ-s)ktx£÷z&…t¼É ŒßÅdÖ¡´UH7ãPß ŒpÿJ!¥RH ¡BRu˜5™;-eÛ„b›ÎÌñÖ7º¥•Í÷2ÒKLæ!ëz]h2×dMHQûtfªX¦øÑd®² é–)&ë?ûP–BJ!¥B!%„¤æ0_bê+Zʪ`§EH»™Ìu!s«ÿ¬÷>´· é8“9Ø:—"F{GgQH¯1™aÖ):›ÌÛ!Ýnî‡L¦…”BJ!%„BJIÅA~€ÉL)ÿÒ"¤¯˜LÝiî3™Zñ„T çZYºnsôŸyYÒ&s†uŠbØ£3+,B:ß:…Óï@K )…”BJ…”’Šƒüx“y>¤ü ‹FcéwÆtNïåE3bó¸BZÓd¾ Ïwº|e…ôs“92dš¿Lf¿€~`¿“É´¥RH)¤„PH !©8ÈÏ2™»Cʳé²gÞ&®6Ip.Û³(¤Q}NC©i¾0™j!nÿ| )…4wÓvY"‹d …”PH !æ wB‡® )ïeÒM Îüì¸BzZ‚sÙE!¶‘]ºÞ6™£Bú…4õBú‰\¢y»ÊäMæû;\*Ýä6yB¦' ˜Kåii+é½ËË©ò˜üF!%RBH¾?ÈÏ6™ÛBÊ´éjýçߘÎémÔŒ+¤'˜ÌgÌå–, iô O{QJb.™R',«zÈ4 M¦"…”BšhrZ¸lL¡F¨(Z§š.ãNWFÆQH …”’ò*&ó~Hùg!h2Ͳ$¤eÌû“Vd³Ž1™V!Óìˆ>ÒO金BJ!Í~!½Féb”‹¤©”s7WI%Ÿþô®¤7€>KFÈ|Y#ëäWyC.bfx1y‘BJ(¤„||˜¯Œj¡µÏÍòî[å½Bz‡ÉÜ’!͸îÒ2§VµOBz³ÉÜo¢•ÉŒ÷ £RH³]Hc[Œn’ÛÅ9”=1eß¹%µeZ`~?Hc÷Ñý )¡BòíaîÔ ZšܦX!­o2«ü/MRHï1™·žKY“y$ !=Údþ ¾[I•~”k )…4§…4’Òw7oÀ×qú+;JVYç¸Yœ7ï"ÿRH …”’OóSMfŠùJJ¹}túßeï¼~T–„t7^ÿ¼„ç³^ÿy+q!õÔÄöŒï´ ÝŽý)¤ÒÜÒ49Ñ”]íz¶Ÿü:Ï r˜Û€BJ(¤„üz ;Â6 å=C+¹=t…´±iy)‰ƒ,s¬…gp}FBª†Þi2q‹_‡õ»œNSëT+fØOúÏ‘„:= ìÅ>_o²Cb†SHsAHßWÓÝ-qƒ´’šROÚÈ£²"Î6›©æ|‰´êÒPNW¹{äÓ8±ïsÕÜ:HS=vyHæ[ÇÚ¦×åùQÚ#¤«Z›ÚRGÈ:]ÒÞ|‹afÌ(ó³$¤¢Ö'ZÖÔ²Uœ‡ÏÅë»f¬R²šBJ(¤„|z í¾±þGtÅQ(Šú¸ÖDŸmR5Í•nv=îC¡¦*‰*h£>Eß°Ô'c!UÃßq³sq£RăհrjnãYÓí˜ñ¯3™xOc æ¦øBª†¿èf_A'TEœ‰‡°Ë û¥)¤¹-¤ôÐ Tnµ´öÍ¢œß‰ i)Œò òÏ'VH‹aA`õÉPHKიižü‚ôH)¤¹/¤‹•|Ùfót`kM•b¡ ¾j´”·ºü*¤ïùúþ<7[…Ô‰¬«êqÞ-qO†óu*ù›QH …”’ö¶XᔆÁ(.¤jšcñiÈì¶â=4MDHuÙø3d>Ë04ýónS‚DZ-9!Õ";À7Uô;ŽA¥À¸Ò\Ò3MMßyò°LP:Ø_wk=ÅŒÿ—hê8ÛÈ(ù\~“¹2Q†Ë-r°EHGº«ÓDQó#ãd 8w"Õd­UH/ÑkUDN—>òŠZÊ]Ò]ÖÈS §®õQýÉáû, éã¦ÌÙ‡ö‰Ó´ù‹ çû»³¸l¥ )!$îq§ÒÆ­úqøR¼J43Å ™ê<‚oðŸ®×Ü„ß0UMÙ1ö¸¯»žËõ!s)Ž‹ðè §4¬ÆŒÇ ð~Nq€Zn'ܪ$3ö‘}ܵEUôÅL¬Õõ£›1CìKPkKki=Sz…4õBá`™ä¾V gtxϘñÿg†{ßÜ#ã}ÝÄÏ“2æÑÿ31¾7Éf.[…4B%™œCAM"m]Ž&çN«h¯MçÎm:…”PH !¤Ÿ0)¤)Ò²ÀW²BJ5ô¦Kõ°† mWGi_³”lêY· iYù3Ç¢ìßu—:Òm#ý\+¡9oÆ~ýÿíUUÀ¿…`ARC!AÈ!K6Ùø—NJ Nü)aF+¥D—2ȱd†˜ [u`PBHR¤X²I ]ù[,ì²ûu÷¼ó.÷½{ÞîÛÝ÷gû;¿™½ß;ßwî½ïÂÂoî{÷„R„!mˆ>è¸.“l.¸bûõ¦gTWu—ýÙÈ7Qr éô MûtÞ».—ûwˆ£ºGg7+HjÏClõÓ) ¤)BÚ!=ê¸.Ñ1¯…ôëj=]ëUaGoJïc²…!m¥GÒ&¤³uŽeºÞx’?OŸ÷kÚ¾[’ÚóXÏ) ¤)BZo!mï¼.«ì˜•¾é¶o°î®åª0uùz6A~¼É·ÔªöNãÄønZÄÌ7}ªîIíyŒ­.BH!@HÒú éçuÙbÇ<èÛïßSÍÓ3þÿ\ÕÈv]c¦°2ÂîçDHH‡eTH«ç ØS]S·OR{Ž>5!„!EHë/¤#×e«CHUŸÓü˜5ótv^葨²¤O´4$¤÷§QHûê× ýôfO|'ëbÝë˜i5RÛ%©=G§ŒXŠB €"¤õÒQuÒê¹7oÕ¼ÐÇĬ½ôï¤Ot¯s¥¦Ì­eŸxnѼ„wƒEµ¶ÕEH!@HÒL idR¨E:ÜB=Âåú–Ÿ?nûzÇLaïâx£RÕv¶úµZ+wû÷‰O"¤€ ¤i&…4zp›>a_ª¦§žñs‘%C»×áÏ¡ñé¶úGµV敃X:R„!͆FÛÚÑÖ¾à÷}Ñ~ì}"…t©­n«e5ÖUjô¯Ì„R„!ͦª®±µ3üžèzµëS(¤³lÅÓ,¤ç´ƒ­¬ÆºèQmôB )BŠfWHOØÚïû=/Ùž­H™.²ÿH³ªÎµõ-ugš}ú9[5-¦!„!EH³ ¤Ñ‡{æ>Îîiûæ¤LH£÷aצ]HË=‘ŽŒø|hJ«H;¤×ÚŠ.šR@HR„4­BzVûêb‡ÔUè¶ö@ïËvr¨úˆs©Ñ]z—n¨“î±Cô\š…T= ½Ä޹BXO*ÒVû{l¥›ãr) ¤)BšF!­~}©Þ«Åú¦ò4­\÷ëµÐVƉâÏüÓé¢ó=ùÜç(ó~¾¢ëu¡{µ ©êM¶¦«Þííã1Ö´©êëi­zxgµÖSè=úªþRûøý­ôO¡q) ¤)Bšf!½@~Ìù×èâöP¥³ã¦Ðo·‡º éVÇ[›&!U-ñÞSM¹›¾ã…B €"¤iÒr½Þ±FS5Íõ{ŽE8«ÛŸýocÆs¥N ,šœªnÓoeLH«|^ÜôÿQ.ÓÇõ”s B )BŠ:„t›ýxû/qï}Žé]æ¼.ûí˜]1½Gt¹§\µ›¶ñößZ»ë`¦Öx…_Õ©zƒ¶52{…~Y‡êÞ™œ«:i·±Ö?±½ÞèbOD“ÿÈ~­=W¿ŸêJ¤=½3Êx;ýg‚z„R„!uizÚ™:֟׳šË­BO{ÚÜÏ^â®z!„rþ½DJ=eäX­eŠüN6È{春Ò:ë/OÊ y[>2™i)BJ«©}¢½íEîåÅ) ¤Û¿èÇÍfuŽ4ÂÍØ®66ÓNŠ¥*&ó „!¥ÕÜú; pÜ%FH!„4|œ/IE¨3*¤+C„!¥ÕÚþå/.z{h%*„R@HÃÇÙh6òkùºt‘« æé ù¦-Ù-c¤—t0™Ï"¤)-™µ©ÆéXÃj„R@Hk9ÊU6p|WU–šM™tÊéëˆ"¤ª!¤€B”6æÈm6›-9~R„!@H Q éÝ6èãÈ6›å)BJCHR„4}GyȽ¹ófSŒ"¤4„!hòB*Ÿ‘áR,[¥TvÊ‹rWô)ø¸·ÉDÎ\ “›(_õ{F›×ËìËGm>H¥Él ôÜÚoy@^’íò±¼+åq)Lðn"ãš8On‘ŲI>½ò BŠ"¤)4z!•äý¸äAå±Õlv;÷v™ æù=êqR¿Š“ÑUŽšµÒ-tô<,ñâ^RHmCHR„!€F.¤2HÊœ“³+¤r»œJPuTú'R¹IÎĤ¶#¤)B €@ãÒ7å ÷³J^û¤@†H‘챕rkƒ„ôù±ÇËöå\ó*–ÈMÛ=ƒüÑÃì7LUÞ—™2VnôõQy×ö•7_HטwSý®~ã‚ü\V ¤)B €@ãÒjÎʸ@ÿ%òŠ ?Œý.iÝ„Ôö×ë¡&¹ZŽ™ Ü“Ñü@+yʆ›BZÍ)ŸÑëˆ"¤)B Òáq™æ²Þ†S²$¤°ÁO¹å6¸Ù)¤•«BHR„!€ÜÒ·¹6Ø“ !•NR¹ *Í£:Ëi<ïÒç2~R„!@H Bú]gv³ ºdAHï·Á¸g¾Ælö;…´BŠ"¤) ¤kBêžsôAŒÎ‚®°A‚5îåI´ é±,\G„!EHRh&P8mƒ™YÒ½fsÂ1•~„gmaÏî@HR„!„rMHw&Èö³Á‚,é'I¾…þ!!]‰"¤)B )äš®KíjƒÅYÒÊ$ß .AHR„!„rMHßJ-°ÁSYÒÈSôûSéÇÒ !EHi) ¤ûBz8Av¤ ŠBBZâ¬ÿB …ôc³9)yI¿„!¥!¤€@Î i•´tf§Ø`B o£ÙrÖ;…BºÉIOá„"¤4„RÈ]!Õøëmv¥ zúV™M¥4wÔOM¡Ù`"BŠÒR@H )©ã±&én,ÚüØ\ØàºP}sÙ“B!íkƒR÷©)BJCH!€‹KHU¾Ê-±Á¬˜Þ 6X˜ðþT­eÿº žFHRB )4!=,1™'lpìÂZH¦ÿJ9k‚3&\2ý÷ÚçâS'¤ýìjöUò[iëÈ_+ e›Ð9=mmíw;(tGï:s–‚ˆNŠžþñ»K—.YVO¡«]$âiÕ»qþï7]ôƒqbrr²g…®ftÞ5ÆT“?<œs:èÁ#“¢Klåuœ5J¡«½¿¬r@|qeú€°i ¤¸%éq{Q1Íô»µHWZ-‚ý}Æê«Ó±çôå=àH°¿Hû†³ EXý­Ï-áÊ©ÔÅйŲ³(nÞË"Û°¸ÓF¡« ½É¦êº7ßlA-p©ÃW¿ô]IòØVaF5&ŽHÞG¯ê¯ ô=¹¶B´,S´ô¬ó–£¨0¼o¾Þº{vïÏ[¼–ÅÏͯÈâ -îÇ/;C¾I°³(F¿«³HGG{1•ÓÕ‡ùÍ(ûD®î‡ŠyÌŽK–m+#Fžpsóˆà=!kãqñèf¨y' jÞ¾w´Û@ßêÆËq×åÍȃ½cúß·gíIñž#às¦­bÅDqNÿÑ(1%9ÙƒÊéêC¯[¹»@«ú¡ya¿1Šò‰‘²»J,Þ~n ÂV ØimKÒà±v6, ÁþŸá©ò§_Ü<F_!Çöb½ÁGéï6zèˆàÛ'gQˆ.u©“}cS9]9ýÑço]þ}죙 šoW¶C·G·‘£7Ž£glIL7`ÊŸí%G—ü´Sà¸9dQ3†~tl›ü,Šk:< íìììʤÆéêCo`޹¼Û½²šµîƒäûÝc ~жõ*DŒ×ô/þ@ZÓ~É…#0¡¦ƒÛb¬ßfx"ïõ;€8"úþKì,¹# Ñ‹¹Z‹-\Jåtõ¡G\G*ö Ý{;º¾x^ @ÜÜ¡N±ôîÏOØb„Žl=.em5B+þ~JÞºI¯ÛÁ‘wéò§ŸÅÚƒCŸò;¶¹ð÷—q¾Þp/oã‡-ò³Ä Škz¤gZjjŠ•ÓÕ‡^â^,k³²CÿE늠±û|‹_tƽ½ÌÒ¹¹ÜÒ½.Î0°/É0œfx[ýåWŸn;,»1<Í [­ë É=(¬  Ãê°ÿ¥à~±ì,—ôÞ9R!zÀûï®X¿dÕ‘›Qèœ+ßéš%Žrè|߶ʤ‘º…>“Єò9XåÐ…‘œˆVöÁ }f¡ã …Í{üûQ_n^XC¡Ï"tÁýÈ4ަVÎÌ&t¡‰õÏ]¿§ÐÿÜè•[6Ð…!Ñ‡ŽŒIxý…>èR#=Ó^èûAÓ®§sÎï¨E¥ ú4 óœGB»¾ñ†¡ßZ÷Ï>V=£ÐMñEÝiõ¢#áeÁŒ3 4 Ý­$µ¤ûƒf:ÓÙÉW8º©úm¶ì\5ï‚TÃÐcªÓÊà¡vÄLB×ÈP„Žf'f^(kZG®ÏQ÷ô‰ù÷RèÓ€.þÞEöM(Ò´Þ{¯ïá}nm@¡OGó:Ìf³³5­÷Îf1™C©#út4ï.o¯Ý´iýjM[9öÞÚµkW-|H¡OGM8vÙÇÇÇ@Ózï¡ÎÁׂ]O·SèÓ>0 Ëì-š¶r¦Gv­u§jút KîpdS4A CëÂâñwÁ:ùèá¿\ ¿òa¡†¡_[²|ùríµÕ:ùèLã¿®X½zõšãƒ†xçöí”6”BŸ†æÓÍyné\py:E(¦Sè¤wä˜-q¼c%AtÁ(k€ñ4èmOƒMzÅEOOO¯‹ž^V:éãtoßá[?`M'„.9gêzÖn<œ¼Ï9‡Í™A’Гµ×oÜ´JkÓ&m'ÕÑ!ŸÃ¡úxðãE« 2bëÞ6<.ïIpù"ñxHÄáÎ|rÐcЏ\.ã|¿)I%t)£8ÆÓÙÁÞÒÞÑÅçNÕJ¡c)óf¾Þª4±‘º}¯âÄžèèMùÒ<¤cÊ££½·M]#òêºÃôŽêœN¦î÷†)t¨<´+g4L¯‹tµJ ¥¦§øDz—‡ªÐ{Gê=Mü+F'Ömd(ÿ‚ñÕt¶£— âÁaM@ :ô’Ò{·Ý¶sÓÜO»•Gou·Lc)¨ýôÃÀÙŽ>^C {ÉA‡º“Í$ ƒàžƒù~P{Ó0ùewz …fI(tö‰’Ð!Ç„©"ºìLy× ¯IIôv+ÏÉ6èj²ñ¡Ð­O:Ü:ËS ]è™Éûe±#U9ôbý´Éwâã]7ë ÐÉD{ùIUAç\‡Œ-Çôôô¾HS =Ç fÊŽ}†A=…N":plTA—m˜9,Ÿ8 7+ƒžc؂㨂“ TN'ú TíÈ"•"½­J —´àzѼÓ]³½÷nÐK":Ôë?V }ÀeÏ»vmR"§·ŸªÆùª)£ä°„+µ£¶ŠMˆ¤QH&:ä ¨„îºê´¹™Ùé„ÑyÖi¸go®ø’:M#u9ij2u˜ê_@ÕŒNûüCÿzâ³?T¢íyª »Ü’}eõF¿é‰¿9fV@fùŠ«øãÒãËc È Q/:bòÖ°wëv’.ñ¾,Q½ÊoÍk¤Eo6 (‘ c2x‘Y®ãZÏàx_Jñ¾y…cDÑyC¡Ý0°åÙèÀ±Q=bÑš 6¼›LqO$ää­©è¥Õà{ÕªŽ„kzhmH7Ëù:ÒÑn˜¯<ú½#—}|.›Ý#ˆ^kÆ%äÔi<¨èH‡úð^ `íÔ#œÓ;-·}»þ5{1ùèÐxªAitÞìk7“ ºG A¨Ë êG—Êó *Ïô(œþ}UŠ<ó˜ìQDÞ DÆ?kX‚è“CJ]Òûœ{®@,!òŒèÊ¢×_Ô#8Nï5!:¥^o. Ý¢OôšøØ[<è õÈC¸™Ù'£v® é¨ón@Š/<òùõ>–)‚Ý eÝVÌÕ‡fVØËЯ{a­3?ùzÄÇz€”ÚÄŠð£?Èæ`G—»$0ÉG‡k®’èÞKÖ®]»då#bè „‡ÖvÕ¤¡óÍðµÈ­–чÖ: 0&pÓÖ%yçŠA¼ñGmÃ!s³ âo–9z[GÀÛxÔcž·, ÿ²$$å½sP»·dÌoc x;ŽÔ¯ürÝéiZqøÑo®Zwð1rȻ冘|tÉåKåÐïÓ†‡Z<{¡#ö„¥â¯“†Î3Á‡ÞöLMïßp‘Ó%¸±…ÈáÏD~«úQ°ý‡ÚfBÝ¢ È\Ü$ú‡Ж>]ºmfÁq]Ôø¬-¿µl,ô=éϦq!kÌp££V#BýYÛcÀŽF>:ðÎF)‡þd£M$!t†)ñÕ¸M¶âWÞm3½úæî"0ÞÆÃ:‹6` êwôŹ»ˆ†þò¨éã'ý#·m8ýïóØßîŠYÞ «7¼9¤åñã~÷íʈÃÚ¶¦€eÒ4 Ã€Ñ}¥ÐËÜÝÝ/ØèÄB/v#.Å=ÓOº¾…=Ï Kx¹ŸëTÚ/cø¯aû¯¿õ½š€L@~ý%–“ëF¿•‡î¯#kýeM¿/ºÔ/6×ù§ôÕ9èÁÛÓOÖ+ƒ~é½¶mÛnÏ"„sK ªsed¡Ú³ð¡›OäéJÁí!ùó1sð“¡;|Ä·¾“!G¿¯EƒŒ°æx÷]†nð-ø/jîס óÝQÑ—‡û>hÐ"„¸szã^íÍ÷<¿¸ä°®~ZÐá¡a¿è5}\‡Gp¹‹Ê̪†%“…>l‡o~¯Çl"zû¡¢jCšôÂi–=£†Ú¹bHÐr,I˜w‘›ðæ5¾ï‰ˆé»»¾9&Ÿ²;þ^=ãËm=ìÓá.©»ŽÉÅÍÛ«KV¿ûA#þްi à ·ývM:9:ÚÇF•A‡ó„­5œIÑ%ò[•ùÈ”èÂ’®§EˆÚ+³."5ˆ4t[eÐÕ¥]ظ:½ˆ CÅõBàgå³j *†iÕ¬ª‚îõàËÎ'eë¿Ø%;…ú_˜Q‰‘¨(ªº¶Sñ)ÆéO^¸AÐ)†l¢#c›øÄÑ¥¾ž/ö•\ç~bÖ%~)zø^,?6íšzF޹FëësYݲb9v*A•ë£^ô©#ñ'¬ÔùQL¥~©Ð,~9|äÈáÇ¿ßýì-Q!#Læà( ¡ìÖ)öžàn3çßçïtÈèCžcÄpgWWƒÉdaçˆQÇ9sæü§ö^ŸÂaT!úSØoÔqä…qz5 ÏÊÐÁÎô_‹¿>ŸÕ=h§L¹”z“…>äÌôœÕ?»{xW9èì={nyßþ»>vI¤®½ÝÙsvØG s k+++ó·æÈã?~çÿ˜:Ä™$™››[8ØÙÙ::ÚÚÙ9XZ~øädsþ²Î¸@¬ýÉô#òü(ÄîØï·Ð=‰³¦?Ó¿oqbH­è½NÂiAGê®û¥1€$t¤‚/¾!k¹çŸÝÜ%*R€UH6V3™tÙ–íÍMÍ5ëdEû/ÑùÊ.¥›zõ©6µ²sr†±s2ºz e'û×ÿ]ù£Kr'‚h—ŒžÎg¢«r‘ì·úŸÕ‡üµÚ*sC ièÝgñMé“65 ºlê0HÖ¡ü’û,º‚œÎÿpÎÜu?ûäõ‰æôÃ’hûoó?1 -¼$§×Fayl$¼cꜾì/«ö+dbÝP±C»¥’s™¬ò1ÌÅsXÁ©"P/:Üt«l¯°XÕ<%º( ´rXJ¼÷>j™¤èÝžÜÂE'é½{®Á^¡J{êÉnÌ#æÓq‡C¥’LZ7ÇMy¥\oøJWá*Du\·ôM­ëè”èJŽÓES/z}¨+>Mhœî›£D©ß%¯„é¶¾SôåxVý vt>NŒix®ÃD"z¸3ŽD§(§‹ù|A±ÛšBˆ êTEbs¼'A 9yŽúÑQ.lztït¡g™áéQ¿ˆÞö«Ï>ûl} !ôJgâËLÙæL2ËXeÒôòG;LÃÄ èAó·ôAžwzЫNµRèç6ZÚÛÛË$„>lJ|ÌVsVJj!£÷O>zÙc§2^ù­ñŠÐ†ÙW{`䋲iAï>U Ê¡Ÿ—/p%¸Ú…ø½ñ7#È.æzƒx…ï#$Ý @Ð…—GB{ D§`:ÐÙ–8צ*XÍÃrz3Á%Й‰– ß²™ôrî³òW°óŽ(Ô¬4²/™Ç_]¹•9 è"·«ˆ²è}߸cÇŽÕ—@Ó –I©í4Üc4êåôÂ"ÛQgµìv£]ÿÍû«öWÁ3´HAGÜð.:Tмo8coï@0§D+Ô5}:JZ|Ëä¹%ýÖþ|ÐtlèØÇ×}€lô cÜݪÑ/ÜQ&§C» ±êTo™–¢F³O>3íF3Œ—‚&¡?I¿¤Þª,‹êS­ ¥÷\‡•@‡šS8ÔÑÃWq³§ @óÐ%sÒÒÒ|¦^D½Û TDGï  )à’® :ÔdMU½D¡ý0B!zÀ’%::‹uêÉCg›¾}àÅšž³'°^ÏtI¸RèÐj<ùbæy—!˜µèBÝk---­É¤¡‹ÜÕÑùÁ·ÑøOö)‡#^gh“ÌÉäÝÂìEG¼äëÊXl²ÐÑ'â zïȨ˜ÛúŠÜ»@KsŒ^²EÚìj5{> Fñ½l)©©)nÉBÏPb!ƒ¢ãsø¨Z“ˆ(‹0jÔúâ„·¨æ‚Y f7úÕ7µ–,Y´„¬œ^¥ßd ‡°§XG«”G G˜:&·OhÇQnc”µUê(ÀìFþØŒåô¤vBèB³¨œììì{Xdäçç””•––V™qŸ Ý­<­¤û‚TAà_±8ã™S\Q_V˜êjnZ/˜íèh |Ïö(!t¤01‹„²¸æqA.XœwÍrÐc+ÓÊàÁ¢HÕÐeRg~œïùóžçÜ‹ûÅ0ÛBaóže7==ã ±q:Ùñ":Ýiï‰#oîèWýé›fi(¬é&]¨¥õÁœ>ýè’–½C^Ý@:PègAîvuuv$µiz€PQí¤ÐIA—ˆèµ±XÃÐýmd䣽3]Êf2ÙBTÃÐùîKç-9ñÜukõ£êÆÆÄ:fÍ`t„žãï`jeggaéV1ªIè¡[ÜR’ÏÙs5 =rÞÒ•+W,Hš±è¢w4c”Ãaõ•Ç:›µ!š‚.t’ÏQ'×jzgT™¥3©°±Î™¸ÁÒit©WCÐy¡ò™òèb C/–MœK“Ûg&:ëŠIþ “@¬ˆÓw%Á7{už{û5 }¬ö\rMUuù±[3½ÍüªÂÞf}™£è=V,›·7[G5¬÷>êz»èQQY72Ñiú/_8$ :«îM x%ù ‹RŸ[¼ªþœÎAWjÛ œœé?7É£?O‰zÑéëßxçÿ{{Á‚¿­èÐ0ô ÛÇ­[VÿÚ1ãÐEN“Ïö¶zÑÅW i®á4ZCžCÃÐã›$úï5Wß›qèIŽS\Éë4hW+:ðPÞ Ù*Ä¢HÃГšo¿ógúÀÔ«¿o»¡jE—å˜RÂMš6N¯;üžåè£_Ì4ôЫSÂ1«S3:Ôèî7üá­} CG»iÉãrÖ Cù Ç'5'{=÷>K*–­êM5Q+FÈA‡f§o¾¹Àа!ÛËBÓѳÝq4dúìf/ea'¼ EWOþ_œÐÃ$¡Ë?È­˜B'#.<Às”÷3;[êV—yGûIø¾ïLÞÅ__Ð@zßÅzzǾ¥Qè$Ę9®º³žÙƒ¸dí¸õ]í)úõ¹‹ÈAGN­Ø««»÷Óz „h²Ãµò²ÕfÂaͶó}Œ•Ç äèÒòkåÿG¤åfMœ-Ö¿æEåIºcc¼µ`,Ã'xPEtÑayƒÔƤÐIˆ|/\‡±­&Ü2ì·0­³}ÿZ¶=1°Ýil•ª4îõÓÑ?of¢^v½?ö‡Ž_szÍÿÞ(ôʤí>*P ’’%b±¤™N¡“)¡¸[Lìã§-m¸²fCï`í¾r/`Þ Ù;ß‚GÚU«±Úî½´Óá$ ™ZMyŸ'eé/jV ñ\±k÷în§r:‰o‡LĹåyt9zWÝšÄÒâ‡òEÀK¡\»ôÎÂǯ§~æ*ËéM>»Š‹‹Š8*Öô‹ß››™™h ÐIˆ[I¸CÝh/A¯^T‚=<†üžòZ@ù‚ŒMÖrtO0p‰ªèý²ô‚TSC62"_¹Ií&ÎÕ¦ê´È?qCïd¬ÔçBmú;zI§Ž@Âú¡‡ }ACêkQ¨øn­ŠèHWyIɽF „ȸŠë0¡ù„Õr|Ï×R„ã…Õ‚K¯g#~¯ÿ㸅¼yÏÃW37 ùì>Íì.<\¾/Ôðo—ûÞܧç'T=îíÅËæ¯¢r:QâŠë0¦Õ„E+™±FúÒâ*éY±…ñ}Ï[ò±¾´<6gðaÌC!R•Ò„Zw%¬<›CQžYD6P¸Ö='9´>ÕOD¡“=gpÝù^kÿêVC+üˆ®X.?vX`@Õt2Bh…k?ÜØP+:„íLÍýù°V)…NFÅãéÇÙ—«P"õÿÔ™G¡“5Ö8ö¦%0Ô‹.ѽ ûÆãSè¤Ä}û)»ö˜ ¨’üÛ»»»ã›)trºrÖSmGŒzEšÑѳ¯-Y±b™ui•¤¨;=Å%õu£C¢MB\\œ=5#GVD9N:wÒn@µ£sä{E²8:I!ºpe’•Æ ~tÅïÖW^JñrnÜË6æìÿÒ¹ñ>³(”Bî§lmpÅÁ˜ênç_²Uja«ގBsÑG{qC2ÐAú[±\^´Á£Wþ»h.úŸ-Ð2s¦çØ–î} ôˆ&ž ¤ÐU ÎmS—ÜÁßÝE] gÊÔñ!ŒsRN[ã‹S(6•ÙóÜÌo$çW–æ&\±=s­^=ûÒÎAD!žˆP „F~¨,ÊËÕÕÕÍ7¥«®_âÿ‰e‹÷¡Á%tEXtdate:create2013-10-08T11:38:56+02:00à»U%tEXtdate:modify2013-10-08T11:38:56+02:00o½éIEND®B`‚stxxl-1.4.1/doc/images/overlapping_runformation.pdf000644 001411 000144 00000027545 12350112610 022337 0ustar00tbusers000000 000000 %PDF-1.4 %Çì¢ 5 0 obj <> stream xœÕ]IGr¾¿_ñnCÐo*÷Ìã `6 =&àƒáØZZV—hQèï;cªj zlj @™Ë÷"²2#+þ|]náºÀü÷ýzùÓ_Ûõû_.X{ýøýåçK šÿ¼_¯y7¹úu\ß}w!¹ùÿ[¹†qK×wëåÍ/>~zûî§’Poñúî›Ë›ßþí¨Ja2¾û·Ë›_?þðéÛ·¸üÓ»ËãdüþÒ{¾æe¹®H¥^o-×koéË,·I×ÿ¾¿ôLO±6Uãž4è˜@’î3ꀿ“ƒ5c-ØnÆñþòÝ/ãú+»â¯ÿ|B„O—ÿ˜á ƒa!)–²ôÉŽÆ(Œkô ¸F Ê 4éDŠ- 7 „K4C‰¬3»BA߸ ]ŒÀPvRÏàȰÀ*¢ý0e* €¼ã_°Ô&®œLÆ\.rLª€ù4®Â^3騄–C%TfÆAN<(ls1gF”Š) ªF—*¨Â¨Æ B”bv:+ÆÊiИ ¹êt èõìEPEhvÔÖr•°¥æsѰæ,aÍIÚ£ ++PjžÙè ¬¬+« +ã ž:°.V ÔPÖÒ%¬¥jXKqa…¡J 1;„ŠC%TfÆA®:(t¸–P¤´µ%¬@©ù4¬}‘°¶¡amÝ… „(5Ïì tVVŒ•Ñ…•qO 8ª„(54Іud ëˆÖ\X¡@¨€RCÌ@¡bÅXY]X¹êt ÐUàZF•­­Qß§°ŽùÜWÂèŽý úÒ¯b5t¸ÊOý°vÄ:³ŽðØŸFßSuÎŽ?»lîÄèÐ¥©i¤šKU¦ŠdL!å ðR Ž?IÎD¤˜cÄz«§êæ²&ÁCÎ;/:JIâ¢ðŠeui‘¬)y!Ò¢YAZ$+H‹fq¸¬ Øò‘’BØçk:ðíÅXé²Á¾<(pa ûd¤U¨Ò'§9R1ª¥ON!Çœ¸OFJ 1;„Šce޹Ù@q>P誅r9@… ÛZ¬ÃŠ”Fª6 k¬•ÃkÖ°ÖäÂZ“„(1/ì8T«(ÆÊžs·¶v>PàÀ؇)5Ô klƒÃ[—°ÆÖ,¬X T@©!f ƒP±b¨ƒÊÌ8ÈU§…®ª•ÛJJ[[†tÇHJ÷—®Ýq\štÇsdÒî8.%8þ"Ý1‘ܽŠô»¨‡ºcQOÕc8þá‹£C—Æ$‰’jn–^ ’Ä9È+¼Ð-ÀÃCṞâ= ÕSu²D@ñó΋‡þfxaÑ‘6æ¢1Raäl1ÏIc>ç‡ ;s(0l$ >ÐðY=UsÁCN=/:tjs Õ\éóÒ4æsƨðJv1‡ÃCṞâ= ÕSõp1<ä¼ó¢Cç¡¿^s-²j:ÛÛ˜ì-²¶³wq="^ü+dlA‡Û>Øâ_…»ñåÙ@aBÓåíJì¤ÖUªòn¤bŸjgÒnÜYÞ ¤ØŽpƒ}ÐA¨D3Öv{-å~§®*QX¢° `Y$€¹[s³M‘bYáû ƒP‰f¬@Á®:(iU0/£âìØ§Ôµà_ ÖÎP)7ÐQHSi Nã§øk¤Éý0nÆ¡­ê\ ÀU- *¤ØNK†ªEAÕ‚¡j‹¡B5"Åv„ìƒB%š±6*Á®:(qUVPœw+(Æ´Ä(„uá ɹ…·žêáTB ¬Ù¦ §Á‚ëZ2Óën¢'«ˆ–'ƒ_ ´ ©¬2Ëën’'¼•–&«_™¬´©¬nÚy,O²´G ™Hn›ºŒ6dmlÒ†¬¤)•ÆL$·O€v‹z¨9‹z¬F»Â/xlïœèžd­Šá!)æd]hÈÂÐØ¬ YRþ¨ðs,€8@ÃcõT]<Ác‹VçD÷$‹/ I1' CV:Æf©cÈZ‡ðc!êb‡šcÄz«§êèà […9'º'ùžFðˆdsúíjÈÇ«±ùz5äó•òC!ê÷+1'´\<õši&·Ž™3ró@Ê\NûŸ4:(qh!þÕæýRä— ûzP¶7Uwíä5â÷ùa:úáŒÎDwä[çOÅùÃY ,ø¤s&ˆx‹3$›™úD¶&îzÁǽÀú’ÊÁ~ôËëTÜ囜v¾ÉÕù&Gg'çŠ4^ô®ðǽÄú‚ÎŽÜèð:÷9'Ê ”«¼@@¹f‰›º¬Ñ‚Äñrc ocxöõ¨lg*î_ Wˆßå‡ÖÅ=Š€rÚqÑÙ‰£$XuƒûÁ³¯e;S[Ýà‡Wˆßã‡ÞúÄ wÏ©g¤Rêܺp‹ùlq\šüöŒÐe½Xö9³ ®ðßÇï7Bë^áÑX\^4ßx¾Š¢»ÚILâ ñ’¦¶p)”98Ivã®¶sˆÝäÙ×£²LéÛ™Â-g_Mø Î#Ð\§NqÑÎ2užV«ÿ½¾óÍHïàœbvz‘²¦L[´Ý£Äá¤ÜÔ¡” ä_8ë±\jwF7V4û}’{ÚÜt+ûeÎÙ/H™:Ú¤¶Qâà ÖªÛ‡xàÙ×£²©eç׈ßã‡9þµÍ@>çƒCò9:&Î,¹lFÌs{i × «5_rÊNb}IçÆâÎî£xŠ»œSóÎ9µ9çÔäìÔ²CQ——#kÙ»úq/±¾¤scq9:ç•*À9÷‹m¾¸ÞÞwëå¿Þ„·srµ¼‰oÿûÝ¿^f*ñ:‹pR,ÆÉ·©y€lƒÊ9š>ÇrHS"U¬Hxج*C:¨Äš¬b™¼“É«a‘‚žj¦ß¨Û˜ûqgîǃ9¬y°)ÍͪÛívPÎuÎ\3±õþÃúÿô-z)ÜžÀûŸÿé›ëÛ?̧ûŽâ-×9-$3ÿò§Þ#×ÓµéˆZøS°žT¨r~Á‚ êR?šƒ¨?‹ÀscsbðIåÒ)ɬ§øí T Ý¥rRt༜òl¬€©$[HºGؾ°àß÷—4è)”R¯ÊtÁ5| *î–IG\ªjÁ"s ôÙù@áj¹¾ͽuȪ)4hyj•õUlþÆ­o@u/€rL:°ý«æYj®ù7ÿ~ž”´*nôDÞ•æ=¸Ÿ½D‹. :I¥ ɬ§ø8R}²F¯x´y´3jýH¥mH¡C8¨õK°¶XëGºHH¥=+w£RëWÍÂ(Ü‚CÛÙ¹@É> Ž$‘ÝÃýIÝ!õÉ"It’]ã/™õ¿S‚ê»ERñèΓ¢£µUñ1Mö¦ ­~-4Ð(°𡸅&'#Ó™uˆAŠÒŒàƒšHõ^üG€©î¤W„Ȥá!ïGFI/‰X¤êPׄÈ*ádÆéEQõñuïï‰Ñáç˜&èbc£Z÷2Š`ɺ— Ò¨)–Uî©u .Õ µÍ` Ê}OJú<ñ‘*¢ÉF|ÚÑ廬M6â#ݤC‘1\¹ï ›maƒzåî. 9(}%yâ‚dÙ6zœ¡,Y'.‹âåvnüA'.Höm£Ç ê¾ÑSuuÁc¯ä9ÑÑ!Z͉” Eë1’æá!ë1¢KÄ©@=Ft©¸ @×4WõXí’qÅÇhO‹NïõàØ")±Ò«.ª\€áHc5ì.Q~,Pl‘”X‰ÀàÒ[Q?øL´ñgßò΋N'#>Ñ<|Ë $8°ã$<âË-$ø€ös²’bÔá[EJ]Å£äÙøŠË¼xr˜´í4N¾Ñ‡tæàpÂ÷QÁ™ºÃ‰ÏE„ „“h6ª"¸us1œjD‡SqñþÓóÃTwrσ¤ô$¬»ÐÌ=[¤©¹ž E!ZOÒ­çA=ͤêèzÁc^<':u÷§E‡Îkš¢)±jÍbÛ4I -[l›ËR©@±m.OUbÓDUÕcµËTÏ{ÞqVtzÃ>U.U–ª—ªŸªKU‡OU‡KU‡¥ªÃ¥ªÃ§ªc“HŸóvK‹_Ð/ ø­ß-èç |°YðË~À>ŠdÑE jø•ÅÅ7 œ¦^9 8£û~+Çî N nMe8œÁ}Áåá ”,ºŠ?ÛÏÜW\Ãewœ¦º“ºm"¹ Ð?£fê¶Å"V7‘¢BQˆÒ «@c„Ôm«úÆø”?úiÞ‰ÑÑyù`A&Z–÷ä4,È3° §ê‚L 2Ñ1¤‹‚¬Fà™gÅÅW6œ¦ÞÝ ‹¥Ãáäû ìæœ|ß?È''ѲZNÒUü% ü`8œŠË.q89Lu'¿<Åed¢ß’¢™X¤j—‘Q¡(D}Šed¤§y€Tí22Åc^<':rÞbðÜ’WÌÃàe]óй¼ì½¨@ð²[öRÀ‘uÝKÕ#¼ÅÃ[6Î;-:º”JWˆsÝVb×Õ†Ømµ!v·Ú@‚×Ýjƒ Ž®« ª«ÝjƒâáÛ©N‹NGé,‘e ¯f†Gé¬X«‚Èø5%²oáAÞJz†‡GÕ.U<æ¼s¢ÓÚH6g÷U¹ÎχDxÃ}Ú¤BÓãbN†\ 86÷>iüþÃë‰ÑaËkMc‹¤˜kÕbۊƶ%ƒ×¢ƒ‡‚‡¤ÄJ ˆ¨‡b+걺¹Ø jyçE§£-ÃCRÌñ€ƒ8`bx<Âýóƒ7·Ãë÷'Ôn]J³X ‹{Rûƒ  ¸x«Ÿh_V`žáF=®qP¡ZÜ“ÖätPÂË:ȓɸy-¿˜Ú5Ã[K²c§õÒ¦§ZD=mmàeÇ`Šxe†/¯ ¿ó’¦Ò&üxΟK›ÓÔªòlüü±„¿'x÷üîðÏ0ô ;Ôót:PÏH$‡ÈÑzðyòÛ³Mè—³óÂãFfÝéÛÚÚj·“Ø_¨àž÷ Öx­QGGê©VDµ-± üöŒa ªº¨>n˜×½¢­‘Zèf¾Pô¾¶QZÁìV¤ iÎäV>´_h ö¬öZÈÁÚè£xP·3w-á5âwù"á9œZì@-žÃ¡`µط nÆx©ÍÔâ—–jµ³Ò7_Ôn$Ö—tn,î ইש¸Ó9ÚPrІ¤s>ub96íWëõÄ'}麱­vôÇ—‹ßw¨6ïJk®¡´ä|ÞÊ."my¹¡H§¿Z—/'l—]”:7—cCy¥Š»lWbÔIó6}[ÕР̡¡Ø°¶Š€žÓ ›qa¯ng,ìǖ׈ëiÑ¢£I=ƒ£IjÓ4Hì4ì×Ð÷;°õ·Á×X—ÑPÞÌœbš~w$JÑH”®‘Òý:úúÛAæ3£¼¡^í×s<¼ØzTº3Y^ë_£ä®Ú£ ø±ñ‘´A”èÖAæ0èkàW ;;Äó¯Gm;[q7ô¿Fü.W”²íɦ—­'›“ ëb©Û.#–ðbO¦mxµ,­$l»¡tn,†COöZ÷Þ0›»\€$¿ÕŸ¿‡”ã¦AÀ¹Kl^¢/ýîÚ[wÆætÅö·g|kÀ«õÜ}i{Oܳ‹6•<b((tð’ΪV7wr÷ØïÚk;š²Ÿ¶³%×|e´bÎZ«&3#Ã…,wOÄjmØv{’·°ŽV¨ƒ­Ü~x¾~÷üá×ë?]?=Á¿?{ý³Á¨ÿeÚ{þ±ÚØ®yá#òáp\ÿ35›CÿqwRË“ö—à=^Oϱ9þŸø–€ÝEóéæÚ‚ Ö<ŒÃ½Nè³· < sßk°½µà7î?ð(Üüìý¹Ïº+²N¼›àñòÿ"x:0endstream endobj 6 0 obj 5343 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj <> endobj 11 0 obj <> endobj 8 0 obj <> endobj 13 0 obj <> endobj 9 0 obj <> endobj 12 0 obj <>stream xœeV{TSWº?1$ûT;¤‰n{Çvª(z;#X­Zk¡*ÈKåIHx“„„„ðØ$@Â#¼ßDA-µ‚bm-Uë«‚3N§¯™)³:kÖafÍŽ®»îw­³²NÎÞûÛßã÷û~‹ðXA°X¬U"IJŽ_¸T’”éþ¿…^Ï¢7¬ 7²KÉRãÒ^ÎF"´™½®bÃU­V—{£g¿B]kþe‚Íbš;ƒ¤2y¶(-=×÷ÈðèßnÞ¼åÿ¾l ô=-ÿßß)9¢´LßMø%?E,•IR2sßö »ÅbÑß4±\–žã›”œœ’ì>•$NÉð ‰E2™4ß÷ ßúîð÷ßî‡~wD$9—ãûÜsß#Ò@ßPßð”´J|@!^&¼ ñ Á'XD7±†ðÅé$<ˆâ!«ˆusÅá½ì½ìvžÇgçÜ]Ü à¬`‘L#g^:ùRÃÊÃ+Wx²<+¨‘LÇ‹.‡zÝǬ¯æÑyv#ý*¿Öd«ªƒdUK1U†kT1e¤¥;@LµÒ]$—/µÙ!y¥C|˜bR U+N¸7%;@lM±^!‘|Ÿp}¼XrHøè±r‘&¢H൴ ¶Ó Ã…-Þˆ³ˆ ‹»XË;Û´ô&Ÿç(ó»î'—¸_­('yg ÑÁcx·uxÈ5à¼/ËòQq¯¤[f¿qª¾ÕÖÑJòÄmŽá±uÈcÇ ³_È<ãÿ' .%>>Ó}&tŒÌO>“›!?­; ÉX•u€BcØËV³Õ!¼È剘۱'*ÁÂYÑöÐ;zX½F f#Èg~í¿™2ë¿Û„¼‘÷ÿDB´vÛÌ+”ZÊŸ¿±™ÙÈpNÚ—œÚ1šOI§Š¾€wÉ&oÝb[Œ¤IÑ,GííuÓûÁ<Ê{º–g°ÒùÖ*3´BrȬ=I-§­Ñ+%y›Èx§šÁ¾*uü„DÛÁ¹³Ú D+ÿ‚<ÐoÐËŸ½yÎ ¼ÐꊫNW݇ w%[–¦Ÿ¡}:¼¯ƒÞøz-ï}ä3¾BUlPB2Wßp‘BÑNz•y®Ä–côÛšë,hïhu¶UWÖTš© k¥šÉî¶Ñ+Ý™‘Â#€ÙúB—˜’W Á³$/&ôbÂìű֩Y!¯>º¦9lýöÿÜy¼Älû†ņ’üâ•´$’éÒ¡qªÀc.´ú"é®ñíù knü‰ü–öð Æ$¥LwR-ÈÔqŠ@mU54CrÔZ’D-7ñÂÙ¯Ï"Z^Ckö~ÿú‘°´Øa”ÇȤkêñÕ=ŒÃŽ Œ>ÕÞ)tGæô²:±éW‘??î`ZN8$ýC¾BàßýÚU¯IµQµyi³¼ ºÚZÚ¯œÜSvš:‘ =w“̯ŸlEìK£ý.awGKwßç>OÁFze«û)*˜g£Ÿ´|ôú§È++`9$ÓtžômLâ8Eº6D!(–ªZhä°U{šbüAÊ€¬î $°•á0¯2¯†ä‹VúúJêaýçõæ³7”™–þó­ ÚÌfac–]ÛÉö–ævWB×Ñ¢dñYTV‚.­òm20ãÌ.|WSäËz欦> Ñä^hñúk¬ûóè6¾ýÈ¿0Ñ×4É«i)&ÀCZuXé 1Ž´•Zà‰âÀ/I×vKÊ9/DÓÀ`gëŽ dnpVW™0Éq‹.‘ZÎð`iI˜Ÿ;@X­ÖŽÏã>‰oõtœs¢Õ‹Þ›Ø/(È­xÛ"8JŒ »±¾Ê ÏõB `Vìv_ë‡kt6xDðè”Ï—åæJ¥m¹=½ím½=¹í™Ô‹˜¬?`íYbó±¥*$mê8j9 ^yy[{ÔPPuƨl€ç( _権ÌXn,7• ,ZS)4%%¥ÅÂB%ÊtpLâ–¤?åÓÛ}÷³[½£ºÁˆónq0‰*îHe]TA]‰JUŒ•ÙˇeÆ”âL]X‘@¢ã<šõ×yv ö©–[We¯Âذ©° g©R‡°W­.¶ÁIú{|>Z‘¢ Và ¦Ç´qŠõt…ºv ›êM÷ØÔe-Š£˜Z· åñJl#Û¢ŒÊ:8B¢s-6³¹³}¼é$/¶¥ã¢J<®Ñ„»šëáUÊ&x‹D5 o|°y’ÓͲŠIÃc˜N¦Ç›¤e”ÕÍpD€N´2ùæ;a1Ya‘Bù´¨;&@©* ”|€'®˜B¤Û¿d¡oŸ²i)ÚÈWפ†«KNA£ã¢ÝèÀ³ïFÂÇ‚¿ïzôZDtÞ™Ta†H™¡8Ðjðý«k`’óÓá;÷Äoݹƒb‚™0N1½ÎmýÛ›ÞèÞ“”{kysÈ}ÿÛ+:Jnå„Ã`rgøûûeæŽT¡¤YaV`ÉPªòÓÇó¾xô¤kl’šëšwà‡E“™ým…ö„¬ós“Ã×Ö}r}Û‰EFšP"Sfåu”ùŒÝ¿4ø $?½”š¦LËΦ$©òPŽ»â!üõÛx„òF—¿óh-ï;ZŒŠø¿¡Ã¸Ìå0NbÞ¼Ÿ¡·¹Ì{·ùŒž‹ôèG 4“=‹dŸ±è~$ã¶\uàoœó_ý/ÉÎgvf÷öwuööguI(¯'åDNnßÊÏ>˪U Í«VÄ‚aR endstream endobj 14 0 obj <>stream 2013-09-23T20:54:53+02:00 2013-09-23T20:54:53+02:00 fig2dev Version 3.2.3 Patchlevel runs.epsrdementi@paris (Roman Dementiev) endstream endobj 2 0 obj <>endobj xref 0 15 0000000000 65535 f 0000005658 00000 n 0000011451 00000 n 0000005599 00000 n 0000005448 00000 n 0000000015 00000 n 0000005428 00000 n 0000005723 00000 n 0000005824 00000 n 0000006295 00000 n 0000005764 00000 n 0000005794 00000 n 0000006600 00000 n 0000006208 00000 n 0000009922 00000 n trailer << /Size 15 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 11679 %%EOF stxxl-1.4.1/doc/images/pdm.png000644 001411 000144 00000062462 12350112610 015776 0ustar00tbusers000000 000000 ‰PNG  IHDR]¶„gì7sBIT|dˆ pHYsÀÀ.¶·tEXtSoftwarewww.inkscape.org›î<d¯IDATxÚìÝxÕÞÇñ¨÷^}½EoñzEØ™ÙÝ$P Hi "T°€REEŠXPP,€”b»Ø EEPŠ…^”^C pÞóŸÌÆMØMÝMf’ïyž˜ÝÙvæLùÍ™9“ ”J€; óXm•¦à 7ÓnQ*À}Áëmºv\ë4m·üK›¡ À{¡«’¶K;™úpí<ºJ{…º¡ À»;õ¯jƒ© ×Ο´VÔ]ÞÝ©o£-¦.\9oÎÒVÊõwÔ]ÞP£õáºy3U»‰º¡ Àû;÷2 Æ3Ô…«æÉ?ëí@„.€r°ƒÏ€î›'C´™Ô]åg'_Ô¸ŠºpÍüø‘4@è(_;ù2 ÆÔ…+æECgc¨ºÊÏŽ~h@³¨2ŸÓ@„.€ò¹³/jL¡.Êtü˹¾îÔ]åo‡ÿ4g‡ÿŸÔG™Íƒ«@„.€ò½ÓÏ€e[ÿK@„.€ò½ÓßZFΣ.ʤîi+@„.€ò½ã¬3r^Cê£Ôë~:h€ÐP1vþoÖ¦R¥Zç2€ÆnС  b€ÓœÀ€¥Wç×0€]+¼¢ )íÏ ú|©ŸÕËošWk—&ZVã@ ð߈;˜ Ç%šfÍp–eÕHª\ùtý\¾×EééN•é“ ÃÊ÷ûƒÿéä{ʾ—j-i{ tTœÐUªjL³[0;x¨|æ ~Â_cšæÉQ¦ûµt¸:#Òçù}æ{:Ó|=ßÐUÅjï¼ßïq¬ëÆ  B@Å ]¡5Åu'1!ᘀa<òGX2û k|Àg]4Œ:ˆMr†Q·ŸÏÈÈøs¤Ðå7Œ—u0{Æf˜³´-¡ç‚>«ËC׳ڴ;º*^ð’5¦Åó3ü¦9Ü [Y„ä”ÁHÁ,hX}ôt;¢….˲Œ\ïë÷Ÿ¤Ø»Îó¿ƒÁãݺôûþ›4@訸¡+4 Æ¿âñþrí•~ïƒÙ¡Æºµ é Ã8-üZ­üB—ýý+‚aÏ×pièºV›A{¡  â/Pãêx¼wÐ4G†Mê)©+êë ]Ù§.šœS ;»4t-c º*vèj%×TÅ齿°Ï|¬8¯/(tåºæËçoè¶Ð¥ß³ h€Ð@èŠÛ€ú=w:§‹GèÊ V†yÈçóýÓ…¡‹4@è@΀Ócºs˜p\Øè‚Kº>«¯Plöȇ֌?z¹Ì»£²2 ]  BÂBÌÔÐEzÐŽ8Á§{‰CWdû†1&55õ/. ]C@„.„‡Pãš¿ç6»§Ë0n*iè úÌqAÓ¼]‡©Ûôû Ôa«eY§F{m(téißÌ÷;JÏY|Bh€Ð€\!AÔXÛ÷´>tÒx&×tåÛƒeœ°6·€é.Ý´9†uÙTû•4@è@xP ¨Ñ8vïi µ¯¹2Ì=ùõJÅ%t™vˆ”×n, ÞêL73†uùh€Ð€Haá&q/Vï焦٧ù™ šÞ_Å_W®‹EèÒ¯5sNM4ŒúÑ{ºÌ…NO×1ªÃÿ8×ÇJ›¡ yÃÿœÀðïX½§ß4{„ |ñœßï?)ï4•*U:1`Xceè÷ŒŒŒ?Ç"tÙŸmÓœ!ëçÿæÚy•+ûÌë÷ßž÷ùÔ!h€Ð€|CÃLíÚ_Û5HUNÀ1ÌOü†5^nšì7̯õß{çv4Mhú§X….ýz ‘ë÷Ø¢=0ŒQò¹úÿ¿u?4¬>1¬¿åÚ9´%º-4ȵPËbý¾Aè­ÃÎkú½÷Gú}“\[%!+V×tåôvùý'É©>÷ˆ`óõûžúkÆ t  àpŒ¶Bk÷—S %èL³µ\k«Óú ÜYMH86Ù0,¿Ïß"Ñ4k¦Ÿzê_ãPwÏkÃiG t  ð j ¬«@„.'LÈ€ÏSùÖÑ)  BŠ(þç Ôˆ^GÃ@„.”$TÈ€×QQOÁü™4@è@I‚jD¯›æ  BbÑ›#j4£>Žª›@„.Ä"\ܬ}¢uCŽK´½  BbºÒ5¥(‚ƒŽ¢¼&«>#Vä»®¤}€Ð€X„.ÅpG_Ó]«YÄ׌Ž÷gİNä³_£}€Ð€R ]ŸyUFFÆŸó"ŸÏ÷Ï€a´±,+×éx*êAÓ¬—7t¥Ÿzê_ƒ>«k°J•€üí÷ù[è×ßš–ÐB*fè2ŒÍ©§¤þ-<ùMójýÿÛ†5Gÿ»%hÿ˜Þ¤_3&h˜ÏêÀõ–ß4‡ç ]úï'ƒ>뢰Ïß•çû|oYV2¡ „.”»Ðå¯â¯0ÍŸò™öW¹+¬§Kz¤6&ù|þ\;v˜u—~nÿѡ˺.è3ï Më÷û}¡ÿošÐôOzºß³¯#tЀrº$Då #ŸP’}J a´Õa­N¤éôsuèj–çš®“µª‘¦7 ã49Å‘kº@è@¹ ]E % º@è"tЗ‡®ìk·Œ ¦ùYÞ!㣾o PE†ÆM… ]2Ú¡}:¢þŒJ•*X˜ÏðûýIzšÙBF>Ìsm˜ý¸þüZyNyœfO¯?‹ÐBJ=t|æõúïì{g™foZêëÿÿ1`X ôßÝaâs‘eYéú5/8÷Þš•T)é?~Ã|@ÿÿvº ”ºü>ó†ì÷ÔŸá³zéé„}F·¼Ÿõ»û|2ä¼Ï =žššú—Ðã¡kÊÂ~ïrûqŸy¡ „.”jèòWö'êÀ´Âïó4ÍVróbûoÃèíÔ¿ìááû$|éÿŸ(7PÖÁéÅ`0xJ¤Ó Þ©_å3ôt­Ã>£cQO/$tÐO…®ì›[wþq*ž5>4ä{´@$CÆW®\ùÿ즃–þûþ÷Ѯ钰£ßsTØgLÐÆ5] tQ  t7ëÐu‹3¬ûɸÂÿB׎ÐsΩ…{§×NȺ ëVùœ?>Ãþw¤Ï tЀòqzaöµVûó~säìž*sO~Ó‡n„ ]AÓ¼± Ï(ì@„.ºà¹4ŠJ2 t€ÐEè¡ eºöiËàyí¡"¾fv)|F¬@èB€Ð€²?Õ±r uèFË¡ ˆågB±Ã\ðY#©º€ó›f?çfÊ‹èí¡ ˆq/—ß°êÐ¥´CŸuõB»^®þÒËå„.4ÌÅôvÐĬ—Ëø.¸èí¡ ˆe/—aäêåÊaš?ÐÛBPÂ^.°¾=*pýÑÛ5Šz¡ (Y/×¾(¡ËîíªT©Ò‰Ô]@ñz¹¾‰¸œÞ.¿aÞI}ÐÄêZ®<ôtËèí¡ (¢ iöñ›æÄp:díÒÿ~•ëqÃï÷û“¨3º€Ò¡k•Zé ºB@è¡ t„.€Ð€ØJMMý‹iš'„Ó¡kµ]·ä}b„jÜà,»‡kâøñªãù] t€Ð«Ð••••ëïýûº@è¡+V¡+o!tÐB¡ t€ÐEè¡ „.B] tºB]„.º@è"tÐB¡ t€ÐEè¡ „.B@è tÅ:t½ñúëêÍ7Þˆè£>ÌyÍæÍ›s=×ñüöë˜ëñƒº@è¡+ºòÓ¨~ƒœ×|1gN¡^³më6B]¨Ø¡Kz­7®@“'MÊyÍÚ5k õšÌÌLB]¨Ø¡ËM…ÐB]„.º@è"t„.º] t€ÐEè¨B¡ t€ÐEè¡ „.B@è¡ t€ÐEè¡ „.B@è@é:t <øðÈÛn?T^Ô«Sw¡ „.¸‚'ƒ´ÛË¡ªÌ_º€Ð t¡ ]@܃ÁS¨º€8 ÆGÓlE]€ÐĘ¿J•4º6ëÐõõBë^.Ó|?`˜*h˜Ûéí¡ ˆe/WåÊiŸ¹YBW6ãsê„. f×re÷r…ؽ]†Ñšº¡ ˆE/—an ]Ž9Ô]@  óÝKz»vø £M¡‚›Ï¼[¿fG«´Ùò¼eY§Rß t B±N·Òu(ú=Rè*JoWÐgŽ“éý†¹Ç Z#+ì½ö&šæ™Ô;]¨ð½\á½]–Ï×¶°¡+à3Ÿ <õ”Ô¿M³UÀ°8ϯ¡ÞAè@…`žnÖ( —+¼¾(nè ˜f‡Ðû%ù|~ê„.”{:=¡ÃÒw:T-p|ã£-a-Ɖ¦Y³$¡K®ç …®‚Þ t |ö|™æ ù§’„.¿iwB×®J•*H}ƒÐwŸ{mZ÷ÔJK_Q;½ÆJ¸[r0q:mPÁB×»AÓl0Í+õãÏKØÒŽ ku BÜ*€až›Q³Öþï¾ýVÁ½ZŸÓr¿ÈlX+tE·)XÅjO=ƒÐ¯„®c«WKÙ¼`þ|EqgY¿~½ªLܯçÕI´Y@Å ]Æâ iŽÑ?¢Ÿå XN3ÌçóO¶,«FÐçKa~€Ð…2—޼zðUûˆ7î,Œ½ÿPzJês´U×t…ž÷¥87KV:ŒŸë€²Ï|A?þKX¯Øjæ]pCo×éÕ“ìØ±ƒ„ã²rèÐ!•Q£f¦žGÜü@è Lðî]÷•g¿fKÀ0>Èî“ÿ'tЗ¨Y=탧'M>BÌqWùàý÷UÔê¿ÐF8ˆûœßg® æ/"`š¿†F =ÌîZWÒ!ãíiLóçý‰ÌÌ„.ºà¦eÛ&g5ÜCÌqW¹¨GϽAÓHx gËÔ!imA7GƇ±èéÒ¡îÁì÷³æº@肇Ô¨¶yþ<ÔpK @ã´Q€Gö'fº6'V6Ï(iè’ž2¨öÈ4rš!¡ „.xi@Û‡ Ì€.)Œ+h‰º:_ÐÑ~pe?Õ¬q“œ°tÇí·»>t­]»–4]¨Ht1xÀÀ¸¨!á'9´CM¿¾W¨m[·Eœî› T˳[¨O>þ8bèzá¹çs=~øðaõиíç¤÷láÂ…®]cï»OИFÛ€ {Àó?ÚÿàZÇÓNAèBð€kCWÏnÝdþ´9 tQ] t5bZöïo‡™^^—Ð%×…àxgÖ,W†®°4þN›B…ÐB*考úˆË€õèñÔ¿â„®·Üj)/^ùŠºóŽQ*5¹jöpóçwP‡reèsï½   tº@èBïíŠÛ€ÒÃ%aæ†ë†•8tEseŸ¾jݺuG½Î ¡KШ•–¾O¿wÚº]„.ºPÁÔxêÉ'c> ÆUÙa¦G×n%]}/¿\=4nœí©'žPï¾óŽúå—_¢¾.ºäþ`ù•ÌÌ̜еtéÒ˜þ~ùŽ   tº@褷«U£ú öÆz%öØÄGí0SÿŒ3Kº¢ ¤­¬Z¹*'LíÚµ+êtëׯϙnÓ¦M1ýý:lîÕï{%m @è"tÐB—= ÆÜ¯¿ŽéJlÙÒe9F®Å*ÍЕ••¥’üûµË—-/°G,=%5¦¿}íš5Ò˶4„.B]@h@[öëó5.î™}]W«çØ7?ίlÞ¼Ùí/¡KJ‹¦Íì×>üàCQ§¹ûλrãˆe¹oôh@c*m @è"tЄz»þ§ƒ×Ám[c; ÆêU«äº&;؜׺úiyä^§/¿¬2jÔTŸ|üqÌB—Œr(¯­S«–Zôý¢£ž—¡òÓRRìi>þ裘ýféec ¡‹ÐBi@wŸz≘¨!§ðÕ­UÛ7:Ø©íÚ©a×µ‡}—%Ésr:༹óbºŽ9¢.¹¸Wö{ªïe—©Ñwß­îu§ýxдìç† Óß+÷ c ¡‹ÐBq@†õêïÇ M©5r¤Ê¨Yë¨aße„Áë‡^§V®XyÔëJº¤>|XMŸ:5'Ø…«_÷ 5sÆŒ˜ÿÖî]º2€€ÐEè¡ ˆºŽI«–òû×_}·›„ `cÎçsÔ—_|aßcK+"׌IOÚ‚ùóU¬O£ •5«W3€€ÐEè¡ Èw@[ôë·Upñʽ÷ØhL¡-]„.º€RP£"@£fö´%€WC—ìLž4ÉVØ"×Q/\¸P=:a‚~ý jÀ•ýìk¨å4þ‚F/&tÐ…Š9 FZÚ;O>þøE)Ryçm{å´!€—CWø}6 s À’%KTƒ3ëuítH“³ªXß ”ÐBÊCoWËx ¨Qž‹3€Æ´!@E ]_Ì™cOÛ¥c'5mÊõÕ—_ªŸúI½ÿÞ{ªý¹çÙÏÉ@Zëׯ'tШ<jÈJ“R¸"÷#sÐømP‘B×Ú5kìÛÃD*™™™êœfÍí÷{ßB]€Ê3 Fÿ+¯d@B–ÑwßÀ º *<ô°ý^—öêMè¡ ÈÓÛuª ¨±uëVUÅ@£zšôrÕ¦í]¹ËcÇÚïÕÿŠ+ ] t*€O<öjPf½õ6h]QJ»¶çÚïõèĉ„.º€½]çœU¯þ^–½tëÜEÐèK›ºr—g&?m¿O½:uÕÎ; ] t*Ò€)©¿}ùÅ$«(…4„®Èå‡Å‹ei¿œâÖBè¡ nPãfÔˆ^œ4ž¡­]”Ÿ–/WujÕ²ßãþ1c]ý{ ] t5\\@@è:ºüòË/êÌŒ:öëGéúßKè¡ îP£zÚ¬Çe@¼åí7ßR5ÓÒ–ÑF„®ì²rÅJU¿îökGÜ|‹'~/¡ „.¸¥·«Eƒ3ë1 ÆÑ+i@£m@èRjÍêÕê¬zõì×Ý4|¸òÊ~¡ „.¸f@”äªëB+`dKò2@@èRjíÚµªQýök†»^yé@-¡ „.¸)xýY;¡,ù*Wù—þ÷û€a}WÖßÅñÚ <‡®ßûMmÚ´)¢ÐõÞ6lPMÎjhOßçÒË¢¾Æ­×‡º@èÂø}æíz¥xÐo˜ûUÌ¶Ô €ÐßЕŸ†õêÛÓ¿ô …š¾Á™õ] tn–~ê©Õ+ÄïþXyßP/BWì‹\›Õ£k· 8Оþ“?.Ôôû÷'tиº—Ë4GêbV(tÙ½]&½]B…ÐB‡^.‡iÒÛ tQ] t%4Í;Â{¹Â{»ôsm¨#¡‹Bè¡ (A/WС—‹k»„. ¡ „. ät¨º3R/WXo× ×v]B]@Ñ¥ž’ú· Ï\Xð0´ôv]B]@‘ù}æmz%¸Áo˜kkÃz¸B­Ño šfcê @袺@èŠx=—iš'‡X†i„BWøã" O]B]@‰®ï2O….ê@袺@è]º] t„.]„.Ú)]¡ @袺@è]º] t„.]B]¡ P¾B×ô©SÕ§³gÃeZµ8g¡ „.€Ðð¸êU«½Z+-}I9²TɃ5ÓÒ×{ý·¤§¤.Öûi§ t„.\´M¶î×ÛãÃÚŠ„„„c¨ºBˆ‘ŒŒŒ?Lsµ³M–àÕ›z¡ t€Øõr w–½MÖl½] t„.»^®Å9Kóæ>ý½] t„.ƒ^®ôv8+к tºäÞ\ú¹õÎ43 þ^Ö {ZŸÕ—ùBP¸ú‰NÐ 1Â.”=9ÿ£ÎˆÛ6¹±Ö>Ì]Îöø›<·Óþ]ôÐe¬µG, ñ™ê_×Ïí ‘T¹òéù½Wöu]ævm¹þߘo t…[Á÷Ó6;G¸Ä†°Ðµ>ŒLÓ…: nÛägò¿ŸVŽurд=]ÑÔì‰@ P%ß÷ñûOÒÓ~+ƒh £>ó „. ð+øã´Å…XÁC}×mò¿´•lh÷å}ƒ§+M³i^:h5ÐAÊéÞ\yɽ¹ôç~!Íom™_ tE_É_}”+ê >S»ˆº îÛä' ]Ë‹ÒË •+Wþ?ý™é–Åý¹@èJÖÛµ(Ÿüê €RëíZ‘O/×èÒü>Á`ðx¿Ï|Oî¡€ivgÐħ·Kz¹.¤Ž(óÞ®å¥9¨Ujjê_†õ–þÌßՋyB›Þ®ï#¬àçS?”ê6ùßz»¤—ëîÒýÖuöM skÀg¾{ú‹ùBPô•üí@ž^®žÔ eÞÛµ¬´oÝ"½[ú3gGgŒa^Д¼·kõ@™õv­v¶Ç‡K»— tñ]É_åôvíÕzP'”yo×ÒÒîå]pÛ Q†Q=¹ù·3’á‚rò{ØHðúvæïål;ƒÂók¿i÷Sˆà¯„.TUƒ‰òîñ(2h§¼¬ZbÒ ÖçòJM®ú¡ FZµ”G8QQÜWd¾Èü¡ð²©Õ×}÷í·¬Ô)JN™ñò˪Vz ] tQ]@è¢P(„.B]„. tQ(B¡ „. ¡ ¡‹B¡P] tQ]@è¢P(„.B]„. tQ(B¡ „. ¡ ] …к]@è¢P(„.B]„.Ú)B…B!tº@袺€ÐE¡P]„.º]@è¢P(„.B]BB…B!tº@袺€ÐE¡P]„.º]@è¢P(„.B]E)ÏL~ZMž4Éö믿æ;í‚ùós¦}í•W ]@è¢Ä¨lܰ1gûô¤Éjß¾}ùNÿÆë¯çL?oî\*ÐEè¡ËÍ%)PôÝpݰ|§mîy9Ó¶>§%¡ ]”•¯¿ú*g+^íµ¨ÓnÙ²%×öûΑwP„.B]^]Õ«VSi))*sïÞˆÓ-[º,g:Bº(ñ ]©ÉUí{_tQÔi'?õT®m2¡‹Bè¡Ë#¡ëÚ«¯¶ÿ9cFÄéî¾ó.ûù¡×\CèB%N¡«ÉY U·ÎU¢åW6lˆ8í¹­ZÛÛï!ƒº(„.º¼ºæ|>GMKõèÚí¨ieRgfÔQµÓk¨wfÍ"t¡‹ÇÐõÒ /Øÿ?á‘GŽšîÇ~°Ÿ»¢O5jäHB…ÐB——Bת•«TÏnÝíàµvÍš\Ó|ôáGö4#n¹U}üÑG„. tQâºöìÙcŸ:ؼI“£¦»ãöÛíéÞï=B…ÐB—C—œZ(ÿÿиq¹¦د¿ýø¢ïº€ÐE‰sè’:íþ¼ù9Ódee©:µj©ºµjÛÿO袺@èò`è’A4d0 Yá9rÄ~~ûöíªj0Qµi™²]@è¢Ä?t}1gŽý÷ðëoÈ™Fz·ÂC¡‹Bè¡Ëƒ¡KÊõC¯³ÿþêË/í¿§M™bÿýÔOº€ÐE)¥ÐuøðaÕ¨~•ž’ª233íÇ®ìÓמféÒ¥„. ¡ „./‡®¹_3J¡”íÚÙÓlÞ¼™Ð„.J)….)÷k?öêÌWÔÖ­[Ur ¨Úµ=7çyB…ÐB—GC—œVؤa#û^Ù¨Ës}/¿VðÇLs—³‚ïQ/”E/—ÕGo‡„¶ÉAÜA½€Ð”ŸyXàÒŒÍf%³*u@™õr…¬·,Ë ~@輿‚ß’g/Áë-ê€Ò<jõÕÛà}y·ÉA®í¡ ðøi >óº6øO÷'QG”ÊAÐc¦ùM¤m²ß0×sm]€—ª™æo‘C—fš¯RG”]/WNo—Þ.ºo®à 붨+»·kM r H]ç^.#r/W˜ \ÛBàÉÐe®+`/½]/RWÄq{låßËvš!½] t;­ðZg¿Û±×Y© {l·žn½]Äõ èÛAÓü*Do{W8>·åyü[Ó4O¦Î@è4JèjåõŸ:ÏÌ|º ]ò™æṴ́p–%tý)Âkÿ6èÖÌ'º…®°éB+ù­Ú_‹º8ϯ`>]Å]Ît Ûö¾UØÐåLóªóüãÌ'ºw†.+le~^B×[Îóï1Ÿ„®’….gÚ+ÂN3ük!{ºNt®é’ç¯c>и0t9Ónr¦•_èÒÿÿ­‘v›vØÑ–ù tÅ$tÕ Xó ]Îi…íµçç¶h§0Ÿ@èܺ¾v¦},BèŠæ ­9ó@èŠYèú{Øv¶c„ÐmäÃ)28ó„.ÀÝ¡ëugÚ©B×2'”…üêœöpØ9‡üßÌ'¡«ä¡Ë™~Ÿ3ýÅBWøöx~ضz·v—vl¤÷¬\¹ò¿LÓ¬šT)é?ÌGº€² ]ß:ÓŽ/Ì5]ú±ÿj/:Ï/Œ¶’€ÐU¤ž®‡¬ó yM—œ’ø£óüýáÏ}æ8ýØÆ<=czûn]—p ó„. tC×gÚ› ºœçÿOÛ¾a€ÐU¢ÐU7,Õ-Lèr¦iú ¹Ö+ìñçý†5>h—û £SÀ0ÆLó'™Öï3G0OAèJ)t…m D“†.gš¹Î4·1¯„®‡®›iå¾[.BèúGØ4 òû ¿ßïsî¶y BP ¡K?wLØ +òŒŠT˜Ðõ37cºJºôó§;×fÉ´“Â/Lè2æÉ(ð;™æ¢ anMHHàòº€x†.ý¸éz!l%Ý)Ïó^ØÂ1I¦9—y t/t9·cùΙn«f1tvž?$§ÿçÛÓåó=­ñó„. v¡KNUèà¸ÜÝèŰёd$Âk"¼>ºî{½¸L{$ìõjÇ1¯„®|CWVض´£6H{ÀÙŽ†B•Ü7³až×‡‡®ŽyÞ£ØĹ®ÍÎõ>óÑ aMw®ç:0¬±ÉÉÉgž‚ÐÄ.tE³_{)Úi…¸O—l<&kÿb>]…ºv:šÍšŒ4øß¯O-Äëe`«áÑ¿ƒ5'h˜K3T2†1*õ”Ô¿1OAèJº†k·‡¡]ëôTÕ/èôýüÐ<¯·iW9GØ æ€ÐUàkN°=½ÅÙžötBÕqù¼þ”¯mׯÐZË@…ù.Á`ð” aõqF/ü”y BPÂÐÊ>t¹ówXrJãÃ0Nc¾‚к tÅXÐ4o”ßaù¬s˜¯ t„.]ÅÙ9Ž2¼.ÇLó3ùÉUªTb¾‚к t§7Ë0jëïù­þ÷Ú Ïj0Œf:lõÖ}á ÿ0ó„.€Ð¡«˜LÓÞø6ìžšaÃÒ[×5Mhú'æ)]¡ BWÉÃ×ÉþÊþ´DÓ<3©råÓ£vº@è"t@è]¡ B@è]€ÐºB„.€ÐU’öOÎŽt†V(@[g¿›º@ œ©¬q!5ðÇv9™u éNg›ü u0]ñ]¹?–gèN(-¡¡ƒ«²³ ØÛäÓµ%¬”7µÿºâºöh¿Ás6…-,Ô¼†ÐäÞ&/s–‰­¬àѶKè"tÐB@è¡ tº@è]¡‹ÐE}€ÐEè"tºB@è¡ tº@è¡ tк] t„.€ÐB]„.B¡ t„.ºB¡ „.ºB¡‹Ðº] t„.€ÐB]„.B¡ t„.ºB¡ „.º]¡‹Ðº] t„.€ÐB]„.B¡‹ÐB@è¡ tº@è]¡‹Ðº] t„.€ÐB]„.B] t„.ºB¡ „.€Ðº]¡‹ÐEè"t„.€ÐB]„.B´ŒŒŒ?›¦yBˆß0Pè \4Mhú'ê „.À,ËJö›æƒÓ|(D¯Ó·ÙË„i¾þxÐgõ¦Îܸ³††Ï'íg¶*×|5ÍûõvùÔÜB·ÉaQÚî¯yÛnR¥¤ÿºŠ(Ù0,]™/éJœf…³‚_þ¸®ä›i”îô™7 s£žG«l†¹ÖYHŽä<¦éi~÷þÔ\³sb𭆱N·Í_Bìv›Ý~W‡=¶ÆoX÷Rg(ïROIý›Y+œe ?ôòs%uæÂm²a=¶‹J¯Û¾OHH8–:ƒ‹<^ˆuÝvéé*ÎÓ ¼^q/(D%ïס«Ò…+ø`ðx½ ,-x ¨/¸°gk^!Ö??û|¾R_¨;íÆýï´[ ÙawopÖóhy~óOë}:œõ¡¾à&¦iž¬3Á¯ìKfMó"BWñÊô‘@;=ß±‚wuÁízÎo! ø¬Ô\w:ƒatÑmtw+ù Ô*ŠôSOý«n÷?ås”y?½\^ÎÖ7ìSÁ¥m÷Ñ|Ã5]ñíí¢—Ë½]ÆÒè ‰E/<ÙÛ¥Wð?ÑË… x ml´v¿apÔ½]Á(½]ôrÁ»½]FfbóBBW|{»XÁ{ãÈh½]²ô¤ŽàâÌÎÑ{»èåBíí2Þi·{¹|V_êÈ×ÇD¹¶Ëîå:†:‚{ÛnäÞ./ôry"tÉ Jo½\^9²–šú½ ,‰°‚§— î_ÉûÌùÚîrz¹PaÏ`0Í1vÚ¿e‡Ý’““ÿž÷Ú.§—ërên&Û]{ÄBÀ÷ÈyœÖåy{»üôry­·ë¶<½]™z¹àÞ®=¹êã©TäÞ®à÷èû‚>NKóvo—A/¼Òvõê|o|Iéí2rõvíóFŸ×z»Ì%\Ë^Û5?¼—KÎ-§^PÁ¤Ý—³Ónšì°{²·Ë вŸ^.x´·+3àóÎ|ïœÎ`ø/ äôvYôryóèÄ­NoW¦Þ`_HÀC½]B½]AÓ|„:AEW©R¥õ:}Y€ÓÒ¼œ k\vp685;íßzÌ ]ó½ô½½óEõ ÁŸÝÛE/—·{»~ êùH}Àƒ½] >“^. ´Óî³Fëeb1;ìžîíÚ4ü„fx±·ëwËc·rãŽÍŸ5©ÌÊZ²V[k¬µ ÖDýïF¿Ïèªÿmë<ž¡Uu¦—×ý…Y¦óON•{¹üW³´4­žv¶v~À°^˜æ„ìÿ7[8ÏÉ4~íTçµlÀQ–m÷T§=†Ún §½>ðYï;ÿvXÛµœöNÛEy\.NÐþ£ZŠVWk¦§·Å½ô¿ršaG­¥v–VSKÔNÓþ¡G=–éü;Ι§9ó¥¦3ŸZÚóÍ4Ñÿv—ù©5uæoŠ3¿ÿ-óŸzD·ÝJyÚn+g#m·›—Ún¼+ì$'uO´ü#j¥¥¿˜Q³Ö{ujÖú"£FÍïk§×ø¥fõ´õiÕR¶UKLÊÔÓ’îÂä@0K?¶¯n­Ú{Õo°³eó³wth×~û…Ý{ìèrAÇ=õèiÿÝêì;78k§L—ž’ºO^'¯×ïsXÞ¯º~ß©Õ7ÈçèÏ]¤?÷Kí}ý÷KIþÀH=mOí k4ð¨ Þï4ðAÕ«V›¨çÛºgk ôÿ/Ñótµ®ûM©ÉUwéú? §*MëHJRòýÜÞzuÏØÝ¼q“ç¶n³½KÇN;.¹¸×ÎK{õÞÙµSçíçéÇä¹úuÏØU+=}¯¼F^+ï!ï%ï)ï-ŸQ»FÍ¥ò™òÙúsßÔßE.¤¬µÖÚŸ˜gk»rÚ…´ÁÒ^¤Ý„Ú®´§|Û®nÒ.¥}J;•ö*í¶×Eï”v|®Óv¥}K;ϯíÊrÖvßåH–'g¹ò³SŠR\.NsV^^5˜xŸn¯èvù¡öµþÿt{]¡·™¿é6ºC?¿ßiÓªjbâÁÕÓ2Į̈³»IÃF;Û´lµ£S‡ vôºð¢—_réÎ]»íhßöÜí-š5ÛqV½ú»ô{íÑï±_ogímºþ7K/#{ôv}‹Þæ¯ÕÛàåú3¿Ó>ϨYs–þÌ)zºaZ-•ý|pªSOäޤþ¤¥>¥^¥~¥ž¥¾¥Þú?$óCæ‹Ì™O2¿d¾Éü“ù(ó³õ9-wÈü•ù¬ß;Sæ{ö)ÕÖaiÒ.¤}H;‘ö"íFÚ´#iOÒ®œöuó yÚîÿiÕµ ´ëõúdjž¶ûS¾m·f-§í6ßqþ¹çåm»Ûj»ÁˆmwcÞ¶«?WÚî½zÚKµFÚÿ\ºôü»Ó%½O7ëJ{IÿˆÅ²Ó!•¥+i÷Å={î5rä¡©ÏLQ¯Î|E½ûÎ;êóÏ>S æÏWK~üQ­Z¹Jýþûïj÷îÝêСCª$åðáÃjÏž=jÓ¦Möû.Y²D}³`ýyï½û®zí•WÕô©SÕÝwÞy¨÷EíÖ¡n·|O™Éú{ÿ¨¿ÿ =“d¤½Z Žå|A8V3µs´:¬>¢ëáSÝ8×KÖÿî;¯MÛ×\5$sâøñê¥^Po½ù¦úèÃÔW_~©¾_ø½úù§ŸÔºuëÔömÛÔþýûUI‹¼‡¼×úõëí÷–Ïøú«¯ìÏ”Ï~éÅ•|—k† ±¿›|Gç»JÀþ¬ZRòxý[:GòØ©-ÿd>”ù.ó_Ú´½,g¶kÛvçµC®Þ÷è„ v» µ]iO¡¶+í,ÖmW–‡PÛ•å$§íêå'JÛ]¯ƒàgz=$Gî8Ë£,—\»Š¢.ÒSÛPv"ô¶m´^&fé_eÇC·¯ƒz§eg¿¾Wìy`ÌØ#Ï?ûœzýµ×Ô￯¾˜3G}÷í·jÙÒejíš5jË–-*sï^uäÈ‘-YYYjçÎê·Õ¯¿þª~X¼XÍ›;OÍþäõÎÛ³ÔÌ3Ô¤'ŸT7¿ñ`—ŽwÕ­±GœÊΗ^&æëŸÖ¿e¨ÖÞ9Š}|9ŸÇ;¿S~ïPùýN=l‘z‘ú‘z’ú’z“ú“z”ú”z•ú•z–ú–z—ú/I‘ù/í@Úƒ´ iÒN¤½H»‘ö#íHÚÓ•}úîiÑ´Ù.igÒÞ¤ÝIû“vèìÔJ»<•å´B¸.-%uŠn» tÛÝ*m÷ Ýv»vê´ë–›n:8ù©§\Óvï3æÈ—÷Ùsv“¦»ô>p¨íþR+½ÆÛú{ߥË%NÛK5té<]ë¥ã4ý…ê`µS*²~Ý3vëÔ¹óö[G”`õÙ§Ÿª5«W—8@•V‘ ¶víZÌ>·ÙÈ·eõìÖ}Wƒ3ÎÜ-¿O¯@¤'fqõj)Ïéß/z] 䚪&ú7Òóïc½£·Vvøä(BësZî4`ÀÞ‡|èȯ¿®}¿HíÚµKy¥H`_¼h‘zó7”ü†Áî•ߤöýòÓS«¯Ó¿ù“D¿”Ô§¡z´íúí¶û‰ÌO™¯Ò#Þ¦e˃ÜûÈC‘ù/í@ÚƒWŠ,g²¼ÉrÞve¹tÙZY^e¹¥í"Ïé°éÚ5º¼Y³zÚÏzgaŸ–Õ¬Qã—_réîûF>,d‡FDz¥8pÀ>h!;FO>þ„ºáºaû.hþÎŒ5÷Ê‘k½ÜoÖ;D_ë¤ ÎÞI‡r6Pùò{äwÉï“ß)¿W~·ü~©©©¯ioÒî¤ýI;¼¬÷%»›6l´+9Ì’v*íUÚ­´_§sj¶·Ú®\ÎÓQ·Ý‰ºíÎ µÝ:5kííx~‡Ã¯¿~ÿSO<¡>|ÿõËÏ?{ªíJ'м¹s탤÷Þ}´Ý]vÛ d·]½=^®Ûîëú÷‘Þ»˜….ýf“®ÀÔªÕžHOI]'§Ð\ؽûÎ'üÈÇ}¤V®XYâ$êör(ëÝc&I\ÐÅ=/Ü¥Ãæà7:Gຸy…ï\s2¬fZú9í©Qƒ³vÝrÓÍû¥ÇQ¿•/ïeûöíjáwßÙ½œ#n¹u“³Ú§“éç ½’¸^×O V¢®l»5dþÈ|’ù%óMæŸÌG™Ÿ2_Ë}ÛÕ˧,§²¼Þª—[Y~¥.dyvNÇ¢íV¬eB®]î#ü”äª;õ²‘9¨ÿ€½Ï=û¬Ý{+GˆKÚ3åö’™™©–.]ªÞ™5K=0öþCíÚž»C/k¦¥-Ñ;DÒ£ÒÜ­g78½óÍå{Ê÷•ï-ß_~‡üù]òûÊs‘ö¹qÃFû €g§OWr°·vz 9E[zþ_“ö-íœåÝ•§ì·ÐmwŒÌË$€È™$ãî¿ÿ°œE¶|Ùrµoß¾ Óv§O›¦ôë·G./Й@N]|Õ锩TäÐ%§ÕU¯ZíiIt´k¿sâø ‡eÃ/„¢ì<9m豉têpÁ.éŠÔìy]o \²pÈ…‡ýÒS«/“S¬† š)GÑ7oÞÌÌsŠt7¿ýæ[ê†a×ËQ‹L]W?9§uÄ ¶Ìüù!óEæÌ'™_”ì"˱,ϲ\Ëòž’JÛ-ÿ;;èúl¹NáÒ^½wË%rô˜òG“Kî¹ëîƒM5–£Ût]Ý-§èºdšò}ä{É÷“ï)ß·¼¬¢9­Lvd¥‡VÚ¹^¯Ív®ÿází²m»¹æI·ÝíM6Ü}רQYŸÎžmŸ¶GÉ.¿üò‹}–œn»rzâ½ÿò‘®·v‘þä­Ü²—ûFß›%§R .rýÆcÆʨQSv€VɹØe¶tX~JB`÷.]wË‘³òÞ«ÞL9}ãÂî=vÛ\V«ö ƒ«”þ© RïRÿ2d~p§à"Ë·,ç=ºvÝíü™Dø*?×Ûêy:Lvvš7i¢ƒÖ3Gä:JÁeáÂ…jصC÷ÉÙ9:¬~ªë2©Œæa5½&×jï#ß‹Rp‘v.í½yã&»¥ýËrÀõ­¥ÞvSô²óEµ¤¤ÆÝOÛ-|ÛòôÓGš6j$mw[’?0$üÔÙP垨7ÖOÖ¨ž¶OŽ ²³Sü0¹(°vz}Îõ_ÿ(Ť©\¿ì×?SB ¥xeÆ ꪃöé…e‹ fÀÊ·TÚî9RßRïRÿ”âüØ¿¿´ÝÍ2¤=mËÓË„?=%õ›V-ÎÙ+g™PŠWdP­‰ãÇË@YûôÎÏÕ¥uÝ|Žþ¼k«éϕϗïA)^‘ö/Ë,²\°~ˆÿÁžä@p˜,3N˜p˜¶[ü2Þ|Õ¢i3i»_‡Æ‚°+X®å—\Ük§ðĦȵ&:üìÓ!hUitëÏè,CdJà£Ä¦H]Êm䶬ˆãÚv{J=ÓvãÒv»ÐÆ<¹L˜U÷Ž»ÿþ,€Æ¦È¨e-š5ß›žZ}JiÌCùœ³›4Ý+×hQbp@[/²<Èrá–SFË+ÝvŸmuv‹½?-_NËA9xð  $sP·Ý=2¡¬à;6oÒtOy¿ø¶,ʹ­ZK%÷Ž÷µ´”ÔUï¿÷ãòÉÇËÎëZNkˆß5©_©gJl‹¬œSÌcÒª¥L¾}Ĉ´âØMO®·ˆ÷èÃòþ)IÉû·nÝJ¥Ç¸Èr!Ëë‰ø]¿%§ÂV„ÖJ»ÈhŽ©U«MHÐÿÙrï=£©‘8”»FÝ©äTŸ8/$—&‚žžßKED$åæ íX!Ç¥íöÓ;AG8à‡#Ãz}äHÛmK[óÖˆDËDj¢Ä¾œÓ¬¹,Sã9«ß¿ ýù¬ÔâPÌ_ dùà@h|èÀõIç :ÒvãP>ý©l“'ÔH­¾ºe󳩑8”Öç´Ti)©?Äy#Ý,­ZÊá>ø ùB2[ŽŒæ<ò¸µÝ6Òv¥ž)±-²>¨JÛõ$¹Ï–°£Ä¶Èåºn•Üj!žó¯zÕj“õΫ¢· öåŽÛGÊ<ÜÏz">jVO›–š\UíØ±ƒÆã2üú”^/d&Ô­±¨y“¦r¯„ qß›Ò(rcÖ®¦êŸq¦:£vÆûqÞqmÚ®MÛ=õêÔU¯î´~ø‘:3£ŽjX¯þnÎ![Ûm}þyçí‘z–ú¦Ä.pÉú ^Ý3h»Þ<½Pn 8‘3bTdHçíÚ©Ö-Α—Äsþ¥§V×­SguþyíÖ?FE–Yd¹ÐËÇvÖñQ§f­Gºwéª:u¸À¾/%m7ëz䡇UÔêÒ~7'èP°hñâÅv “ 79;|ø05UŒ"§IÉÎc£ú ÔAƒÕ‡| ;îïÆ;tõ¹ì²íŸ}ú©=ÿ$ìÉÝ´)Å+r¤o¸ÁÞi•yy^ë6›Ùq_èدÿV©g©o©wî%Wü"˽}°§îJn^N³æ[h»Þ£7Î{%$tl¾½óóã?и‹Yäf­“ŸzJÕJKW?ú˜œò¿¿4B×Ë/¾”äóËûMcãY¤ýËr ËÃ’%Kdçu+ë‰ø…®×_{MMxä‘ì¶;im·eÑ÷‹ìƒ=]:vR?,þAF6ÿÍ]²‚Ýoyv Õ¸ÁYv2“»ÛS ·³3qüxÕ¤a#Õ¼Iû¦»R¾þê«R ]òyÒS);]Õ«¥¨+ûôµw¼Ð©# ­®ì§ÒRRäþ.jÛÖìSC]ñ]RÏRßRïRÿ2d~Ðv ×ve9—å]–{YþCg,º¼ºBG÷e»R§V-{‡óÅç_P{÷pCÒÂ5ðö#d'G–/ˉíî;ï*•Ð5ãå—íϓг[wû{È÷a4ÃÂiçÒÞ¥ÝKû—å@–‡ýû÷ºâºÞ|ãì¶ûãª{—.ö ÚnÑÚî Ï=o‡-¯Ô=no§åþ]G…®Pùö›oìž/Iº—öêm©‘ÀïÙEêA† }zÒd¹µŒ¦®½új5÷ë¯sÕQi‡®P‘®©ÏLQçµi+§Ç©‘·Ýf÷`ržîE€ßÿ@9Ò>Èpn«Öê™ÉOuŠ-¡«tBWxÛ•ù óCæ‹Ì™OÜö"˱,ϲ\Ëò­íº¼ºBE†žõÖÛê’‹{ÙÛd9‹BvH×®YÃÂVGóæÎU§.hßÞîí3ú^µjåª\Ó•vè ùò}ä{É÷“ï)ßW¾7%»È}_~é%uí«ív.í]Ú}xºJ/t…ʯ¿þªFß}·}¹…œ2ûÀرö¾í °*k×®U/½øbNÛ•\ðî;館¬¬\ûœQCWxb“pÓðáªYã&*£f-¹ÿ”½CÿÅœ9ödË{“ß·qÃFõÕ—_ªiS¦Ø×¾Õ­UÛÞ!”£Ê¯½òªÚµkWÄ×–Uè /Ò5/]Åõ詪W­¦ÚŸ{žºç®»ì•™é̽åÿÈ©üF9â(÷/º÷ž{ì#Ò+ u"u³xÑ¢¨¯%t•nè /2_rÚ®ž_2ßdþÉ|”ùYaÚ®^Ney•åV–_YŽ Óv ]å#t…—õë×ÛÛ!é ΨQS59«¡}€tæŒöMd+Âà ²#³bÅ »‡÷Ñ Tï‹.²{ÈÛµ=WB•ÝKíZ¸² ]¡"ßK¾Ÿ|Où¾ò½åûËïß#¿+|G­¼9³AîK=ÉiåM5¶Û³´kißÒÎ#BW釮œ¶›uȾÎwŒ’ý"{;Ôë í¶;û“Oì ᾂ¡¶+†»Þ>ÃMzcöﯦO›fç…hú ]‘Vø²Èi@²$‰NFéiÓ²¥êÅ•ö‘“ŸßûÍS)÷ð˜7wžVï=Ú^øÛ¶j%C¾Û=Y²³#)V~ßšÕ« õžn]áEŽLHx”#²¢— v’?`}ëѵ›½ò“óÞå(ºœŽ!+8¯ù®r3?¹?‘üù-ò›ä:7ù’e!¿]ê °Gi]eº¢µ]™2?e¾68³ž}úŽ’n|™ÿÒ¼Övey“å.WÛÕË¥üFYNey-jÛ%t•¿Ð•÷€ òIO>©úõ½Â¾4 Zb’}*›œ–%Û«‡Æ=¨ä ‚ÞK½ÅNd;+EòÞqûíê²Þ—اï'öQ÷n;Û§=ÉrSØAÀÊ:tå-ò½åûËïß#¿K~ŸüNù½ò»å÷K=H}xi`ioÒî¤ýI;¼æª!v/Ÿœ®&íTÚ«´[i¿…=“ŠÐUv¡ë¨¶»m›zgÖ,5â–[U×NíSé¤ížÝ´©ÝÓ#g©H€þü³Ïì^y/µ]9›dáÂ…êµW_µ{¥ÃÛ®d™\2\ó&§]¦í+tE*rÀo,P¯Ì˜iï\5pPd¤9:-‰Xà˜{ﳋ±•´,]ër”V>[’¡üÈXuµË‘"ùò¾Ò5úÃâÅöçI—£ä¬ä»8Ð>Ú$¡JŽ8ÉéxÒ“%ßURìüyóKta¿ÛBW¤"u.ó@F<“•ß­7ݬ.îùÇN­œ¾$Ëãò¼4ÂÞ_Íù|Žöå4KÙH=ÉQùXô|Ê{È{É¿òÞòrW>S>[VâáßU. }Wé¸åÆ›ÔSOÿìsûú¬x âU*¡«8Géä(„ 9?T®Û^4Yˆ¥ ^®Ÿ’QA^ùŠPåñÐÑ9™>ÖGç*zè*ÉM9" ÷(‘£Rr¾ºÕ +BG8ä¹xá tUÜЫ£ñÒ>CGãÃÛ®´çPÛ•v.íÝmm—ÐEèŠÏY#órΑkÉä éE–g9êE–ïXö"ºJvÖˆÌÐY#2Ÿd~É|“ù'óQæ§Ü¨^æ¯Ìg™ß2‚µÌ·\gFèònè*IÛ ?ãIÚnèŒ'/µ]W†®òV*bè*o…ÐUqBWy+„.B¥b‡®òV]'t•·Bè"tQ]„.B]„.B¡ „.B¡‹ÐEè"tQ]„. ¡‹ÐEè"tº(„.B]„.º]„.B¡‹Bè"tQ]„.B]„.B¡ „.B¡‹ÐEè"tQ]„. ¡‹Bè"tº(„.B]„.º]„.B¡‹Bè"tQ]„.BmÐE!tº@è"tµ8pÀþòreB¡‹ÐE袺]eW䦲MÞ·o¡‹ÐEè"tQ¼ºd…þÊŒ™jÄÍ·¨öçž§’¥WšªÉY ]„.B¡‹Bè"t•bY·nšòô3êš«†¨f›ØÛcqã 7º]„.BÅË¡kÉ?æ¬Ôú¼º233Õ¼¹s•lŒ¦M™¢>=[ýþÛo„.B—+C׊+ìuOŽ%KÔª•«ÔÖ­ÞɈ„.BW<Ê“?q›LèòVèÚ´i“úìÓOÕôiÓÔK/¾¨æ~ýµÚ»Çݬ„®Šº:”{{¬-[ºL­]»VíÚµ‹ÐË"«w|Õ­7ݬ^ùŠzvútB—‡B×Ò¥KU¿¾W¨ÔäªGm¤“AuÛ­#ìÆHè"t¹©´k{nÄKQ+-]õºðBõÂsÏ»z'…ÐEèŠG‘ƒfÝ»tUcFß«>|ÿ5|Øõ„.…®·ß|KuéØIMë¨u[ÝZµÕ3“Ÿ&tº\U¶oÛu{,Ö«¯®8H}òñÇêðáÄ®Xé!!ty't½ô öüʨYË>eüè©ÏLQ7 n‡.yîÒ^½Õ‘#G]„.×…®®:«a×µÉÁ9”ž’𳲝Æ™êóÏ>#t¡Â^Ó¥ƒ ¡ËC¡KvNe~éõƒ½žüÔSvï¥l‡Cë5ù›ÐEèrcè4`@Î6ù’‹{©M›©ªÁÄœçå€ÂÚ5k]„®ŠºìÓ¦NµO/Ì[¾Y° 'x½óö,B¡Ëu¡ëå—^Šøü;³f©æM²¯g‘¾œ:Kè¡‹ÐåöÐ5yÒ$»G ÒΉãÇÛóR®—]B¡Ëm¡kã†G=/ƒø<:a‚´ {š¦»îRB¡ËEŽZÈü¼ç®»]„.Ï„.)Tm[µ²§;»iSû¼sB]„.¯¤!ALïÚóÓ=ø„.BW¤Ð*+W¬TÕ“ìén¹ñ&B¡‹Ð•·È¨”2?å4BW!ç¹ivpV@ûÂV©È9Ϲr ˜§df?g\@èŠMè’"Ÿ‡êü£>$tÐEèòôè…¡)Ýxö‰[C—ß°ÆK ãÍ<Ûê+íêÜÛ>£AεíÉÉ'tÅ&tI¹ÌØœ3PÜtk)B¡Ë¥eó³íù)¤º]^ ]N¸±§½óŽQ„.º]ž ]6lÈÙ¹]³z5¡‹Ðå¹ÐÞ†gò ¡‹ÐEè Ù0…ÙØ¶u›+¿£C—ßço¨¿Ól¿Ï|/üñä*U*Éã"©råÓsµŸù®sˆ³M¾;×PŸÕÎÞ&ûÌr?îK m«+Uªt"¡+v¡KF/ ¬!÷$tº]*ûÜÛ´”{^Μ1õߓkº]¹íLÛ­sgB]„.O†®WfÌ´ç£ÜÞŽ\n]åEy]RdDC™öá"tº]¿mܘsÞøõC¯sõw%tº *r=¢LÛ£k7B]„.Ï…®Ï?û\¥$%Û÷îzí•W]û= ]„®Â„.É2­ŒÆIè"tUèеiÓ¦œ£û÷w݈o„.BWQC×Å=/´§•ûxº@è"ty)t}õå—vï–ÌÃéÓ¦¹ú»º]…®¬¬,•丮=º]¥^¶lÙ¢ZžÝžWôéc/n/„.BWA¥a½úö´>ð¡ „.B—gB×¼¹óTõjÙ§ùËM’Ý^]„®‚B—ä™Ð´óçÍ'tº*fè’2Ú´liÏ»>—^fßãÈ …ÐEèʯ¼öê«®\Áº]„.BW~å› r®«~â±Ç=ÑÖ]„®‚B×°k‡ÚÓÕ¬ž¦8@è"tU¼Ðµ}ûv /ö|»´WoW-„.BWqC—\›XÿŒ3íéºtìäºß@è"tº]‘ÊÂ… %¼Øóíщ=ÓÖ]„®üB×gŸ~j_—(Ó=4nœ«~ƒ§B×äI“ì ×¹Ú®ÐZié9‰óç»î{º²ZûsψVVš^*„.BWx9”uÈÙë±cUõªÕìiêÔª¥V¯ZEèB…]r-|ÛÛù‚Žör ëùðÇÿýwB— ËâE‹ìý'™gã~ÄSÛcB¡+oèÊÌÌT?,^¬˜3M÷.]\wpßS¡«qƒ³r*3šÇ&>Jèra‘ohIï¤ÜH6¹?¡‹Ðå¶Ð%×kI/­öº@7¤k§ÎjÕÊU®l»„.BW<Š´÷‚¶ÇBvî ]î+Ò+/óGÖeѶÇbÚ”)„.B—ëBW«çäl“õþu®uŽôrÝ|ãjï÷­B=ÛÓ =]î,Ò#P˜ ´ì¼º]n ]áä‚s9P††¿ç®»ÔÂï¾sõz“ÐEèŠËPžž®hèérgéx~‡Bm“÷ ¡‹ÐåºÐ’hùíë¶š4l¤ú^~¹ztµnÝ:×.wž½¦ËK…ÐåýB誘¡«<B¡‹Bè*O…ÐU1CWy(„.B…ÐEè"tÐEè"tº@è"tº]„.B…ÐE袺]„.B¡‹RÖ¡ëÒKwQÛ„./†®WöÛI+#t!WèʤõÆ+tÝIè"tÐUv¡«N­Ú« ]ž]Í.íÕû0µŸrn«Ö;Ùq[Ûm{eŸ¾´Ý8•Íší¢ízÞiÏ¢õƧÜuǨCñ]i)©“ ]q ]ÛXOÄGí5§ºâº6%dÔ¨™IèŠ_èª[«ö·qÞq½ù²Þ—¡¶ãSÚ¶juˆ׸µÝ»ûõ½‚¶§rvÓf´]ï-§§§¤²Lü{×啅׳Ùîٞݬ›‚0L»(Ô`L»`Mì½kŒA@‘¦FuWP±{/Q#öÅ 1b jt Œ0ÀÛïN®Ž‘Ut)ïóQ柹ó½ûîwï}ï·Ó ¢“φØU4+Uç¥è²«èÊ–¾ÂN¢K«K–¢Ë®¢Ëô+‹òú˜Q£È )$l ßV­/ÀAŒ±ó"=  ‡¿lG±Ã8vô˜ðölz6~K:d»pwD/ÿÉ];ŒÄƒ…—‡çmظŠäZ™šŠº®n¦ãÇŽÕ“k¨m±wÏ^wøó,ØX%ìÛºkaLLžôB¶sç|•Û¸AÃMÒWØ®jÍ·q‹ce÷‰FTD$qw-9ùÁÀ1àI:›.ž~@ð¦ïó–Jár÷®]’Õ6F£Q´iéC t_Ég»q÷m¥“³~ÿ¾}’p6&“Iø4oa‚}$ÏÊä¼X„K[ØÔ¦•€@| Ü«¶ÆEùèÖ­[ÒÙp\K½&°^О¼’Óvãn=¯ìïÜ‘„³áH¹rE¸ÔrÊ}ß·>Àà*ÐDï•Iûà+€`hUB÷üÎÈ8uj.•ßåxµqâøqÚ‹—­Uª¶Ê*—ݹû± ¸;=(Xr×&ÕÙ£¢>¸‹ ol[Yr¬LÎ Gà<°xOÚä•íù/àD?—Ä=Õ —P5„—l3|õQPP â—-+P9+Ì\ÇJNÛ°sÖó¦%ùlÀÝ%qqù”,F‘} ;¦à>° p—ä+¶cÿ0H¶/áû¿§Qª6iØÈ´<>¾€úGå(Þ8}ê”1l˜™DìÙCòºÄ¸[÷G#÷¬Ë— ƒAÆYÜqòÄ 1tÐ`3»öì*yUæçį(€N  –Ùý—²¡í~BK:Fq„×ÍÎ~³öìÞ-ò,²ã°8#77WìØ¶]|ìÛ6 " öt“¼.1î6U+\îtïÒ5ë›={%w‹9rrrĶ­[E;ß¶&ÄåW`OÍcÛþ"p‚€ëÀ`TI‹‡2FÌ7éô@``d±ÕîuWt*õ*e2ÔL™o9aŠwnßÿ^´¨ÀÃÝÝ„2ÓÙÁ1Hrþµq·ƒN­IwsG a&!‘—'¹[Ô ö¥…1144aÌtªé0 6|Wr©\͉º@@'Qî%A T•¶)Ò^¿£„W¶ Ýk|?ïP2krŠ›Fk Í»š’"×ÿ—/]Óƒƒsa33ÖŠCûSBò»Ä¹[Õñƒ#ñ=¤ÖÖê²ÃBgJî>§ªuáüyAgÔ^ìªÖü6ì÷ô¾ê¢Œý=CX˜X€QË\;rj˜„Ô®e1ºÂKÀäÒÖBïGáXë L˜ ¥“sN@÷ØX‘|9¹BO ªRÖqÊäÏsš4r7ÂF49¨°%ñ^:ÛRÁÝâ{ Ô*U·ˆ»½üýKã–Xû¢+òxøð¡Øµs§˜Ž´¯å0@"Û£"–BÕ+ÊÊs5ð(`¾z¿-e﵎F©Š¥jtW·¬‘Ægo\¿ATôý37nÜ k׊!eA˜f«œÕ —˜ÂÕ‰×ÎÝúX—QP]âîðáfÉ]!222Dš5bð€Y:µÚ Þþ̃½”EÙòE9ö©ìÌèp3ÀvtäÜ.G«8¾,°b8˘ ¤KøsW/#Ÿ§:½_­JBÜש5ÙÛwÐ>iÒ#btAjjª°XÊÏãaîß¿/¾;}Z¬_·ND†Gä÷ï÷©ÑÛ³©jNm­î´SMâscyº[©çîûô\pökâ.mòíìç÷pÊäÉ9KbãÄýû­¬ËS5÷Þ½{ÖVWÚžÿYß~FoO½³ƒc®ä®¾÷ßSG'B¿ç.‹Ã¼.Q°#UwÊÓáX”u\Îô˜`) Púìg¹“Öëje(y;ó™ºR²Ýë70ôò0L±¬^¹Êº7óö­ÛÖìyyùùùâæÍ›âÈáÃbåŠ"xÚ4‹·îúuêšh?º›V»‡·g(å/õÜ¥y8Ü=ˆxÊìÞ ¡‘¸;#8$¯¼r—‡+âãÅ´/-=ºvÓׯS×hå®F»‹$T¼ˆ _ö ˆ¦\ò 6ò¦_Ê0=N+@.ó×£E¢’§2÷|SEo0ØÉŠX€[@" LäÓÊÉĩŠó$‹rH“¤rVèkÕ¨™ß°n=c×Nõ_LÍ_ºÌzôôõôôRÙæE™ÿ¤¤$A>çÌš]0xà@“Oóz|–GL-ÔÓñÙöâçHæ+ñömé<ËwµJÕ*8þs¹Û¨^}c·Î] ஥´s÷Áƒ"éìY±iÃF1;jVÁ L>Íšwsœ ® .Ó(w#¸Âá)¹+QÄœø+ Ž‘@4@,í¡ç´Ýädé€ñÀ'tdziäÇjj3&‹ƒ¼Óšü@Î|zÜ ü©|•¹4~,s Ö­+Ð:;:Z¼šxúöêm ͧ¬:µ]gff–Êö*j×§ç“®YµZ„†LÏëÐÓàÙ¸‰ÁÙÁÑqI•¬ËX—7:~Pãq|è*«ôe~‹M} ¸âªÑnÅwœBÜ,Þž†~}úÃgÎ̧jæÉ'ÅÝ»wK%wI(’`$á8#$$„¤‡{cÖà<—ŸÅÕEJ½¹Û[•Šk3[+`ÊJ7£gGèn.r;Ä]®%²¸¡cqãù×Hn• Eae¶9£E ‰ 7%ŸBô¼\9ÛÛ’’XSej oœËŽ{5°Øhc[.@Ï`:Ä=ó”!ìį[µ‚N jaq{œM„óO¨­Ó}@ÐJ<Êh´ié£÷û¤½¡gÆAý˜ÆŽmœ:Õ13LÌŸ;OÄ-^l%- !jã;”xÈZqºxႵ"AN™J²W’“ʤsVM•¶í[·‰¯Ë c@DÍÓƒCò'O˜H{Ó²ûõî1ØÉðч¾úæ^ÞÜfVx¼÷áÏaÞ5cV’N±brNqÊÏÜu}Â]jÇkãÓJß±}{=s7ë wÃÂEô ­ayÇÅæî»t":Åóàn(x±Üý¸«tr~&w'ŽŸó„»11Er—¶Éd\¿nånzZš¸téR‘Ü™?ÿ wÇû îZ“…¸ l†€¤½ýþ@à¶´KIÿ L$’|8“E“q 0¦CŽæ¶u,ÌY¨%7¸’fâ6¿Tn­8ÎbŠÞ*ÎèQBÙp`8=k èÂÜ›…U591нAÙí×–íÙ—í; $Ž7Z§R¯pÓj·ÔѹîG@y ¾HY{ˆ¤1Ñ(ã•C/ê}ŵ7(+‚k’píü¹‹‚e¥‹q]/úc˜'4Ú3Ü™O2C&ñ2ÜíÊ\swÆc[jët q7]£Te“»‘Ïáîû’»¯yNÐéˆ.,’Úpõ¸7'=Çs4Š¢XÎ-{¸¥/‰;Bnó¾fÿœÂÿG×ìæGÑ,ç×x:±Ú‹ïÙ†EœB>žã¥‚Z [p»i÷‰gºaðU‹tjÍkðS‡àÛÎ`}NÖ©ÕðU÷(ƒO~ö™Ñßéß!˜.ãÚï€DüÞ6øµÕx…ð“3¹µ“Äðg@7Þçßœã‘ß‹Ä r÷oÏán8q×õYÜÅÚû îÞ¥$æÓÜŵ«˜»¡Ïán‰múìË•þÉÞkIEND®B`‚stxxl-1.4.1/doc/images/btree_uml.pdf000644 001411 000144 00000110343 12350112610 017151 0ustar00tbusers000000 000000 %PDF-1.4 %Çì¢ 5 0 obj <> stream xœÕ\ËÓ$µ ¯ð ÌR<–%À† “e“|ÙŽßkªR©Ê-ÌVår"@;T}ä?’Û¶ÔmwÏ|Ó $ÅY}j·ý“,É’Ü·{1ȽÀÿòÿ¿:íþð…ßûïÜãß»C²B.î—jÐaÿý×»o>ÛÉÁ*kÔþ?»ñá/þ¼3N†!†½? ÞîO;'ó£™ò|wÜi+`¿×>úA*àÒÞ¸Á˜½ò> Ò$.£´,ç²AÇA«= å—FÚî¥p¹áÈ•>$ rYÓ¼ˆ«Œ.£Qƒ’‰Kyë‡èØìeprj:ûàÂ`ø¼œö «£§7 ¡÷V«ÁË\J;?]ŠÁ…qAúÁ)˜½ózð:…Ó2{¥ÄøF#,b½—f“Æ*xMfo…|ÜXjPì…Aû!ä¡ M*g†˜ W6 z2’–ðå+”ÖÄ!è:Ï„|ˆv°±®ç´óÞÀœAŠ0¶‚—p./íàøùc‰)?®O2®ü2Ie’ 20u-“>\WREÐÛV þ©Å ñµÆ[+&ÙfQ˜ÏQÅ;šj"È)ZFù†°×”"Æ‘’Þ "Ê€Â8­.åì °–Èf´FAh/åàÓ¿Ç£qO€@Ó@­Š*Ý-›·r* ‰Fûâ/ÒÝàP@…^ä’É]dÇ“^8u*ð>™5–íÜp(•Ü— ´Uçý‚JÛB5X+—¸¼K«Ó§j³cת¡£pèŠÆ” V\}J”cœ ¸Ž»[` )&(Ò‰(Ö&Ï öÜ.RòSÏwÿÚýmÿ] ¹’»ÝÉñwþßW§ýŸA„†Qûgßdo'÷¨×&©Y,üÙi÷÷›Ÿ@ %Ìüæ¥üÝù`n^><…Ãûho^9<…½"]ððÓZ÷gÙýéÙî¯ð_ÝMiªf4ÚÉÊóé²ó’ز è—JþÐh„ªG©@\¸l5_¶B›šÞfP‰óªŸ‚ŠD­pÙOA§DÐ× z®Àžñu¿zWä |¸yþžY…–7?? íqšô:ýdTé  ÷¡k†‚ÁZÁ™ÀZù*…b•l®;”:ÖfXØ`íF\¿üî  *‘=»î{‡§8[ð‚ì7I0W{óå÷D|‹žZ‡Ûê›·`ƒ6þæzí»u.÷»Ò~¯«}y> ŸU úky§£.ïgNç€SkÍtà¶‘%“®Ù©‚ÑO2ìPª¼7K=9Øëé®®xYÒ\ª¿¸ƒT?¨Reƒ}HÜE€mÈÛz†Žpäq»Œ i)$ž­Â€Ø‚~Míj–†º÷VYá~$ÎCö"á¿óþÉâÍBÞ?^ø•ýSÄÁDVcåâY;”*ÄÍ"ƒð‚­5‘‘EzŸÖþËê–_áˬ"û˜X6lfÝÎ(À]öß@ z&ÙkªbˆÚ(@2‹×Ð’X DY"÷1b+ !«ƒ#g³ƒÖ%d³B'™÷J”âa\â10ƒ(¯¥PªçÚÍ)ðÖ"Iƒ+Îjôêá)œ*¼#+!XÄIBø© BÈ"e–ÿÂ4òyó_¡xˆÅ ÆŸÖÇ‚†@#]»m,Nß+Vcµ_d„ÓŠô\a^"}ê(/P?IÊ g®zûƒWjb=fK& œ€çSƒLL‡RaÙ žTðø²‚Â=Úwdð}0xü1î 4Ó½Øuè !±£Ieå¤I„×$â+:ç:Rb¶¥ÐX[aS.„罦 qÕmâÏb5áˆÝs›‹Š÷AU<&f5weÂNgß°$¿ÑV®»åÀ*÷Ýò\H'Àr €ZO%æ–Z iÉV€wz<óßÅ-ãŠF·làø˜¨æ¦îu]½˜S–Ο߼zád?¤þgÔ ~®D},ÈÁ Ö¨îÐÕcÓ\›H¿¤ ƒNA^õÎ …4n«~V&ªÕcSPfÏû*VëŠâ÷)u<Kc8ºÍ¡‰l»*ÑQŸÅ×®¨ µÿ‚þÎiò-¾1öÊtÔOë^yÒC€fZ_Dóì å=RÙ³oŸWÔ]ÌžkaA}žý”å…„¿´ñ"ÄUÝ¿ÿz¿ÑvJû/H ,o羞o³~¨Ó­sýq¬ß~Ä«ÖPs1t'h&ç y¹œ×Ål—¬A+æIÜw— \9—ÅL™ B¡S¸ H“ÊöLPs?4ó»»÷Iˆ¹@ã"—,“÷ÇD}tN`L¢é'—~¯@ÓµÀ¦W ñõ4d¼êh~Œ-¿¤”ª©ÑLMÌÔsMCÙžª©¡]7UsÞœ÷ªÚ èlãnÿdʨoW·ÌÀ%ed ÄDò¸«­Þu3)Vóe­Ç Àûià“Þ ¶Y&eÑ tÅ åÍã2ÕdÅã ã ±ØägâöäI57ÝäÉDÛzÚd« ,jÞåÁßTËγÒjî¯/£66ÖÁ™ÜF•ëy®T# .b; Ö"÷ëVŠ´kŠ˜yÓ£ä§6ÔW͘îH=âÂ^\_­é­{ÄË"oN2úëuÙ TYVÓlê|£õNª|•‚õ¸˜Jg1Õ°[Béê¼v€}Þe@‘WK’ –¡d5–£»w‡ ÚokÚ–åEû)ÒVˆi:tЧ‚Mq#€åÇ£]K©xnEO…0øè×Ðû¤¦2Y™dOJö;o='yÏ/oà)9aÇîIGGo¸€®4ô:/Sãb‡R!ÝŒ kbô‚,©Ï–õZWÿÞlGXÆëR0Cp&¶ƒ0Ó¬w“ŸáŬ•\“öJÅt3‚p Vaµµ‚ªá †ûùiæ5›}±\ô9@jÎèö%Ì.~‘%åWÁnÜ™$tÌés¯aYZ÷(u¬Í’À†tmÖê(}­¥rÔ]àçÅ•G¤´g‹+ç,Ò]J¿y¾bÚ&ðo¡Uë“¥Á$¦L.”˜ C©2Ü,1X‡_m$¹ƒÀØßKWÁµâ\«“Ú0‘ ëK¸¶Yj.ù…²çâ†f‹`vô£Î޾mäÌ$/B.}x¡R³C©º°YòØ  V›Üûb¢º2󽮚Kj+ÿq.n¾5qŠNó¶ÁšÐ—Q×R”‡ÓRK ilÅ^âM5¿ÚKÑëßú=y‹köjõ‹Ý,KèèºõR²"õv(ðÍøEµ­>¾ý¾¥Y¯¹ðt,ºoóM?ŒJ« ó³ñB™ 0ƒ(÷Ac֏ܿ:Ë”®ßëeôQqÓgR£dK¨د¾ÊS‹há#J.Öé)±œZ¾A¸Ÿ3ëçÖÜŽçV%¥jo~e„òÂð†’K×òêJÄcm¾ìV™2åùÇÊW):æ[dDŽf-¥Žuu£~0)ÃÑX½·êÜg†ê}„Ï»fɧւ‹È»÷×[ï­î¨û]B±iÿêl y.&.eÊÕ=Ø…h¬:”*ÀÍâR¡^æëKë -å™þ¨®¦æEœ—'›âB;euCsÁÙ*8zÎaŽ*ÃYÄú%‚lNZJE~3ÎRŸ»Ýwf[´!/5Òâý–K·í†¼Ó›.õ¥lÓl“S7⽨‹úʈw.f<ݦ7B„!È…Ta«à1ê:sñïaWrMÄ;ÙWŒvà/¶—UœX [²—ÓmW>CSnÓKÓ6*ýDc|p:6'5/[ç|m!3f½qùïœÙ’ÖNw¦ ”­}kÜq6ò¤6ûùöhSòß@心¾ž»_T?á3®æ«‰ùÖr^Þ‘xðk .p¦Ly>®²‚’9nªPµ”:Òµ;^1Þ¡ÅãÃÔ½~ss»«7/8ç3÷W3 ‘Èð>cgò–PE¸U` ß5þ¿èK^†q©¢Hßyòˆ–n Ö jðÑ­úûŸþ²Ô]Üð‚*O;)B‘îýf“ ~kfø/àBëeЧ/WË_)ÅÖ6–†°ÝôK4çajûß©_o× ó¹õ{¼¥p’¿°žäÝz“ ÿþÔ¡lõ å0ͼB婿¾2M¼BE±òUJ5!R†Ñóµ”Í~Á€£ßÖs “>«Û³ ýkT§Ö{Ô¼ÖÆ ÍB×ßÙúþ¢žyÁ–®½¹(˜pŠeS†Aê6{€*š® ˜ˆ†õf³fž~£ÞªÀ¼üi¶±Ír&’¹–Ú~×P6{›"°¾»¹´ýYÖâQÓþüˆÈëD€òB³}v:-Uðkyb‡Ñõö)»Ð“ÙðϵéM±îܦ7yÓJ›^ù\¤KŸPÃZÚXQµ©´V¾ñyWJõÙôÝ;ú@e!àá5• LÐùà ¥~½ñú”©$~ÖM*åÇxÆ/ZžE%Vvzzù CìWV‚ûËæÆ•µÑ×8£—¢S&<ç¸U¦üoðè„5-uúžaK©ã\°½g8«• ðÅ| iÊÉÓ¤ Ù… KQBÒ–B@mÄÅ98ÏÚÜÑ󴛚ø¡‘¡ œ>‰q63òB¾¼3ŸTÛP„O±~œÇ­|Á%éÇݧXýéìîvÓÞ™K<)œ ž™™ÇóLǤxÿ Ϋ|V8„ÊI>09ã'¦ZJ~hýOˆœ@uöx¿©¦ýÞ«ù³ûT z@ª9µÎt¬(ø”¹i,p”eà‡ƒ€|ab «¤éãLeù-…FºvƒÃË’Ì"œÔìâsÉ ÊëͶö’›VóvÅÙJiéØP—úàëÒ[JcëÒ½¥OWõ—ÞÙº‹Ko£,Û9ˆ—­’—EºA =ß…Õˆïendstream endobj 6 0 obj 4325 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 76 0 obj <> endobj 77 0 obj <> endobj 33 0 obj <>stream xœeŒ1 À0EwOáÜAÔ„¨ÇèZ:¥Cï¿ÔJ åƒ<ŸøÕ£äw4i¸ 0±¤É¼°pa¥Úê#¾œG1’Ñ5YZaÆU-Èã·žŽV¸Ö&» endstream endobj 68 0 obj <>stream xœmR»nÄ@ì÷+¨#âÍîg䥺ùÿ&³‰ÎgE'6c`w'!Mº©’›R‹Ñ‡aQ$ð<>ޝñMÁ.Õù \¿‘l]sתI /z"+VqÝGŠ&û|D…óì âU“«èŒeNÖ‰¸”§‘-K® c¤Êd+².g¬‘&Êdeν(=„ùt ¦›½¸›,4x‚Q•œNæè‰^9Wïùñ1J»ØóD '*ÓXRkÇîeÕÂæ`t¤s ílV­˜ƒn˰)„Ë™àNºY&‡:Å2ãÔ'pŒð0†%'äÓA‚]…R/rè´WÀª¦kËaKìÚØ²{Ujž+4ˆæk3ÔkK«‚Œ,Ò{`†Æ,–@l¿Ÿø1t:ì ¦SuKïŠwœaX,ÆqmÂòtÞ¦­WÈã‚î'ëaòr/Z.rOÎÿ˼Ϸñ>~Œ’  endstream endobj 60 0 obj <>stream xœ]»mƒ1 „{MÁ:Á7Å12ƒTv‘ý›œeøNIÝ©ªIΪÚÔZt×%,ú¦q{®_ ö±Š®Å×ñ\e>stream xœmS9ŽÜ@ ÌõŠŽ¼gø 2­ƒý²ÕÂŽ4 ŒXd7Y,²ÛlñÊÄ_å¬0^ä`b¿—qþ;>—SIÕ\ŽwA±išæ±E˜™f}ÆeäõÏxEo‹RÊšiDzYGQùv.òån`³qyR®óf%ó5bEưš“¬tzày”rRåí©N£Ž;AÛØçÒ@ÉóèA‰Mœ­“t¶¯iô·©OÐàk%’qÔîÔ»ÉïR¢Ýõ†ÏK¥¡‡ .Hnº<ø‡óPïÒªW¿’̆S:$8™Æ¼º&ÉçU8H]‡7d+PÓúaç˜Ê›§2„iÜJP¾¥…jcu_йmEÄôzu'Æz6ìÀƒŽ/ñ- Ô@ÍKBÑÛR$èW‡ýLª‘@/õÞ^7¤°%þœøϘÅl…#. ÖÔ})Ï´aG¹j”J¡ ¹qZó%þ˱5‰Éø» ÃNôæ™Ä4lUa-» X°'äÂf0ºòM¿lmï‡ Òx\`›OÃ1UWün8ìý9Å4ìejy@ÛF ™!Ý«æ 6 1¸µ¬0<,îkLÎ H.˜ùëMCqR9ì2Ç£ðfz—xy®µ¹_÷Çñ÷×ñûøзϑ endstream endobj 26 0 obj <>stream xœeQ9NA ÌûŽ‘Æò}<ƒ7 "Ú ø‚‡Ý^P'.U嶪A0B Ò )¼‰§2oç}}¡EHü$þÆStÑFi8,²ÑîËØ9áPeÅ4PC.88' ±rT¨ˆçÚnqtÖp,Ð’Âä =»ñÑÿHä¨#pèØ*hí ܆¢À”-Ó.eŠ>ôr±:ãš…Áö?4 ­k¬xâÛòô¡l™‰>V‘å²T„Ö¤h#¥ä ƒ™;Æd„̱Á¨u¨{\lù®Žkð\êš8VÊ•®AÕan†ä0,{áßõwF¤æƒGâ)y0W`ì-^W9ÿê¶>ßÖûúÛsd® endstream endobj 18 0 obj <>stream xœeT;n,G Ìç‚lþá3¬áè)ðý{wdá ¶Éá·ª(÷X¼t¯{s/«^®¼þ’‹‰_ð÷<^_×?Ki»¸ÇÏ7>â™”¶nÙ-”ëë2q!”¼%àqY¿.ÛiÔ‚v—è2¯MæëVN'ËåŒT½Õì<^—§ŸØ[[ƒbE˜cfÛ¢d+K¦ác¾®f{7þx„yÇwºpò¦øÙAD³iPh?ãˆT ¡åÞæhd‹$ÍvêX\& ßx\ÙM±1ÊB½T‹ó=6 E­}úZûŠÄ l«cí0+OYž}Ë*âŽ$•GÔÚ »÷Ú ‡œ!/CÏ÷n]F@}yÙ,þ˜Å~¸4O{åÌL=5ÀoÉÁË€éðôÂⱩ–JœZbº„}»Ñ!k{eoE儲$”±f³RÇÇÂ8Yü1ÀN$ºNgAE,‹>¼zÇ;†ð_Øžµ'öž‰¨ô‡gF3²Xb‘á–wS!ªäaÿÑ ZifM’øÑD tj½™ò‡B0®Sù$qÆLz‡ FzµÙf Á@–eo±?ÀÕÈŽøÏeÚ‡âÛIç>Þ. ˜'i3Hó£ø÷9ÍJ£#l o|;Æ×¥YÚyŽçëÛà!&0Ìu¡„O)jôi†YߢXA¶WT¦Ÿ) ým€Z­Â÷f>é:14êûaaøå[ȹ@hôó±KêPpq< ƒ†¥w ãé±}~·ƒ½C,kŒP5·"—••#¢¸Î==öëzÖ|<^9'4Zip*™ç’úüûy t×õ)qcµ~§âuýýÇõçõ/ß_j endstream endobj 10 0 obj <>stream xœmQ9rÃ0 ìù Ö)08ˆëyƒ2©ì"ÿo²”m%…¥XbÅʪ'Ï|ëtãù%ƒ‰ÞWpÜÇÏ4R·øã2tÙµ÷¡¬MµãÛ‹w%osÂÀ7ˆE9QR +MÈgJmʲ¢ž™Î$<3Iç1JÓú%D„ÂC‚±a̳½V>³c»àú™‹¬Å+ÁЦ¨…ÑBwgƉzaT`ž΀@a3ÛŠTSЩ£’˜²5§RüåP¸<ÉýB²ã¡IÍP:ÓãastÇ/Adë endstream endobj 52 0 obj <>stream xœmR9ŽÜ@ ÌõŠŽ Áûx†ß0 G»ÿŸ˜lF†½PÐb5É&«ÊÌ.÷õ ÊeäË×H}Óßõóü:~/62ÙÀßÿ}ÉÈIëá˜*ë†ÈÊ b}ª…@õ"Úˆ¡ D,R\Œ Ùœ´\-A§ƒ§B­çT¾¨ßÕn™h¡ â*B+x„jgòÄDÞû÷,‚݉8pfê—LA&[BcªÕtæi~€{Rs†™½ædêÉ:߬xºcEM¥ÏYVÓá'æÞa*NäÞÊš—ZZÉÀÝI+l‰7KS¡RÍübñ¢™FθOV^>stream xœM=nD1„{Ÿ‚:ňÌ1r†Rí¹ü¢hŸ,KãÏ0Œ(Ub²¹%N!M_²,Cæü‹ÇkýÃ6[]à®çÓ²7ìX½Vnø.'‘íé¹T<Ðwâ,…6òîFìCJi¤žˆ‹L±'iT#{jÃÐJ6͈ k®`ΚôX5ƒNâ÷œÖ–ãñ&"ì3¹É1jÒ’DÖ!jZ¨[:q‹‘wŸ4æû[sb…ŒæP‡^z§6\¨x¼v"¥ØF-“{ë¨ Ô_ï{“Ïõý±>×/KQ endstream endobj 34 0 obj <>stream xœ]S9ŽØ0 ìý Ö–à-êyƒƒt)òÿ&#­µ.¬¡†×Ê,r§UJ”.ôK/aQ\à;‡ûÏõ—œ-5}þ?ãREƒ§Ò˜•ƒ^‹ÊÔÁ“´á ŽNòÉÖ¤‘àßW´5x94yP¥ i¹ut³?àFäº"n¬¹øšYÛê=„q5Ûâ§e,ȼ˜³ö_†ûb>¸MQ˜nOKi ÏÅ(D0 Ôô‰Å'+”Kc«åaA¦íœèÉ™VÆúä›»q­x,Àà«V@ºÙcGøpG‹b§×/|_e±õ5!Us5yM–ä2¶æ:},-µæîÄu~júÌBÀ¥èû´îË[wåïüŽ%§'Wú Ö¤ð‰qÖÅ‹% ²îBsâF™W3j@´‹ÉÍ>stream xœeT9Ž$9 ôó²× x‰Ç3ö =X«×˜ÿ;Tee50h +IQd0TD.^iøâ+%Ö/¹˜XàÁßûãëÿë÷r²ÖðãøùC‰ªÉõù–0æõ}eÖ¦ÜÝ,›¸n[˜%h,‘¤†kg7éúºD¶4<¾“UE'â½Ï=ÝnãwMÃ}ÜÐÞF¨g»y2š” ²BÉØb÷·IjrkgLÍ ¨ ·'¿ê^¨QÈ.±7²4[R¡*7;Ūœ8 T œ²LŠ=éÌ< 5J8)m“`à$Rä ¯" ëjvDÑ®Çö´ýâèvXƒy–½QÂÔ”¦sœËÒ ·¾JȮᢑ‚ ÒÑX6h†hê|µ!S-ÃTD"d"Ý£&oÌ/ÖLAjxê¬&‘Õ‚ Wµ:±ŽÞîÖyò>Êù¾¬Û© ZB‡F*çû*cÈ!Ou+™Â`?+1æÚ£:àh´æ8¹ñÛ>Sf†N&ûҀ鞡pÉQB|:F+ñX އ™c«ópŒ»š“M«ö©9(OfgÕÞ#ë£Õסi@íˆ8XšQ&äÑ7(’31·pØÉ…-µÇþzX|{þæõã‘%íaú-ÇÛ¦÷ƒŸ˜É­ümL=¦ êÊíäÃ…:>”"ó’@#5»¢Ž Õ?˜ÆS§¬Ñ‚ú‚ð,a#¡7_fŽZrØl­,Külà•Ä–kFeûYŽÛ± sáYØ]#T@á™ö±pŒÇ(ãÁ„Y‘˜…É£ì7Ó݉ȶ@INß6 Žv5±ûxF°¹Á¹q³>ögFoÏßù¾þûçú÷ú;Ò endstream endobj 27 0 obj <>stream xœMO¹mCQ ëߪSº12ƒTßEöo"§ø° A$!Š fbÛæÝ2ô”Ã`¡·p×ù%ƒ´süŸóа^ð:Î í I[÷ë˜N!çÆÙP¥ SD.S2 ¡ðt”S…¢Èm£T‚t2Í\gzœVô‹84©Ý–›?YÔ[ØtiîÝ QŸAß!d'üÆ#Ũ÷³×Ç3×ùù:ßçiB8‹ endstream endobj 19 0 obj <>stream xœmR;nÅ0 Û} ÍbèkYÇèRtj‡Þ©ä yASd°E‡¢Lš}Mذ8"|PÃŽ”x~çfÿn? Cu.à¾ÏÃè=Ð”èæˆv`é<ùjæh)=K£àbl” 'uÃlúö¦ˆÖswA¢,°-Ùë˜æH`$NPŠDK™gÍâL% Lê&¢5ëÞ(>stream xœeS;’1ÌçÄ®ZŠ?è{†çrôøþ‰[ZÏî³]“ˆºéÉ,r£7UJ]”.ô]/aQ\ໟ×/r¶Ôô¼žq©!<”ÒÁ€de°Qx5kÓˆŠp‘-' sH«„;I]zטRV õ@Õ%ÆJR2%²PáÍ}ØÅhZœs¾€Ç•Ë“ë%ǧ“Ür§&Y¦q™×0x£MJñòãqZŒa)ËNÆ÷„q$^Üš~¥`Í‹\"¸ ",¼ˆ@­RŒ¡‡Ò(7ºƒFß’Šô£¨6”ÄSè‡f¾äÜúB©u /CQpò7kÕÙ†æÔVººŠ%¶J6‹G?‘ç•nÂÙI>䪞¦HàУJ> æ»F­>stream xœm‘;n1 D{Buø§xŒœ!A*»Èý›hØKd5ägÀ{LšLóÆ<Ýt¦¬ùɃ@¼?ö{ñ= !ñ z¼?™¹à>stream xœmŒK €0 D÷9EÖBCÚ|láWuáý7¡PPÂÀ›&fŽŒ)k¨U4_DñÈÀÄ9º¸û7 éZMÞ`æ(“r!õ†É¼bÇ9“j… vÈ.Ì_?6ýçO‡s H\!J endstream endobj 61 0 obj <>stream xœeS9ŽA Ì猼gø 2­ÿ?qµ´£]À ²‡MÖÁÌ"![ü¥8µ.ýÒKXüî—ÇŸë/‡ªÄ3ñý‡ª!ÎFºìJ_¡«FXU_ïÙ¡],=Ô1¬f´¶“ï§úÏÒãRåt2•aÒrck²QeEÞL¹ÉÓ‹sPp„ù²ymž‚”LŽ¥î¥i®DEå6ËÆ“Ïîä š,LC*ÜJ.çøq­¦SR1|Ñ´€¸NÊY¿ÅãÎÅ;cŠP!a8m‚=CÙ÷<+zc‹4¼4xè…B¥²^®}Ç+Ýš3Þ™È,î<õ)ìä;Ë‹ ïq.T`$a8@]7 •«>n°sÐ_1†Â‡G ©|N¡î zÞêà+tÕ3{º„:A=RvžwÜBiÎ œ€q"®"q“aÅW¦ŠË>cPâGBÔÒà_ì`ÓØy± ² áð¤vPÞ³«KÎÉß±öÌ“…¯ 0~œXb êãÆ6Ä;¥\¯ ~^äÃϧ >í²ÇãˆËöÿSì[Ä€ô#¢c]’nÿaa+{Š Ïf–_ÂËeqht‡‡±.q\ƒصÃX] 3ß|{aèÂrnòÀûÞÏÇûqýþqý¼þHâÇQ endstream endobj 48 0 obj <>stream xœeS;®1 ì÷ªSüSï’Æ¼žWYõ~Ç;u×wl' TÞÔKX³hb.%[KD™)—à¦ꤻ‘pó©ÐŒ¢Xâ{o •©Ðm=ú4ØÖñK7¬Î0OÜÁÖ4ÚÇl'†ÁäH C+,žŽa⃠ô¿q$…ÇŒØTG"HRc$+=¦d±c^gÏSÑa›ð­  z¿ˆ·;—¡Àã?>Ìß'ßµx^¿\?¯³Þºb endstream endobj 35 0 obj <>stream xœeAB1D÷=k„Jé1<ƒÆÕwáý7RMìOÜ4ÓÇ0@!¡ˆz| ÝÑ„´ ?q{¶9ÛÔð8ë*¢‹r_Þ­VéG³.süý‡<¬Hªƒ%HtÖ"´`;‘0MÕ劕^è¯Õ+!My~µO 6¥b©§‰9¹ŽÝdù÷îG{\Úµ½µ²59 endstream endobj 28 0 obj <>stream xœMP;Qì9µßcx†5V»…÷odM\ !a>‰Œ6Þ˜ø`bfìl¼Ðhµ†}ˆÿx’aÒTkÀ-Ä!†‹wP[NÕŽH&u4 =õ‡é*êÄå<•‰©eÔŠRĊ飰Jv‘n9½ÝS‹Ü±ØGÁFÁ… ˘Éô±hIÐo qf¥ß’:£Î_ìÇìð¼ÁÞB8V endstream endobj 20 0 obj <>stream xœeŒ± Ã0 {NÁ:ñ¤$J#3$H¥Þ¿±¶ 5çVƒ#tdÑà·Ê?v–×—6N¢-£üÁÜÇÓš¹”`MjU‚'â àN%¼Šåõ>}q\“/i†¸O†•à¶íôyГvÁ–-f endstream endobj 66 0 obj <>stream xœMP;nC1 Û} ÍÑú£gHÐéeèý—ÊoH:)š¤í"$”s¬•MO,aþ=<^ë—”Q&~ÿçYn4—Å€× T²4•es&]+¼Ùµ¡w-Œw¼qÄÎÙo‹b&%‚- È‘¥™²úàa¸)Ó“m$,¸è± ŒBe®R…žÐ2]©ÊqGhŽ|lìS.ãŸoܶÁc"ÎÚ·ÂJÇöVoi°ßs¥‚e“‹OªŸêåà0 Ób=Q/9åþóQ×úùZß묠J endstream endobj 53 0 obj <>stream xœmŒ1 Ä@E{Oa½…èºúg‘3$l•¹'&°A÷ŸòÓŒ•ZÑŒaÊ‹‘Šv]sÁ¼ÑÎñˆæ§¸s ATl骼R(\2ÿò€´os÷&ñ`úÿè^é÷¢‰AÕ"Ü endstream endobj 42 0 obj <>stream xœeS9ŽA ÌçŒ ,ѼÉgø c8ZþâêÑjV°¡ ›œ&«XEE$-Ê  *Iú%Çâ%ø€ßër>Ž?äl£éWâýŽfžûíã¯á Ü?ﻤ­›·±ü./Åùy¸i°+‰„ …M‰DW'eIpõŸG¹;kÝ™-VC‡Y‰]]ì;NÛXç1®ÎȘá)Í”\kPê8­/N˸ñ^VÍÂm:®Ï¨S4éÝ&p¡öl® òÀLªl ¢RŽ.ÎúŽW5[\ÏŒ¯Ð'yU†zË%ÇRtëæœ=ĺBŸ ê%Æ.ô¡±pe¢aéwâ>stream xœe‹± €0C÷ûŠÌårמõ3üÅ©þÿb)¢„@IÜ 7 \­Û±S4){Ñõ„í” LµÔeð+öг1å²DàÅáªh°Éþ<öíóorL²Ê $æû endstream endobj 62 0 obj <>stream xœe9Ž1 s½‚±‚÷ñ ¿ÁÆF³Áþ?Yj€1l¤V7Y:JÒöä ÉO^„Äã̸Äãwýa˜qžÆ»ž½7å%Ù'FcY¤ Ë›SÊŒÖ`N„Û }–Z&CU V‚$+RA“)zƒÈ Já±Zر|*21¦ÂÚ1Ôgmè Æ°hCŠi`õð‹q(Ñמ¶25ÂøÐ,Õ9äc]ì)£ç©ÕÓPxž§'ñrNÎõEÇú¹­ûú wI endstream endobj 36 0 obj <>stream xœeŽ;1 DûœÂ5…剉s ΢Z îß´ìJÈ’õfü%»˜tŸ‰lÒAy¢˜¦3ã€Ç»|$ÔGe,ãʳšû®“A7“­xÖЖ:+R}À0N:§Jƒk@GªÅ^oð!«³Öáê‹™ Ÿkj¼jŒ¦‰‹³f/oåu+÷òÿf3ª endstream endobj 29 0 obj <>stream xœmUKŽ,GÜ÷)rmé¡äŸÃgË«y ßã€êªKV/ª È„ Ú=Ö^ëór¶åº×_üÚ´ð»_¾~¿þYJâì:ŽŸïøÈbbT‹+邃÷bÞIœ‹·yP,+¡¾ÊU©báÊ´õõ:ÁL*¨äíJ¯ ƒC7nYá¾ gÇ×˲’û¸ÔÌU“ï²%.L§V‰PXaG†Bótž§B9±‰ë±MX© {u„% q5î) YèY¨:YvxUÑŽuÂõÎ6b_ºQaÊÊÚÓ¯—zuËŽ¤  xülòOÞ“åtЧ]ë:‰ä{®¬m6©YW1ND§(+Âј9lUZD’ $àˆ¹ †Çc'Ñ„[úùØGœòç .:ý ÈÜWFKP²XÑ®Žß‰ŠÐ<Í<Ĩ…/VlŸ¾ñh8Éyì¯Wn´Ç?ž`Xvfwö°*áGÉè…¥ *Yfu1,lóÍ0ø5ÜôS#Ýï11”ž(Vp´SRž%ItÔâR…Üœá9¢ˆ@)C>4ÝHg Ú¸ÁÓbÔHÒýúý¹©kQmPCôeu×.†.›Ñô>sŸeÙ§gŒ!œ¼½‘3³ªz ;ΰ©Ã'4z¨¥Å¦ðxCâ._µyrðÚÀ²º!Y3vÀEÛ –›½¬óaû¶·¤ '·'"gú¾¡¨/l´Ê1¹gعK­”K"—´ô¢Ó Ã_-ê‘¿áï«ÙÀ†M‰lž!è¨Ûeg bbÉÄ6ØGÓYXDrí´«ã÷jº'(Q,aƒBË£É{öAïgdMù~r†;‚g¨Ýð<ØW&³ÈZµ> 1¬X¨4xž^:à ‚f¹G“ÐsKÆ9&…VM1–:e?¶ u5 ·œÎ‰{ãH¹ùº·ê½º°qÔл7Ïì¥ÏÊJø›‘·‰eŽ^ή|{ û¦s¿Ï£¡3XˆšúÙr꺋¡u§¾¯áÌžjÓý^iP»{ëæ™ò‹-?Ü«þ±±ð—sþ‡Ïï×ß¼þ|ý äËX] endstream endobj 21 0 obj <>stream xœmR;nC1 Û} Ï},K:FÏ¢S:ôþK)÷µIây"ÍGItÜ÷ä¹ûgeL7žo2˜XÀàù)nãs©‹Û!žk*kQ¶öQ‹×ŠykyѲÿÛé”`|k’ôW‹QÌ0)Ú:eó,7£Úº aŒKëÂ"E­qò)*‘TðŠðÐkñv·sb^Fð[x+`ø­¥«õË€áh|ø胙 ÓÊJ=o…n­/Žõœ_®“»Û…+…)ü[Ææ¢Bò§®õb=1ÂQÝ »­Î›wÚg“‹‡·*d«Áöo:&˜<§ïÞÉvÛ'Mã3·EäSw¤²Ьd²üÅ·)Çú"vÔ¦xÄŠ{F´Ôô¾ÛpÜ4ñê1¥/n°ö0aö„·‘'¾øa:–Çßë>Þ_ÆëøŽÇ…v endstream endobj 73 0 obj <>stream xœUT9’ÜH ôù ØŠWáx†ÞÐk­Œý¿£{º{4H€U82È,ò¤Ÿj”ÚTÖô^¢øçõñø}ýOÁ‘iy;¾ãgK²DÐ1Mú}ù¤³:Ç…‘á¿+4Œµ(âN§8fÜÈ^ÊÓtT‚çk =®“],C–ÜJ%\A!|Œj㌾íÇÕчû¼=c9|p"‘Yœf«ì"—îÍ¡âläÇ‹3H%ÕPÈ1NýØyŠõyãé9¢‡½q_tü(ÛÞ·ÂA:©Ãc÷y9Ž §»9iòÈJE”C³¯*J×X¨ÑCÉptÍ~¨ú4Sfüy·{‘æ½…&S…ûþð“²ÍÖ®n²Î³úLnTÔ-›sÀ‹‘:*ÓÙNâp}lm»;xÙ–P‡nûH§îÑɤQüûw|[Æe=¸n¢\¸„ ==Ž6| 5@2 HRäáDºé¶À<­Ç5À×¾l•‚éˆÖà ^-Àw ¢t«ÞR¤¢BÑ( ÑlÀ¾›PP¼—®U)@P—‚hApÆMƒìM,­ˆ«µl¾ªÁágó]ƒ€9<¡Û©+ƒ2ЛûqåŠÔüíy "(p£˜ÞðÒJ0¡ïî"ªo"m53ßl¡‹ÃÛ£ 1½#èêV+H© ÓŠ ãà;£ØÐ1ô .‡â™8Y­Åï ðƒâYÝ‘e'¦>ACݘ6y2£½ü÷¤ÑK"Õˆs-~Ú+×^+Ã'>¯€,ý9jOÕÞæ·Iýò p’0[¬í$ú‡´—‚·¨¹‹¹§Y·^ ëlÕ‹ÆH`A¡Ó»¤Ù‡å™HS¹n[Æ~bcqbc½FòíX`'—‰·ËÛún[ XÆ:TÕ{w¡ª<÷Ö2Áøôr µâaØ£õïÕû¸þýqýºþá7 endstream endobj 69 0 obj <>stream xœ32·P0P0F†Æ æ†f )†\z†@ „1’s¹ LôŒ-ÌLÀÈl ¤¡©‰…žˆ‹`š(äp™X˜XêaðAl„¾®4-®@.Р endstream endobj 54 0 obj <>stream xœmT»’Iôç+ʾˆ­àMñ÷ «µ2ôÿÎ%t÷¬Bq3F7TSI‚{,Zëƒy¹Èr¥õƒ_´‰q€ÿóòùëõ{ég×qüùŽCWç­²˜)yçúõÂaî4x2kóúzqÅÙºX•cŸ%I± !fnÑtÙt;—àüóeY×^J»V¨ú¶\J´ŽIì¬Ûú|1)uÜe#-çvXä¸AÂh#—‡è¶þZñº¿³± TÁx"Î<³m-eäF„•uݬâÞ8œq#S•ýip×ÑŽf¶½zX·ŸwˆdmËñ¶Y†Š¾ÂPIÝãÊI…—õ†7ð¹¨ï¢Îss.Ù uWBºä¤ÜÚyÙñ­6-àuè`cæhIõ¹Ú8Oÿ¶Óu“ ÙÁ"åðªÃó¥L³ˆyw}Èì ‡Ï|ññ¸R¹vÈúP`AëÐÞšìoG+@c‹~»ÔÌ6d‡4h©,qá} ùmƒÍa *3Èâ€19£È͹¤4ËoÛ4æÙØ/‹×¦èË6_ öêÊ:2bSc>Ó¿4“Ö<çÏY5åªëHóhL ¢ä;bThÅ–›Í E97DŒ,qËíà‡¯J+̘ãîÑaƒú‘4iµfÇí9èvŽnÏÝ­VäEàþËâ–#òªºX“s<ûpÃJIõDÞÆç 2¼Ú|{"ëšI)…ÆÐÑ2«™: òÐÀ¢ÔSåŽ Çþaˆ£gæï5ò *Çb,2Ÿ.-¯™Œ¾žµÄ÷ ='OÿÂÎ0.´€zBp[E×x×ä'[S¨Ih;ä¡g.|ìg;!âñð™f:`ìãX¬uEV̦Sð€Ï˰¢èýÐ< ¨ -t*<…yì§œµ@ý¼¡~ìÇž‹kªö¦I¹¥È&Ü„uÆö€C†˜R: ¬÷6Ô36ûç™{ì8õùÿtæëõóŸ×¿¯ÿê6* endstream endobj 49 0 obj <>stream xœe;1 DûœÂ5…åolƒ3€¨–‚û78Hd‘h’™—‘5Ž¬Ã¢À¹à΃Ûâö/Pä4òøÕý8%‘ueO-"…p ¦*A6VÁjRQ‰iM(2”‘dyM ˆtÆiÛß‹æÜä8ÉÔÞ¢½¹1Ê¿7³•÷`Ç*0fC™MŒ¤7´Ÿ¦.Ö3 D{6%L£‰Õ ;!¼-Ç×v+'SÜàØ`8?å˸Ž7'ƒFG endstream endobj 14 0 obj <>stream xœ32·P0€ÃC.=CÆHÎå€ÒÉ endstream endobj 43 0 obj <>stream xœeŽAB1D÷œ‚µ ´í?†gøÆÕwáý76UûM ™y VŠº6Ž–,Úzƒ¸9õûCžJ«[INð«ÇÙiÓžYÝõö±<IëD·qð$+ÁDÿü{»‚ÍVʳb fn}tÈý"Wyª&- endstream endobj 37 0 obj <>stream xœ]R;rÝ0 ìu Ô™€ÇÈ”qe¹“¥Þ'wÔr ìG)BBZtš,Š¥”ºè¢´ïÞ‡ëëøKÎ:!yßϸÔͦt–ÅâE@ÊÁ :½‡¢ÏÃÌOÆ*ërîÙÀ %7'ã\‡+×$\Œ7£k¸!»zqî9žx’’Å‘€¼„“Ò}±BœK7OPæC¬Ýöoº‡ h¥’R,ÖN,j‚a‡¡J,ôÁ£JÑmíLhÂVª„¡µ1xT5 êåVõà äþyxL±úû» Ä©&ŠË´ks;e厛ºA!U¼ŒFý ½\]ÇxÆÎ<1* °ØÛîÒ–ÛZo¯×¡Å>stream xœe‹; €@ ûœ"µEÈo£{ ϰbµÞ¿1 ²ˆÂÌKžª"cx.)гnL,™ä¼Ð8Ñɪ>¿üá<Š— º¤ –0fì`Læ?¿yô:ì¬p̦ endstream endobj 74 0 obj <>stream xœ32·P0P07F– æ†f )†\z†@ „1’s¹ LôŒ-ÌLÀÈl ¤¡™±¥ž!H-‚ ¤ r¸LÌMÍõ,Í0ø 6B_WšW Ò*³ endstream endobj 70 0 obj <>stream xœ]Ž;1D{ŸÂu Äö9C¢T›"÷o‚-íæ#KÖ›;U;÷À¼\ ¼ßјxš:ÜžíÕ<,Çß\C $ETø0†VÝÞº‘Ø™s“êÍnAˆ2€$œpeþKóY<̪9?¾·Ç¥]Û/ž4› endstream endobj 63 0 obj <>stream xœe’9ŽÜ@ Eó:EÅîË1|†6õ¾b²»K` @¨ÿÉâò$÷ظc^κƒ|ÿ¡…€ÔN?çðøZÿ¶‚»¾ŒïçRCI‹ûL.ˆû¹"«ÀòÒYÞÚ>º: D­XbJŽ"EðýXDsQ7QJŸ-¡6±¤B»&îïl®ŽtC䉈¸@Ï¢9U$P'.QÖºo(²@O#D<Êh]ëª}4YÆ»Çq¦–a÷($ì œ>½-¡ôµC…ö *ê³£7qÅ|,©.Ôló¼œÁw£¾ý4‰ùT_+‰X.ç¹Ê:§ørµ´Íè}´Ñ¦wÿj€ò×–ïJÑ©~Æ'´†I·&£ù¶ 1÷†Ü‘(¨A‘ÔÇD'ÿÝó #tyå)úàÁ´é£Ì °‚æ·PéŽÚ¨{—´Ñ®ý#|y#<ÎOTÏõ÷×ú½þc´“b endstream endobj 57 0 obj <>stream xœUS;rAÌ÷Ä®5ü™cø ër$¾âfßî³T ´ÀÐ4ݼR¥E^ô!BYB¥M¿äX¼ü=çŸã/9{¦æ•øú¢H÷f¡ènNB"–7ù^Ænôyˆ‡ +™D ¬Xyqt4[“hbº¬…dÐ ÈX=õH.“«ˆ?Ô“u µ•ñþŸGª‚×—'îÕ¼+.–=³EYð÷”BËr2‹PL es õw\ݦÓqgæ?V£üW`÷÷ _=õóðX‹D¤@¤4ñK´µ¨#l8¾¢þ…{Å¢"ÀŠ’Â®bæ>šø4å¼ö•£eǾò©‹ ]]ÎÃaºd²/M«Šg˶„;Ÿ£2T­y“1<Ñ aHæl¹§ˆ0fDÞ»ƒ÷pQ`m…‚NÞ [öˆÿŽÏ£¤ëRäÎ<6‰­Ý˜Ę ¨K±‚Cj°’ÜpU>dNÂßqƒë˜òdr-Žùð4çŠ÷+X²í:•¥¼’p% —/éÜN:/ K螥î[KmÞñ$ö–æF½àÞ³ Íĸü’ú%$œÏ|®e®ùûæ<~ÿ8~ÿK®W endstream endobj 55 0 obj <>stream xœmR9n1 ìõ Õ)âõŒ¼ÁA*»Èÿ›Œ¯½€ƒÎp†‡´f6yŠâ°Ð™ZóK |Gp¹Ÿ¹hEh܉sŒ¤FQ”L‘P&Ÿ·!L¨~0×£«òÌØò€FÙ›ª§,I¥žªáÆŽüeè*'\¼H¦øÞØ­PÒ–: ^®¤0X‡û”ep®È&‡#ŒyºC–þ@—‹<8ձ涢KÎvRÝÅ;!ÎäÝC,£1ìu¤rSl¦JÀDZÓÚ¸¶üe7º / Tvç$”…×›¹iQa2 ÃJ[/šdk_–Æq†0·ƒ¹ÎX—Æý²žÌó Òгã¦Ë úõézgøôdçø¥<~ƒwæÕñûc|Ž_ê]y§ endstream endobj 23 0 obj <>stream xœm=BA„{NAmA˜eÁåžAcµÞ¿‘˜ìÓd_hf>2üøè¬ìÁ@°·Á>’ E5ª–¸¿èÍ&ó¯ÿ“ÕŠÄ[ƒ ’d„u1žï)Wß4µ¦Y™¦Z>\ºïþH  yBŽÅ+´uýÜþ™ô¼Ð>¢·:˜ endstream endobj 15 0 obj <>stream xœeR9ŽÜ0Ìù Æ}Ïðd8ZþââxG²± vû.FäæççÝ»$÷YL,`ð½ë×ú½l4ýEükãR‚•âø>¶¤1ïeÁA]_1»wIòÞ"!‡‰d&VΠÜÅæ7¼Võ ÅãÐY†C¤Ûà8&Bl'Ÿ+Üg¼ÉϽ"qžÊš'!—êëLä;‰ñ«€ð”Òì E$xvj×Ë{¨OÀ'qä#ñÿìOLJh93wzC%ÅèSà1CVœ\«Ñ¹ÜÝ•MS9°E“B=u*½ñµ’M©&<±ž9:ÛÇ¡lïéf’BÄ[‰DW±CIm¡ìmXø{þ *NÒØn,“¼ö[Õ2ô=}ãt(dùŠøËœµ<ïåcýü¶¾¯?Ðö|¡ endstream endobj 50 0 obj <>stream xœ]P9nEAëçÔ‘ÂÃ~ŒœáE©~ŠÜ¿ £¬Š¦1ŒÇ;‹„Jé‚PM¡,aÁ0ó¾Áý¶Þ oÿlüÅCf¤sÄ¿¸b«Ñc!t„ÿ×òƒ.…g<:x yˆsm‚ùˆ®ôj¶M÷‚š]mœI@Øæž?@´ySï],3»WÂk˜«ª”%çn%„½¦…VÆl5’{d¾f!Ñvv<³XqCËŽÓ.>‰¡àÇí6×it%ŸPï?‰<ÖëÓz^ÁEþ endstream endobj 44 0 obj <>stream xœ]R;n%1 ë} × D¬¯‘3Ìb»¹³´gœ÷L1–,JééÜûžÞ]¹ÿ•ÆÄëß9\_í»+ùȸïg\Š¨Ò”žE•ý•©2Cm3§´.ÌS¨úà˜¤³£NñÕFL%FÅp²®ÁL’]Ô4hvËÉ”…NL„O¿e4zÏ;#í•<6≯&Z?`Œ^b£rÅ&¹ïG™P,„;øêâ9<%”¹O/!Ï'Jµæ zÇ.>Ä=´«JP®Î º€ÍÝ OtÔK2áªUð¡}F€½ ‰¾WKØÞRÁÓÖd¸X zÚ¡ )4¸šÆ¨­¼@7Xuô=ÊŸ¿ŽàEç1Èl Bëc WTÿí/´Ý{¤Ú žpüd6À-7yË‹qˆÃÓµìÜ]- ë°Vˆ•)7ËÒ½ÑÙþÄW›9VáI;¬Êƒ<#y€½Qi?d(CŸ‚ÜêOt]n¬¸Ï V·; šíWzwÈ~I÷x­/èa'R6<ôO ïžOæì…^#ކ‡ÄøümÁÕþýiŸí?%y»ì endstream endobj 31 0 obj <>stream xœ]R;N1 ìs ×HXþÇ9gXD÷o˜<Ø}+”Æv<žñ$™EBôªJÑA©‹Þu‹âç Ž¯ñMÎÚ!ù(Üc\ªÍTÞ½ÏØ¼‹}Õ(gL¯dצ5½Xœ^=’cͬæÕÏÂ1*šo%´ç‚Ôœ;ô$FÚÍá èQޤtç…<ÌAH*’{ÍÍ‹ ˜p +)žA¦ž\ É,M6Á¥¦²$/Už¹à»BW™¨l±–3œ<óÒr=¿-1§sÿO§e@ô³ ˜ígxÌoxVH— 7øÝ#UÛ¼$fTì•\¢8‚J-oÖ¼Ykºà\ïävIoÅh¿ÒcôJpΫ²¦—]T"Ü(Tµ…ç%BµÚ±Å)š×ä½¾Dî5Ô¬…RàZc¢…Ox˜ð¸ÿ²ó“ìÞŸjšÈÍÐ3Ä×·ü/ãmümð endstream endobj 75 0 obj <>stream xœeŒA B1 D÷9EÖ.ÂLÚŸ´Çð _\Õ…÷ßXD±RaòÂ<ϦÐô¹]“¡7 œdÎ7œyjµÒ=ê¬y>¥©,Íš.$  Cji=öûÓ›ãG°úØx˜¯†à¯;ä~‘«¼»-E endstream endobj 71 0 obj <>stream xœmR9ŽA ÌçA6ïgø c8Ú üÿÄÅY´0 Vóª*†ÈâµÒ±T0©š‹P‚DÊ$6#ržÇSÃû´o‰›‘¨²þ5á<~ÿ8~—#½5 endstream endobj 58 0 obj <>stream xœ]SK®Ü@Üû¬#=Ä¿á9ƒ£¬’Eî¿Iµßë™(lhš*ªpf‘P}¨Rúþ ýÐKXø—û÷õ‡œ-W­'ñï;µ¤“•TM‚Ñ Ý‰Z\ô ™Õû 5•¼ðŒ<{ÍŽe²¹IÍe8è¾Î]µåÃFÓf<»b2h$“³¾¢ûjQå>ñÒ4öD”½Ñ*½£´ûʱà@7A[ÊÒÅ©4¸P¯¸1)ÇF8™ò ¶ Ó!Eœ³QÃ2Ì‹µpci;K’Ð%¨Pàì‚ÊjÕâÉW|_3š,ë•QL¹ùºu<Óª´2n,°„;PIs*Â$9vŧò‡•šB¨¡ÂM®ÍJËE(CŒ+¶3ÙK ¯ÇÌžTSÐ! ‹ y«?ÎA6Ë£ªÂ¸úòîÆURãd½÷U‰î­ïT,[Œ­ójö¼c$ˆ¡ ô~F› àºCÛ^6Ö`aMQýO¼&…c=¼>3Gµ¯‡î !Çáp€D \tHjôwÌ1±­<ñ^ʱæw…ZÔ^ˆˆ%EoŒ²×"ª<‹¦±°ðX½PX·+s6ù1ßuÿ}:÷õóÛõýú _O±h endstream endobj 38 0 obj <>stream xœmQ9n1 ìõ Õ)Þ¤ž‘7¬‘Ê)òÿ&#ÇÞ°±À™/­ž<ž‰ÍÒžL,`ð=‚ã{üL'ÏÔ¼Ï1~Êò¢åÿ±0WPÌëP ‘wŒ¤ò9³^íZÏŒ¥é„KÐvqYA6U|‘pTÎc¨iSÔ”àÅpÐLô­À! ½IÔžP¢uQ#ÃR Æ’âëjäIóŒí^vGLjbTë;N³$´–( «bn‚s2êÌ唸­„à:R—P¢˜»#˜±M®{[E„RʼnáË›TNÆCw-XB)è¶[Ýëà¸ãX85Z¶¶‚Â{jd ÙÄ`'Ö,ý[Àƒ9Ï{Iú–9Ïyf½0üt¶}ŒÏñ e‚y endstream endobj 24 0 obj <>stream xœ]U;®9 ÌûŠxÿá3ŒáÈöþÉÕ­~ G#RÍ_UQãDƒ†òøb<\iüä‹&ñè«÷ðùsý;tгëvüÿŒËrZ1,#f­ñçҚÞ¬ñûÒt™´†QÑTüò’6T‚†-/¤’Ód|.wõ¹àa„-áBƒWÆ$™N“ëµ?תµ³Oé$A¸ìªLÎkò`›ìˆ`f’é˜%E³1Ká©òX¦•³dݶ﬈U@0ÜÒ¦²Ó´áËtv;HmÈ3‚VMÓQŠê±F¥(—É4Ñ€í|ån,ÅÑ'ø°Ú#f¬y[ä9³#¤|ç@ª ±hìÈU¨µrpÁFi›¹gp8 ¨‘ Û¬K'Xc*Ƙß6'ìÑñˆ‰õ®»8+%Ío´¥ÁV7ňpܘ!BÂȰ³X×ÝsÉþþè„Ý©<ª`O‹¶Kh¦Ã®à™ Tø=^pæ“»£@½¼Û‚*l5·Y^>m²c×QRSÓ½-@¹4Z'-[‚Çü\h™+õì¹Ïz8µ¼›×[Š`Ù°U7¦âÙ­ۄƹ9<w¿±"Õ±CºWë–7ƒæÞØP¦`÷‹^2\׳¯ŠKÝ|Ëý¬ÔaœHâ} ¥{±Ðse·òÀ¡ÌJ½w•Óʳ6 Elµ­[­ ÍíΘaÒ¨µW°‰¯€L±Îq¿-v÷,5^Û"Ïî=žó "5ÝR²±xø=ÏU7u4$7}B«u‘Úhôsõ<_„UµÜÚ‚Î㋚&çMüRÄ(TtÀzßÛôº}Ø6‡X‘Ÿ÷PÒ{¹G)ÚÀqbÕ³•«;+Ô²‡ýûÏàsýúçúqý½P;O endstream endobj 64 0 obj <>stream xœeŽ; 1 D{Bu ¡Ÿeù9CBªM±÷oV8Ä1x3šuUdìQ+¼@ŸL,åÔ|áñ†lhø4v®£tc’^âÇÆŒذ$—¥E}$)ZãFÙ7g%¼Å üÓŸ¶…PL¶d¡¬ž¦Í™ÉõÓ¯ÜáA-n endstream endobj 45 0 obj <>stream xœ]“;ŽA Dó9c"šò{Ž¼ïŸ¸ZÚi, DÖ4Ù|Å™¡E.ô@^T’ô[®Åk?À×?r¶Ñô—ð=ÆCQåBòyM¤°â¿'–²ÅR[Y¹8…dy± µN4®O¶ªç¤Zœáô¼JƸƒ8¸Rœ2Ì8“‹]†¢ËØ¿ ÏËc£üHfj\NŠ£Òø·Ä$–Á£¨—ר éÝ}p‰5Ù´ñøÆ¸p%Î 5 ûÄeCãàJCwnÛœƒ¦Õ›X$\É=u»+º0A¢±4ÏœÜj—ÒVÛ’]/x°'‚/ûMŽ÷D/!mmïïüÌwìhÊa”’{°µ[ñƸäZÃ=‡ß>stream xœe‘;nQEû· êHƒø–‘5L”Ê.²ÿ&ÌØcGŠ^ó¸€à\T‚ajNH)øâEH<™y×g¿¯0´‰SøûŸ¤djÂf‘®p_¬áXoå¶Ô51 6æ Œ 5L‘RÌ~ÅžÝHûº”ä0,³,$r% ;tèJ{thõŒ-`j«™¯C¡ÀBÄÈ Ì‚2±’òÔKÉÈǺ5›hÌlé>ÕO%:ŠŸ˜ø¶Œ¹1'o6\Î’¨6£b|¼ ˜²ezC½Èæñ£"¬±ÒE‘ê¿á/EK¦“aüîsõG›œçj5þÆÃ®´À¹§œvhùÁ¼©òlðïLûúþXŸë}¸d€ endstream endobj 16 0 obj <>stream xœ]SKŽÝ@Üû¬# âÛÀ1r†e5Yäþ›TÛÏÎhäE›¢ª3 yч*¥&¥ ýÒCX|÷ÏëÏñ—œ —ü¾þé6Q\Hâ‹%HV,Š8‚>X'+ù¾3M‘í:cÔ­Mk*½Sz1c[a¤’5°?Ü›]Œº<Øã?ð:–Ï™û"Mx‚| #Å×IG««v™Ö`tâ:Í1¤êÅc”fHñ˜Õa¹Þøh£Ÿ;Ú’AÇO¢ô»KD¦îF3—òÐòµØ°\„:[Ì. é¥ÄÐæe«!øK¬zKï3T÷xq?uÙ¦a«d{²Ñ;"d…nüm×ÌYuG¼‘Ö‹V ¢J\Øs¾mëæÞü}$¹óA> Ç-š„G†"FXqjƒ{@H8¥É1“½+çYdŽÑù¢ò5Voò¶_Ç”^C¼Åñ^¾©‰U D ‹à9Å¢³þG\g±ôpVü.P,E &bV:E\Íù ,›@›ÜÓ¾³ªvÈÞÑ»êoŠí!Â[iÂÐ/q¼ÅÐ 3wû"Žòö~¬¯7|å~.uÍÆÚâ’ÓÒ“ª\:äÕ³Yöfñ½³×ñûÇñóø‡èÁ endstream endobj 8 0 obj <>stream xœeT9ŽÜ@ Ìõ żgø c8š üÿÄÅž‘v C€$R<ºŠE¥ÈIg*n{rœ¿ø E ®ëåñuü9miKØv|¾ã#Gðª<9”èü:ŠSV^öóh³\¡o›É$–Ãå…(&”â`£çã`Nâ5B\,’4ñžÝK`gîïnžÈG† â‹3Ç<5 w‹ Õx’èä5Ž9ñ¦¶ãÕEГÍP¹ÊÇÛB7o›ÔŽŽÉ¤DÔÖª˜^D¨ÀûL–‰·»4ÕY½Ü§²øYá´Ê^€5ÏR·U¨£ì>³`“ß'JtêÉp²‰«ZŒú®Âèçµ¼nÛRýáíÐ.]=ô¡É©â¾ld'€ˆ[¯|A6ÕZ†øï#~ÐUq„E/’mÓ•0VÁœǪÂ0לq²®’=6 Aš/ŸŒV¸Qc æ#ÎÇ-®Ò¿B{Ö!«†~ïõ×íéâù<’õãÃSF´LnO3™ÁV[b[ëâÉ(Zü:)¡¬žÝ¨…öäœ{ú¬´Ÿ¥1ê {º5Ì3eÊØ€ÙF¯ß¼íÄL7Õ—g«â+ÓÔÔ.Þ[Ð>*Þ´QÍ).ÕwÍ`Ô|4Òá$ßê~­˜d#F¼þ‹µ($hÞöã?^¿™NÆFä'Ó’è<¼‚Žeüái†¨äÛÓmµ,`ƒ‰éˆ GÀ) Ä‹éÄ«žÒ`4gá6âQÐÔ a~ý+löN TŽê¹i&aãÞ£Ë.ìMî½y{\AÞì%'V2OÏ’=3ÁøcT}Œ& B×uç¨:’¡„º¥•Ø{ûÐ ~b<óš/s¤Óø²ÿqû<~ÿ8~n‡à endstream endobj 59 0 obj <>stream xœUR9ŽA Ì猼gø Z8’ÿ?qµ´+íb€.²Xd©’PŸ_†Ã(úÐKXNß—qû{ý£`_«x¾ÛHF{²‘–‹ÐÛWã¦û•íÅÓ¤Š"µáAjRÉEã<ûòo×Ê8û¼"Û&<Š kûª¬Z™¼x¯2£Œ÷êó̸£ƒJèᢥÂd¹"„Z$"fŠè4ƒÌvûáfb@bÎÄ3Ìœhº‚ë¼°·šg^~-¨V?ψ@‚µ¯øa—y3ÔG:PK@B;Ž@c`/_ 5•&1]ú³QÜK³‚ù aãå¾&i½—Ý@­Ùœ6Á)’ºËx¨Æ²†Ú¥¹æåß®ê –w¤Ü†»QÁ‡k)WçQÁ œ3eÙ ™`°IJ]vS‰å¥Èœ¥Ž&ÎæŠ±:Ô¡ç2Nïólj¶1ɧ÷ÕäöuUG@,ó±£ïöóøî?oñ~ýùuý¾þ„k5 endstream endobj 39 0 obj <>stream xœeS9ŽA ÌçX‚÷ñ ¿A†£ÝÀÿO\-kZ† "«yk"rñ2_"ËÛWI®r1±à¿Ûx|]¿–“¦?¿m<Ž„P6œ·-^Cëójg&×eÒT«ŒkG|h—YÑ0êí?.ïqŠxCÎ:äµçÕòX¡FɆ`õhÊ\Âê侤ÝhfI…Ò&Ùšd‚Œ©¦ÁÒ“;¢Ê‰U—gÂ?n4žu·¸‘â(Ò\I‚’-Z;B,ƒF×pY#ã4g¶!47wêí×05ÈKD|!Ãz”ÄIÁˆåÕ`Ê(ž+*†Àùí?®t–)0@…Åe›®Ê’ç ’Â´÷hE× ˜€ªÕY 3 ÖpL/AŒ Ø}žñIcÆ)Eü?Ûo|^ÚŽê0øu·bšÀþŽhˆBMw–äÂà)Øé\’¼ÏC²¨ÐÛçž• !Ä|Œòîq#•Xš(ޤVkÚ¾Á-ðÑP‚¦É-é52¸'¾ÅE¾ä)R†š[¯ ˆÐ8=F1ƒÖÖNjnÊ^BÈ®€v6ÉÀ¡ãAëÐã?®à€~å ÉïïRqCï1†ÿ­µ›Ë%[à7×åÐfø÷ëç·ëûõ_ÁÄÀ endstream endobj 67 0 obj <>stream xœeS;®Û@ ìuŠ­Sü.Écä R9Å»ó†²%? 0,ïŒH.9§êâ•6I|÷ú# |®Ããßñµœ¬+ó$~žñRv4E|β5“l=ŠdŠüÁkN#)äÀ"E²DmçüF©Ò^CLœCOÞ27x f']â>O°9ÑžvÖ³ÐMèÅ»N¬{O½KrRo†½ø¬¸%Q«=›ت‡¯]¸Ð_¡V­¬* ÔTnÈ5<¡x/eT:SÝìL§iܵ×6ÁÓ[ BðÙ”âÒózç=Œ»ñ ¥ÝhÙ¤”R&~ÏF ð[À9÷ÜÂ{Yø&÷aе,̽z] Šh%ÏU:}4.òÑ3ÛœØÖÖJ_¹%(Ñù¨Ã˜Q1Ñàá}Zß-B¬˜U›3ÛÆn,>stream xœmŒ1 €0 E÷œ"³CÈOÛ´=†gPœtðþ‹Q%Þ ¿gåšbW8Ï E˜˜¦vÎ’ºy¾Ä›ãwH«†'U^)5ËRÚ7w˜X4Wù3çÿÓ½Ò2ÐH=…"¼ endstream endobj 56 0 obj <>stream xœ]S;Î1ì} êH?âià9ÃF©’"÷oûú¢T^°™Ö‰€@ ¾˜ÁŠÁ•à/Bb˜«÷ãø½þ€¢8»ž‰¿û’ÅİÀÓ :áL„ÜÉ‘ ~-¶(Ý^˜ läS †jÀ’îèÀ\…¦p,æíš#úž8º¾T•Ò<Ðý“8Ö–&þ¤ÌŠ«I É”wà€Eê4 JöM%Z½ô.ÒoEŠµÏ®®LmÙ(ýÂâ:™µÉAh6Õ#ȱd÷„Ü“iðnEÔâ¤dÛA`ÛNäž“½ç;–' înÒk3…¢÷û­m×3öu?·.WÌ]d£¢gK0] [E—Í{MÊî‹­õm§Ø¼Oùèo):x,>ù®¡ê¾™Ò§â×h°u2\$ÈÃÚ§~Ø(3S¥1O/IŒçÅÍ^‚6l Úéy©vÇÇŠæŠ;±)OÙY½†úñGv÷fýÞ´­åFà-éձN×”3ùGÕé×±žÌ½²€I´oý+pB« £óSu¾Õ ê1&{ÈÚš^‰¨7>Ö;æaÒÝÛÂyU2Å&)ïÒGÛÉŸ%1šîîMþï?;ÖÏoëûú T¸¹l endstream endobj 51 0 obj <>stream xœ]U9nA ÌçPƒ÷ñ ¿a GVàÿ'.¶4#ÁP°K.›GU‘ŠÈEË|½1¯`_)µ~ñE›?àïþòz¿þ.Û!qß¿ãG&Êí¾œ­wêz¿”ˆ¶är•Úåë<Õ» õDv-UÖ¶´œ7/äÝãÖØ¬ëuÙä,]R‚Ç;óͽĸ&Â¥lS.a²I^WLMtÁÙ¼#j#'‡¢ßZ™ßí×UîºIO‹åfØiºº5lq·ì¼`r`"±qÔ†ñ–¸Ë¶\̲eI'zŸ ÌSB1WÑnFD2ÝC¦¦;>‚]€ØÞOïAïäa;O/ ÐdÑF—§· ­KO–òM“½t¨ˆD>Ye¡`q%)më]¾“WjÕóºÜÁ@烛YÖ UT2ÌhµÏ”¥†/À© .•Ý[ùÁ¥Œ€:Ì7¸aÅŸTCGÁ²©1+ j³ êáyq{$P/J}7l hˆ%$;Ô€’€qh·2t·ÔÄwà…k Ї¼d¬4(vÆæ¶íè)¨ MŽÀ "ìˆC*ŸdƇœc·50ýzÊŒ&5Ï0CðpSZsš¢#Kñ2 TâðBº"`DœO•É åõ—ÓÖ5‹Bd…Í{ÀõžXÊèÉ¡¨?ŸÄ&SUÃPœ}ºZåég(›Y!˜ÑkÇcÏâa5UO=µÉŽôY]V wX„b°x>íÎ~ÖÿfõË&Ñ3Öãá©1âKÙÞK`’qX£È€mÞ|ÆȰG‰R؈žå \»‘3v|€Œ¹8bÓvÏ8ñI^%ᤡÓ1~>@·5кΩCïPœ!¯ûYu`= ŠôC¿:.fIê '“„>öçœðÇ3º:À\Ä@y@³cÏýc¢GECÐÆ%²2OÌuh™Ã Ž¦ìã–4cë k‘5‹Ý@gïMl·`N‘Û—\kñì×ãòQ næ“÷ç¿RʳŸV ]¬¦Œ>ð‰Á Ç1qÊç,ÌR"•B¹[…=)†‰ÿþÙ¼®ß?®Ÿ×?‹ÎMÄ endstream endobj 25 0 obj <>stream xœm‘9nÄ0 E{‚u ‚ûrŒœÁAªI‘û7¡<‹S X|_¢õ¿¤ª@>µW‚/^„Ä£ÌxÇÏúEqv=…ÿõ,rw#ÅÀU³w0&Ü–™F½SÔUGq2ÅL` mpïDÝÑL4v¶á´>VDÞ;‚ÙÑ!Å ™‡•²fUžt¬Êv,p'ZoG-ÓËɸٴf>Ök‡4oÇMÆXÃ*PÚdÛ?1*dŽ;î+§Dº””É£Êuå‘ÎÇòÿ5y$¨'áÜ ¡ofÞ7à¬÷3§SÎ3¬äî‘|Ì2X’ã>stream xœeŒ1 1 {½Bu ±+[òÝ3ò†„TN‘ÿ71Ç…ø0±±ãî Í‹Qµ1õIƒŒù…Ç[>Z­ìžõsOÜ‚Ê ÀšN$  ]J lkë}6úâøÌ>n óÙ°\º]^7¹Ë‡Å,Æ endstream endobj 9 0 obj <>stream xœm9 Ã@ E{Bu ñ5ZÆsŒœÁ!•]øþMf^ÀF Þ’Pª2¸Œ \5ù£Á0½v˜WÚØÅZIÿ‹+÷aX©‚ÖÃÉjá!Î yÕžLгš›JÞóq1%BLÌØ?ÿ/ô}Ñ›~–)– endstream endobj 78 0 obj <>stream xœ]Ó1nÜ0Ð^§Ð –3¢È5`°q Œ8ÐR”¡ÂZA^¹½ÿŸÉºHñø+Qâ#ÅÓÓóçm½õ§—ãZ_Û­_Öm>ÚÇõó¨­¿´·uëDûy­·Í~ëû´w§§ŸÓþçïÞzÜÐ₩÷vúÏöø˜zÛÇ>ÕvLÛ[ëC(ËRº¶Íÿ]Š£¸,÷[¥x‚ÎU‹'h`Š'¤3k,žFÖ±xBz`MÅRfÍÅrd=O&և⠩±NÅôÂz)žÖZ>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-664 -324 2028 1037]/FirstChar 1/LastChar 45/Widths[ 333 278 556 556 222 556 278 278 556 584 500 333 333 500 667 556 222 556 333 278 556 500 667 500 278 556 722 556 556 833 667 722 500 278 667 556 500 667 556 833 667 722 556 500 556] /Subtype/Type3>> endobj 79 0 obj <> endobj 80 0 obj <>stream xœ]‘Ánà †ïy Þ &%$•*_ÚK›¦m/@ˆ©r(A4=ìí‡q³Ã_¤/`à·Ûóõr˦ڼú/ÚTXâœé±>³'5Ñm‰îÔ¼øíeõëï.5íùÍ¥ïŸDªl  þîîÔ~ŽPÿh©ñëLäåŸ9SÜêTëÔxZK¤¿Á§5q•*4¿£}“ endstream endobj 12 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-627 -375 2034 1048]/FirstChar 1/LastChar 21/Widths[ 722 611 389 556 722 611 611 611 556 333 722 556 611 722 333 278 833 611 667 278 278] /Subtype/Type3>> endobj 81 0 obj <> endobj 82 0 obj <>stream 2013-09-23T17:59:02+02:00 2013-09-23T17:59:02+02:00 Qt 3.3.5 Untitled endstream endobj 2 0 obj <>endobj xref 0 83 0000000000 65535 f 0000004640 00000 n 0000035135 00000 n 0000004581 00000 n 0000004430 00000 n 0000000015 00000 n 0000004410 00000 n 0000004705 00000 n 0000026248 00000 n 0000030599 00000 n 0000007283 00000 n 0000010289 00000 n 0000032980 00000 n 0000014036 00000 n 0000017957 00000 n 0000020710 00000 n 0000025711 00000 n 0000030421 00000 n 0000006560 00000 n 0000009925 00000 n 0000012879 00000 n 0000015547 00000 n 0000031279 00000 n 0000020514 00000 n 0000023858 00000 n 0000030091 00000 n 0000006223 00000 n 0000009688 00000 n 0000012644 00000 n 0000014669 00000 n 0000018702 00000 n 0000021859 00000 n 0000025371 00000 n 0000004819 00000 n 0000008470 00000 n 0000012428 00000 n 0000014460 00000 n 0000018244 00000 n 0000023506 00000 n 0000027396 00000 n 0000008189 00000 n 0000011137 00000 n 0000013507 00000 n 0000018052 00000 n 0000021356 00000 n 0000024866 00000 n 0000005655 00000 n 0000008960 00000 n 0000011882 00000 n 0000017707 00000 n 0000021087 00000 n 0000029199 00000 n 0000007623 00000 n 0000013336 00000 n 0000016892 00000 n 0000020160 00000 n 0000028677 00000 n 0000019648 00000 n 0000022994 00000 n 0000026965 00000 n 0000005389 00000 n 0000011306 00000 n 0000014197 00000 n 0000019226 00000 n 0000024673 00000 n 0000028507 00000 n 0000013057 00000 n 0000027946 00000 n 0000004978 00000 n 0000016734 00000 n 0000019018 00000 n 0000022462 00000 n 0000010812 00000 n 0000015942 00000 n 0000018860 00000 n 0000022283 00000 n 0000004746 00000 n 0000004776 00000 n 0000030777 00000 n 0000032279 00000 n 0000032631 00000 n 0000033546 00000 n 0000033724 00000 n trailer << /Size 83 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 35277 %%EOF stxxl-1.4.1/doc/images/btree_uml.xmi000644 001411 000144 00001260462 12350112610 017206 0ustar00tbusers000000 000000 umbrello uml modeller http://uml.sf.net 1.5.1 UnicodeUTF8

    stxxl-1.4.1/doc/images/overlapping_runformation.png000644 001411 000144 00000101307 12350112610 022337 0ustar00tbusers000000 000000 ‰PNG  IHDR¿qG«ugAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿ‡Ì¿âIDATxÚíw`uÿÇߥ¥¬BÙTD‚ QFÁˆ£Uù  "UyDp Hø¸WÁ=Ðç©8ÄU†¢U´ ‚R@–hd† ËJK›&ïßwIÓ6m/É]r¹|^ÿ®Éå“ïxÝç¾÷½ï‚ B$ ü׿Ì_|m^öÜøG?-"¹ÿ“9Á}ÁÇ÷¯óïûß[®Ó+YübU*]]mµàÛ×|m>4íá»_ÚD²tEÆñ ¾`ç¿ÿ[ì×J—¾¤×ûëÝeb#!êü}xÎí6à³Êø½çÀG;§Ï|¨'0>¨ï˜ 4:¤Ýö›^îƒ÷ôøq?Ù§Võ×ìØQÓNë^¼,]+ÿÁ1©ÍC7˜øîðÆÀ Óǵ¿ýÄܱ-ª®¿~Üê'ÏAù3¬«›n—¦Ý÷âOQ•`E?5®|ù{kâ ÷¶Î€`ýý€_4¿ûÀèó}ü‘–€ª‘‚GM;ýåÖQñðåï»±šœ Ôº% Aú»(¸ZûÛ§]ßúø{Ý-•[Ìñ9-4¼ýžÑ—uÐòåb ‚ÇOŽÕõåïÈ"¹ºnÜö Ößš ÅŸÈÙV'“ïV­ˆ @ããQÔT÷åïŸÑ‰¤ë:ôv. Ö߇ºßúóþÿéäo²´Måó€[H’k†èÿØJˆ ÌÕøÆŽ>ü½ XL’ù“·ëoþó{©¦÷ÍRÿí§›¿V­ˆ»Lµ@üT[Ùò_þþ.!IçNþ´¿]›´}þ÷¼+Ç_|´˜9îñrùh±•AþóÆ7võáïÇÏÕ½ñAû[#¯¾¸X7/ªRG8Ãñ-pcƒ"moü̇¿ÕÁ•îׂö·ÖÄ¢Ûw5UŽßøh1ß{ù»´3PkèJˆ¿‹`ü}]üýk½Púû?¸À7‘ÞÿéŒ ü½¡÷·ë„Äß ¼üͱf‰®„Hñ÷Êø ü}qèý}èt„ÐßÎ3zOR#¼ý¹®A0þ^¿Ž0øûuoˆ®„0ùûï• ½ÿ_¼uÙ†rß¶”’®?Wº·-o/ü‡,X³Û³³u?ïtUïï3«ôw¥oÖÄÁ÷™ì’Žß-©øŽ½½QÑßVo÷ ³üplZ¶É{à×µoE^ù°òWÙ]U+bÞç½Ö›ª9•l\±ÏU®ÞËWOì"y|í6u[ñ-ðò·ËNÒ¾æ¤ïšòåïYUû»â7k;œìr¿Ú˜äß+÷WzË[±ý]²e•W˜~Ä5«z¼pãŠåÂ*Þ¼úxÍþ~À<Ñ•—ü¯Gí¶ èû…»á®».!±C­¸K¿T6¸~}¢ ŽseO fð!’œW@#›Í6Ÿ,þòÚÚ‹øQÔšN’E¯téØmîÛ_¥¿Ï°ÙjI6›íòŠþ.ÿÍÛϳÙl¶.YÜs…Íf³ÙzÎ'9­ƒÍÖ£œžOÌ·š$KŒkÕ‹ü 5P÷áò—Ýv¶o³Ù2Üþ^“ Qí•~Ä®q´xÔÝïÝøâ+›ÅÝæÙßÔêØ,ñÕ*/‘ jZÈ-n5QcÚts£¦mÐøß©ÿ¯XUÇf«?‘Åÿn4›M’ÅW@[›íÿHnÏèp1üÐí„6âÃßoÚlMº6›Í–SÁß¾ùY›Íf³]q’ÿ9Ëf³ÙÚÝ@òøE6›­¼37<Ún Ir÷/Žùšû‡Æ]+ÿŸ€–6›íÕß%O·b.S§Ëÿ×ÌÞbú/u7¤·»¶Ô¶©žËùw%69#î’?jò÷Å@ìߢ+! þþ³w£7 xr8p·‹$Äœ±²”®:Jò‡îp|Z[+ÝKI®È¾¸&;;{ï¦ñM,šýIn耧ÑùE $|Q•¿¿ÊÎn <½°¼¿+~3÷4Z'éºÀ$EÉ[bâ·yímɨ«I¾v*€^Åãã:&TšS¾5{Ð);;;OõwV|“nud’¬ø#¾j4rþ¦ù—ÉJŸ|?æôäÑKÑRÍÿÿÂðÝt-íœT…¿·Ç<@rP7ß4C!¯Öð¹¢6Zþ®è°BUj`â¾~ :Õð)É#Ùï˜ý#ßíàâ£ÝàG5åÃß›³³ïÎÌÎÎÎþ«¼¿+5’û¼D’6’ö*á_¸Õ+>úòÙ’Üwq-_¯<µy;õw”ûÒ¯³[Odgg—(þ>Ú'¦S[m ÉŠ?âØà–ï¬[;¥ð–2Âvžq’sGÔ½å¶h2«”Ǩ׸zàI±•ïi[g Iþ `I¦ãôC$éô."‹]ï}Ö:r^=÷eš›Õñ“õóðJž«ƒ ä¶Æx]‘l¢¢€*ÆONT?©øÍäã@IJÞUÞµ ã¼wõÃ÷÷©þ.vÝ tèwÍ~:Ÿjï*ÿ•ÓËŸŒH˜æ`ápàÔâJ?â‡Ú÷‘¤³0’$ÅàQ’\ܯ¨ð& r’äÞfUø{bÌv’9&›¥)=‹N’ t#}TUñ‰Î@êiOòø ‹“$íîñ“ÏsÚý¯{lÑ…¨•﫦|ŽŸ¼ô­<~R¹‘luÓ@kÕÚcã¼kpß—ß´Qü]Zº@ZËÏœÜvÊ7’6ïñ“–©—ÙÉ”©œåDÉ• w“ä@­ßIò2 ˜$¯Bm%Ð_j)£|¡:ÿ=1#7ðað·«¯Ú ]ÛI~åiªÅmÔºÀ5ÇHò. ­œ¿É£Úæ“›¦¥³lêÐÆ›@ƒÝþùÛÇ7Œw”?\«üñCü^á¤ú›œÄ<ã$éê ¼S¿[,'ÉuÔyèÞ?¢ Uâ1’ä «HŽî"É" Iòc ÏsºîË߯"Ig{ É$ýú{ÔÝëùU{鳪F‰ßäž8`s9“÷ñ“HÇìŸ|Ö”vûúæÜåGêzÊöôë*ìí6Åß$Oºl!ÉOSªó7n)VwI¥ñŠzluuVö»@¾:˜ýIuÁ0eO'ëøöwÛ+¯¼²Okà”ñ²:ŠŽÚ{ÕSÏzu¾&Ɉs_Üy ˆßE²À$ÉO€ *ø›-€)êË9e§‘Çwúåo_ßÌ‘ÀÊà- \¤Ñ·â!¨¾Ûß Vʶ[‡«ó÷ÛÊ«>ÀGÄËî»´÷@Ypã)àY’d,ê“dIÕdŽo¿‹’ä«>6ECröÀåUé%èïð]U×+›:ª—ã¼ü=hªj×gMiö·¯oþ¨¥ Ë&‘$7–µ•=þîçòø •æÇ”ów’K=x¡}Åq¢…{Ñ…Û€¸BòX Hr²:˜ó–zp'y®o'õìܦ €úÃ>)Y að÷8Ëýòð!’kŽî ;k¯’¤Çß Ó*úûTàõå`à¯l©±Ãûüæ€ÄB’t4QU˜_ûÊ{Ktû{‘Çߣªó·Ú‡ÏUü½qÎx…ÚÀ’'_Ë,Vý“Š©ž¦G>üíJn¯\;\èå2CCZÌvGg/­¢ªžòø»0­‚¿ß.Rßﳦ4ûÛg#¹È]¯\$y_ûŠ7?½æñ÷Ån—Ø^¿ÕÊYÄWüßã”jî àg’«[GÕß+ 7¶´R‹©<~²;ë§/[ !÷wA<—Ûð2p™ç?gƒÊùÛÓ%Êû{‘š—5~rv€5þøÛç7;;YîÝ¥ä‹Ohð÷SÀü}#ðT…q8Žõð çó¥_¦8NrŒ*·*ý½/—ù KÍÐÖ–ËÇ}UU™¿Ý%TÞßW×F´úÛw#ù8ÃEÒy¦Zb…MžgÍþ&€­5û{+€Š?â^Ô)«æ±¹žÏïz´90‘ä.w®Âç.l@.„Üß¿ºáñ²ioÏR€³ýð÷ï;—øÒûüf>¯ÜãêÙ߆˜¤³ýêïµÀ3•Ëéà‹]Ç­Uüݘ_­¿¯ÃÀúÃ3l^†åV³õYUZýí³¦´úÛw#)LrI~…›±$?ŒÛg¬¿£våÕ \‹†7íQÅߟjô7?ÐúøJ±¿¿.-·á& ÉóŸÑÀ~ø{<Ëä3üñ·ÏoæÞXÄì$â»§Éä÷îË[Fù{žzµÒ›}·×Ÿp”TýÝ È®Îß»cZ¹‰jýi‚†ÔXâýŸU¥Õß>kJ«¿}7ŽW´Ýïüã hX@öFcý}zµÒÛÞŸuë¸ÀÅÉŠ¿3Zýíìà5ñ•bçu’èîG1ž)ýüÌ¿s¶ÀB?óïÊßL&“WôpíˆA¯½Æúû;àò ûÿ²QÝŸH¿ëº'»Uáïǽ†L^€{Òax9¿ļêªò#ÿ®\Sþäß• p9²9ø˜¿yNqŒò÷ù@…I#Gcx!éö÷ @c­þæµð” „Ìßk”»â%›Üÿ¹¸Í;ÔÓ¤25`?þöùÍdÐÁõ+f—K÷ÄN7Øß?qeg®Brž;aTýÜW¿ [œUvÉò`] Ñ±ð7¤AÀïÿû¬*­þöYSšÇ¿}6ò\à#^ÓÙÉe@ ïjç4ØßK«œ K/B÷ü“‰$ßðV'8A™¿ ã½;öcǸî™JÿÌs0ÐȽŽÊÏîþìÇü“Ê߬¬¿ä¦Óä,`ì¤FûûhŒ÷ºÝ¯g²¤5pÐÛßUûû-ÏLDUoøOøÒ“@œg¢Æ¦©¾«J«¿}Ö”?óO*7ò]àÒÍ1ï“®3³¡¡kúú{"ÐÆ= ’Gús*Ô)–ª¿yÆv4øû øvÄßFsë^CâýѤ³;àÎ6´>éÛߣôÊþžƒ²ijï»WdÓ<ÿÛ×7+ê¹CWΦ×çùø EpŸ24U¶ÝVIë,¡ëÅed7w¦Å÷i„×(NÐøá¹‹?Œ;\äF}óØ;Åßó7e9ŒÿË'ù}S¯oôž8v¢­šÑ»¹ÀY%aoIkâœöànAU5Á3Ù±“ûm3 ƒ\ø_r p¦go>jê­ŠWÂI> œî~ý‡çV_DKi¬\)ê9¿)ÇCž°îÀI*·ÈþZþ]À˜Ü9œÔqªß£žFxÿˆYpÉû‹¿žÔªÉz’#̦kÕàS€>üÇν̀³\䱑1ÀÙ+·”ûžÿ¸IMïoÐj›èJ¹¿ùO_(ÔVÓ–Ò»€áÈ‚[1ZñÎÇîÕéK¯°’$÷Æío<ýe%¹Ò3¸6 ‰_¹È%­š¯ð$h¸®Bj2Àeêgö4lU|³:*“ žÃŸƒÿùøïër§A¿“äÑSVåŸ(ëê4¾áÜANnªô.$I{ ó1Vø[OSK7•’t] n§ØË÷œ 4í6”, îÀÑÔ{@Ì%«Ë¾¥dp•÷Ô]7ÀÝŽ°7¥Y±êêùwUu²‹»&Vè$É»˜¡:à±~(bòQS'/jUXÌõxWÏôº-{ÄAåF¢VÐãÊ«¹hëcÄ©àL÷þŸqÀu.’|À‹åß6À…×%-¥ó&÷Šl®qêúSåÄ£îjn²‚$@Û¦ ïçè‘´’\’àÌ myM?-Ë=°d['qéo¼þÄ]çÀà¿ÄVBüÍ¢Œ&pÙž-ó/DýKR«,â±öªZÐÿ}¾ßêÙMrj Ðè-~ÒÐz¸{ôáðýMqÆÀî'(ƒÀSú:ßä5×vt@Òµ¯ß]UZ ÏõñÍîl½ûbÙ{Í +…ïE€n£ùÉùP÷ê弯5$•_|{YS vÜÉ}ƒ@Û[¹{H$eTüûFÇ@u Ûîþš¾îäÇÒN’,ýo+H^½=^ÚíõŸ­R<ã¢ç6RÑfFØÛÒêþøšçPR¡ªi­F<|m}èò’ûz¸dω« ¦ÿxßmäÏá§@üU^Çׯ¯i¸è_[È1]}ï÷õÍ*ÿk¥¦m_®ýOÃZ@ý9ÇG4€.ùóe€KÊÝùè¸@û¥œÚj¥,à’ý­ð#\Ÿu€:Õó§·êHÙÆâ.@“Å$¹i` €º_Üà¦ï¼ÁןY Úþ_Æfq•“ÅKg->RnË_ó²¾ÛYý‡v|>Ï÷¼¸’Ÿç|º<àU÷|}óJw2^øC0?óÐw_îÖzR²pÆ¢ýžço³8I’¿~fW7–®útNž‹ÿI­iÇ7Ÿ•{¨–ª*ʧ½¦4ã뛋W¹_->įtý6{©Æ Æ®õÙs~/‹âàü9ÛHòØ—‹ÝyÂþï²æÿC®-U_¯?!¥!âo!bØØ H AÄßR‘Ç?!þAüy¸®ø[ñwäQ| ¼ý}r¿ûÕ_ÇHü²N?ž·Áéý¹kV”Òñ·>}_mm¶ÿ#Yúã­‰/“$·¾t–²à¾@£L’ë‡Ç‰ÏºÜ9ûÌÞbú/•ñ·&Žd¿`vvöÜúÀ©^&ùë¹1–æv¬wÁé>s½ßõ²Ó|¤|èØà–ï¬[;¥ð–”  ˆ¿…pawŸä}û~-Åß%Î?\Õå[]ãî÷\ðézèM’,¹²án’ü¨õ»  ˆ¿…pû›d'Åß$[ƒŠIòP<ÐûIžll"ÉW0™$éê ”ñ·`ÿŸÇß½éÊ«®@¦òªð-É-ð‹²á6 ®PJPÄßBøý=Äãïó=þ¾ÔãïAÀû$¿Æ?~üøñ}ü,%(âoÁœþ¾ÒãïaÀ‹$ïE±eäJ ‚ø[ˆ?Or0j;¥ØAü-Dž¿/ò¥ØAü-Dž¿Ï–I± ‚ø[ˆ<&–}\œñ·)þž´9éþô‘>ÅR„‚ þ‡ýñ÷מP?P:ì )AA á¡ÀFõõÿÏ)¯Îóøû à5åÕµŠ¿§µÞ&IowRJPÄßB˜hd ÿ«dÝ)ÏÞU^]<¦¼º\Í»gÀ%ï/þzR«&ë¥üAü-„‹»˜¡: 7ÇÝ IrK,0–$¹¯>еˆ$6.v’ä£Pi²BŠOÄßBØØ×À%{øÎUu õuÛ~Ñ.¸‡ o íFÜwM8綤ë³NPgâ!)=A a¤(wvžŸq­Ïžó»Ì<ñ· ‚ þAÄß‚ âoAAü-‚ ˆ¿Añ· ‚ø[A ‚ âoA¢‚Œœ¨|D ø[„H§@Rzô9\ü-BÄû;3ˆB‡‹¿A°Ž¼(t¸ø[q¸ø[A.þA‡‹¿A‡‹¿(D‚ „—¤ôñw@_ ‚V’3r Äß‚ AAnF²µÝ-þAÜ-þAw‹¿AÄÝâoAÄÝâoAs’…î ‚` ‘EŸ»Åß‚ X#/ˆÊŸ-þA ‚ âoAAü-‚ þAÄß‚ ‚ø[Aü-‚ ˆ¿Añ· ‚ þA ‚ âoAAü-‚ þAÄß‚ ‚ø[Aü-‚ ˆ¿Añ· ‚ þAëJÉ:ÃvmŸŸ½²$2«¤deöÒƒæ íØÌŸ¥ËøÏñ¹ïúñîÃ[Ãè¾æKm‰¿µá˜Þ~ 1{vÍI€FG^…™ØÎ_fÆànÁÍÒeül‹¦\íõÿd£—Ó6ä>Þ ˜,5&þÖdï:Æø»d4TNßiõ±¥˜a¾à>‡øÛ_6ß~] 4ûûÈÓ‰@xüýÕý!þk³÷G=΂Qþžˆ¶ã^ß:°ì»s‡óàôæ@Ãf nOSñw`g-ý}tR§¶—¿IW{ñ·ø[y NòRƒü½8fB!IÎo àéÈªŽ»ú%IþQxÛd±9S“Äßð‚VÏ_ç,ª>s€ø[ü­™ ò÷EƒêA"hÚ8ÔåõÕÝÀ­& nJÒtñw ¼?Æ¿{‡ÑßCÄßâoÍS4?~ ÅßAùûï±½m6[ßšÝßËn:Óf³õ¹y«Tø;œþþ x1"kåhß«˜* §3•3‡Ü¸kÌ·ò­ø; žúSõbÿšÖ¯jÆË‡WO’ä'WÍÑóºº9ßý„Ýo¥Ê+×—# Ãg݉Ãçïí‰@Óýúï×yS¹É·ER»x@œRî¹Þ&¼õ_ü…©@³£j"Žs·TûæÏôÓ‘{osà —šˆcô©º*q|3@í·xs·à%õúù`Àì]'÷̹8ûGKúûø­ƒê@£«oݤó®'•Ÿì¶&’šÅ&ÔkÓ,}‹ÃûÏÖMàÔ¿âÔs Öe ªzï/£ûÇÀi#Ç…^á_÷€ä1™ÞÝ-ÕW5iÆ–ý·¤‰úÀÃ鵑0WµüÍ€)V?*S¾0Ove5,ßÚCÞq"Ìß €ù$Éÿ•žzx1€‡Mèï¢[ÆTqdq$û¨pd”á™rWÚÈñõ}I²+kaùÖòŽiþ¾M•;tÖhX~éHW€ïÍ9~rCþÎ…Où sîJ>›}`#‡–ß•>I¿6—$é|eð4eÓÆ‘cvû¥–oí!ï8æoGs÷B²®SüRîmyêEPµðxþÎô]áö¾Âœ»Ò–¶øþ¾4Ù•aŒä4óHÒi.0öK-ßÚCÝq"Íß ïÔ“¼Pîm¯¸Ó¸(‚ò÷SUø;Íw…çðæÜ•&쾿/IveÝ,'É»ö«D¥/Ý‹Hò§8$(v`ø#ôÌÙD#¸ãDžõ¿-p‹gë{>ñz×îD"‚üQþ6ìt?ÝøØíV‰J/~ÌR'»ñ5é)þ4Ç©‹¤«úK¾)›h~wœÈ`2Ôg£ý|ëÙê4.{øá¾Ó€¡ÎøÛNòÀÊ#êÿ ò–ïô~þ´ãÏå›Ê8wþœ_¥¿âïj† ÃlÊLKûÛÀ$ÛÏ«an¢ñ·ÁÜá¾Ùa{=±ÁqÐѽÞàŽdà†BíïÃo÷K'Æ"1$ óûêqÃùMJËÕi|ï?Ԯî™õ:|[ÕøIr$ùÛ°yP¹ÆÇî°JT‘L²)›hrävœˆ`u€„ediËòõvý¯%bîÜBÒ>9‰S\4Øß ‡ÇpÝ O’®§ê<‘»aÖéÀM’tŽÁ rK.RGé÷õ¬•qœ%Ó“ªðwçè¹ú B„yWÁœÂ&Ë®¬…å[{¨;Žù9zQ{µlýr€oÊÿõؤ3fšg<~ØèóBrÑü2‡.»øœœŒ¯Hò¯ºÀÛ$9I~þ0—$ùÏYêz¶ó«º3´ŠkdÜrkÎ]i=çöEºìÊZX¾µ‡¼ãDß?ôÐÉJ7Î|å‰ÿÌÚ‚q=’ü À Rrñl'W#EùËí@Ë’gŸ’ä<÷Òˆc‘¤Ž÷\R…¿óõ›WfÎ]“·äÈ®¬…å[{È;ŽàŸ¿7xÝ54P¹¨ªu’¼X=SZ \E’§Õ÷þ»ª•Y|vì<ý$ö]ÞîSeWQ1€’'»Bäï­Ô‹•Gc1xüøñãÇ¡ÞJ´÷©/©úû’,V?\ÕõK_ëÒzZmÎ]ù{â™ìYg–~'°æŒ*‚±|kyÇüö·úÿ/kÇz˜æyß±©g½H²=°½&W^:ð…¡Í¹+m¸—MNÊ zqTýÖ1gTŒå[{È;Ž ¿_õy]ySz—Ésç8Y£¿+efIv²X3íJÛCªûVµ¤`³–üTÝ sFÉX¾µ‡ºãúûaõj¥7¿\™ø¦ƒK¯p¤fWèÙA=WËœ»ò§áç1CIxu“„»2[TŒå[{È;Ž¿ž-ÿç“1güEºýý3€_5øÛûN¿ »µ!» Í Öüd I9 ¶Õ{=BV]™/ªHÆì­ÝL» ó÷ ê(·B¡“|µ6°Ìß›e‚xþ&í9ii™¹z\Ѳçdºí*--37?#DwšÜ‰ ’IXü® ò²€ô,»ÃªQE2æníæŠJ0Èß_øÝýG×5yü­ºÓËßEñÀíý­s¨:¯_šÜ¡LµÊÐihX§R0gT‚ÉKTêÙäþ>T¸Ø½`Ö†“S€ËI’ ÔÌüR îA¿oˆà¶š_¦S)˜3*Aü-øíï_ìW7l« ã«ß/x£>!ù&€ ¥Üy î„s=K/ðh1é|%høí'#¶í„"w¸ïTÓi¨BŸR0gT‚ø[ðÛߥ÷Èp¯s87^½Ì;•$4Ðì´˜ûħ·}Üs€–#n8£Ã]êŽ8±m' x®{¡†*ô)sF%ˆ¿ýýài€n7îV6ýr `¨òü)þÔ@§äm@Ìk$ydB=²ÿ©ZW|x,’ÛNðtÏs¦ôªÐ§Ì• þü?©È_93W–y¹pÙ¬Õ.’Î…_R7X4ãë]äÆ}ÞvŒOÀe ýè3T¡K)˜3*Aü-èàïèiц'à¹eëlê3T¡K)˜3*Aü-ˆ¿Í”€§{=¦[—¡ ]JÁœQ âoAüm¢Üá½N².Cz”‚9£Äß‚øÛL x®÷cJtªÐ£Ì• þÄßfJÀ½*ôªÐ£Ì• þÄß&JÀå3¥ÇP…¥`Ψñ· þ6Sn‡w¦ëHÕᡯ:”‚9£Äß‚øÛT x†—*©HÊ7E)˜3*Aü98W=­ÿN]ëŸó³/Š¿/S¥>¢Ô§Ì• þŽ N|;®5buÞiÑwãmÀVñ·™p*u¥N¥`ΨñwDðîM¡»¿gÞÜâo³%àª*õ¥^¥`Ψñwd0W+ëx‹¿ÝÖZz$àE+¾XðWÕªÔM”þ•BÉÊì¥MIòØÌŸÅº”è¾æûãˆ#fŒ*âøÝ—Š¿=y²ÑË!KÀ÷ÝžçϯJ•Iz‰ÒŸR82±!œ¿ÌTQ©Ü‚›EÒA—hIî㽀ɚ÷ºõ_µV›/ªä#üMñ·[]O'šýl¾¼™ûÑܺªRe~ÈKaK; 0ƒæ‰Jåsˆ¿u(ѯîìí¦Ü6:Xm¶¨ÄßâïòÔ©-üðw ø®D z4cxKx«ŠÐóC^ G:wøè0No4Üiš¨Tö4ëS¢®öZM¹}LçÆ‰¿ý‰Jü-þ®Èüu΢zþø;¨Ü™’´Š$Þ ñˆ>à Á—Â]}*-­ð6Í•Zd©IâoJt€VSfíâ’Pù[{T‘íï’Í+”Û~lò­ÅÕôÄ.’Ç×n+wª~ì·õ%âo/zûãï ðj»˜GøÈ$¦<ÔåõÕÝÀ­&ó÷”¤éâoJtˆ¦,™¿‡Dƒ¿ß׈IYç9ëøâ’ØSOE£ë%IÞßÃf³eü¸ŸÍf³Í$Éc3‡ÕŸÈâ7šÍöìíסq§Úâo="þöÐ×/“€¿Çýêï:À½&1åìܯ>î6—¿«óí×âï0˜Ò!þÖÑß;ÎP.zÅ}¦l;:ƒw¸¸¶7bs’äÁSEKb÷HŽj`â¾~ :Õ𩺳7⺭"íW%‰¿ôw ¸óô²yƒ½ªHu×é’| ˜m*uOñ·ø;Âý]+¥Ï§—Ý nI‹kKIòDowÆt“Ûßì¦ø»øDg õ´§ y|ÐÅI’|§ ÉÒ‹Äßú;ð¼t}ÙëQU´Ú°ú{ z•šÊß÷œyBü-þŽtc”‹$gèK’÷!F}äûÙ$y—Çß}“£€ÄoHrO°™$7×C¦ò¦Å–ô·]P_hš5á§¿½p‡=àP¯>7È”KÁ®ñ© Ûêuڧס@—¨æÇ¯¡ø[·Ö®šrÙMgÚl¶>7o5Ä߆F©þÞ­¼®êžxôwÿõb5·ïñw_·¿®W6uæ‘äõh¨>,þ„%ýŒ dáowîÈJJ 8Ô®hRd)s+–‚Ƨæí{õÝRy=¢:pÊsë×Ú=™îh´™é2(ÿ64ªõ·{þà&‘¯zfXëÛßOyüݘFro,.)ë‘ôwFŶ“g„¿•Ü‘•„ÀµöW-wuéV,mG™µÐù“Rš'*×о¥âo[»Û”sãÆ6~bhTîoW[`(9˜äþë^¯Ôäï‹•M£,íïÜ€O¿ý],ÉJ:€ò,Z4Ê”•R g!ÎoÆÀóDõ^Ã?)þÖ±µ«¦œÿzÍinÀþ64ª÷7S€¾dWàOÇ«­LDÓâﻀG,íï‚€O¿ýͧ§~CV`žlWÕ'uzè‚¿g!_Ü-†»ÌÕ–Pü­gkL¦ë™„o4ì<`U¤ûûfà2² 0ÒóçvÀíý= e'íÖœ’Àð·ßþvdµò|CZ`qNÆ-.ãüÐð7÷Þ`‘I¢*9o˜Kü­kkLvÜqʯšyÀóOŒŒ*ÒýýFvU§¡$Ï×èïÀPkû;#€áo?ýíPGN{äöº—Uu符,ä,„$ßp§I¢zâu€Iü­WktùIÎO€–§NüÅ¿û"cýÁdÿS¼óü\Í/ Ó¿;x¾mêy–zÉOseÊÜOîÃãÆê•ø[¯Ö~µr§ã¦ÀÈšú~aëU¤²égŸ-?Yn“kÃÜ?ñÞ²û›GHn8\í®693×A.)õ7†Èðw†ÿ)^K?¯ØËùÛ¯;x–ÔMè Ò.OfÊ2 Ÿ{ô™©\üÏ»ÆÈõc ”ëÕÚ¯„Òä>ð` ªÜ`‰é¢'2üëwŠ÷9€þ¥~‡wð¬ièýÉZ» 3¥W Tsô €9¥Üÿr½'ª¸­( Q‰¿õní{›W¸Ô”£T÷^×™-*Áòþ.ð/Åûetÿ8mä8?žØ<‡›•ËÜhÊ ?2 M6¨×¦Yz•3JÕø[ßÖþuÿxH³‘ém áÐÝU½wÖ]gzÅDQ Qàï²/ÓÀ/ÉîžêJYŸbÎõç,¤xÞ”Ç_œžç4WT‚YZ»E¢GÌ»ðïY„úÞw “) ŸŸ9Q ¡ií‰Jü ä†$Åó¸¾.ÒëI“ɺf@æŒJQk·FTâïH  4)ž×,B]©^¦ÌÐ52gTB¨Z»%¢GÉ¡Iñò½ƒ'4¦ÌÕõàbΨ„µvKD%þŽ2B”âåvOˆLY kdΨ„еv+D%þŽrC•âeyîàqäëWÊšŠ9¿æ_—¬gdΨ4ž(Y:ÙÏ ý †ÖnΨÄßæÇ‘û*à´,ãëSÍ2ê'HËÌÕ¥µÖhJGnf€äôj`~Ö€G¬•†Æ %ª&”­]­Áôš[»9£rÂ<7´Aæ{­½nxe–_‹0IÓÅšL©ífFETæj a ä?PS š3*ñw óPHcÌÒ_¨Õ¦?ý˯E¨Ck­Á”š~`~jtDe®ÆzBýµÕ 9£ñ÷»©¿+®Í­ï¥ÅÊäUü¾¤ Oõ¦Ôô£&*s5†cÎÖn>(þVYRgU~«¡pŽJOÃDº¡¦Ôô£'*S5†0gßfiíVèƒÖö·k熣ÞÛ];^wÀ{ÃÁÈ‚5»Éå-JçWn:H5ò`œîã ƒ] ¥:Sjû¾¢Ê uT ET~7+uÇÿ@M5hΨ¿ÞyÊ€kÚžsÇ=êÖ}÷´Žï\=¦)ÿ/þòÚÚ‹øQÔš>¯€F6›m~Hâ«ø`Ão!Èñõ}IA>C¤SjûQ•™C¨1gk·F´°¿µí¸‡<9ƒ•Ÿ7H˜]Ä‚bÑq#ÉMã›X4 ú¯È¾¸&;;{oHâË…OŒûBŸm5Øá¾jÖö£(*“4†#é×*g]ÎWOS6m9fW4¶vkôA ûû_ø@9Î^N’ü,¦–òûOÆ[ÉõóðJž«ƒ äÍ¡?ÉôÝvì†e@¾¿/-ÈRFp?0š¢2Ic$ Éi摤Ó\­Ý}кþvµÄlÅבäQýë §“äQmóÉMÓŽ†Øßi¾ë2Ǩï³ûþ¾$ÃL©éFST&i Ý,'É»åggåJTš£ŠdÌÒÚ•àñ7‹F@ü ¥$ÿ`“ûÏØNWqŽžkÔ÷Uq²˜lØ•BM?0š¢2Qc9ælíÖèƒVö7ù];R¢äß Üþ@I8ý]Å52ãîÞõý}éÁî4ÈEQ™©1„s¶vkôAkû›…Ï&(—òWxÛ½õ ÃéïüPO%J7b®T5¦Ôö£(*35†PcÎÖn>h]‰$¹½3Іt4®pÿù`|Xýí».óBÜ…‚]졺›Õ4ýÀ(ŠÊLÁ(aoí–èƒÖõ÷‘S]$ÉCÍã ïj¹oŽÿ—ZOÞþÒSK‘ûõYœ*VgJm?0z¢2Uc5ælí–èƒö7Ö)ÿ¹ g‘Ü•¼¬l8\O½³µ×üï#Ba^å37c/DWZë8ø•ª],BÛŒš¨ÌÕB9[»ú •ý}•’€ÿŸ202hù7Ir2ÎPV•mÌr$hr˜Ec…(ÄŠã$»±ß—Ÿª{ªQýb?š~`ÔDe®ÆrÌÙÚ-Ð  dãŠ}.¯ÿn\±ÃûÿëíÞ7|¹v®ÈÓûRÅßx¤„䲸žÊz±ÖF×ßÉÒ·jõÚO’ÌpåIõ#{ã€ö7žþrÈŠ©|e¦;BÛ‡ôxVT ‹µiûQ•ÙC¨ okošgÕ>¨;›nnÔ´ ÿ[½YýÄÛ]Ûê ÛT·²¿éÞýú>­†]µY•ùó6uoØ¡¿¿÷oÙqìÓ×ÇOp?ÂaÓ°ø˜s¯jÓîµ’ü¤ õpuej Ðè­–”#3Ä5iOÖ·©Ö´Ø¦#S˼ÚüGŰDU#š¢ŠdBÝÚ½MÜÝŒQ™ñ(ízµö€ßȵÑòw’-GÃbÉî˜QÝ} yYiiÊÆaÕ{:ÂÐÚÓÒÒ³ìG«="FvÔ™g1ÂI’Cn$yPL’W¡ö’Ü×ÎE’ŸaIî9í¼’LlE:û[Toìª tI¥9£ò§ƒ[4f;"¬~úu•9z3”;Ô÷»gÈ< åa oâ,%OïúIÂR’äx&‡ˆ¿­èïv!sFɺ‰xÌvD4«¿=0AyUz ú;Èc H, ÉÉÀK$y?ð«ÒFsH®Bb©òþN@?ñ·…ýº.dΨ$—#¢éý½\}.I—½”$W=¶Žª¿&É7€æ+Hòð!’ Õx…v@ƒRñ·uýº.dΨ$—#¢éýýÔË’Øõhs`"Ih ÄÜq@Ý~6Ú-ã€øÛÂþY2gT’€ËÑôþ 쬴ѵhØyÓUýÍ/ã4ySɵuû¿øÛtþY2gT’€ËÑôþî,©hïϺu\àâd·¿¹0 zÿAò$ÐEü-þU2gT’€ËÑôþ>˜V~ËÑÁ^Hzù›GnPo1Y4(G‰¿CÕ…Ì•$àrD4½¿cÊm(½ÜóO&z¶®¹@ã£d]àë²7Ÿ[Úß!êBæŒJp9"šÞßOqÛÝÿÙ4•œ uB¡ÛßïüE’®güì\çùð*Ø*þ6¡¿CÔ…Ì•$àrD4½¿—¡Þž~¬çòjàY’äcª¿ïynŸ?IN°PýìÁN:®? þ6£¿CÓ…Ì•$àrD4½¿]縳$÷öz€d`(Iº®Æ’ä=C”wnf“4]A’ÜqÖ z4Wú;4]ÈœQI.GDÓû›kâœöànÉfÓµjð)@þcç=PÖ©Z‰†ûIŽ7jöÒOïªÛûñ·Õý’.dΨ$—#¢ùýÍY±êâˆ=ÿ&ÉÅжiÂû9z$­ä=踎dÉåFÍ)îÅ“u]ÿKümJ‡¤ ™3*IÀåˆþæêþøšº:â[õ¤lcq Ébòž^Ý\õèýgtZ üýä3M Õ›%ú´VSú;]ÈœQI.GÄHð7¹ã›Ï6•-erpþœm$yìËÅ…$ÿvÒ±mþç›ËÞP²|vΟN½ H«)ýŠ.dΨ$—#bdøÛ$E`N‡  ™3*IÀåˆ(þG¸¿CÐ…Ì•$àrD‹¿ n;[ŽÜ ýîBÎßæælp™-*׆oæír\UéÆõå\iåºÏ}×wÞluý1e›Ô‘ø;`½mýW­ÕF§þu¡¢N€¶o”š)ªÒ©6hó‘ÓØº (ÿ6iåA]¦\Í{>òd£—ƒj¢ÇæÞÕêãÈDRRþëmÛèXÀpû•Tîëåž_zù óDU˜ Ô€;Ì—€ÿÙPü­Ãqóí×Å@³¿<èÏ }5Ñ—ï8âoñw zÛ>¦sc„Àßþ$•Žóqî}ÏßÜ®7MT®´F“ÿrX˜ à ³%à¥}!þÖçˆx‹VÔ©-‚õ7¹Sü-þXoY»¸$$þö£ Mªõ¡‹déëõ,6KTŸ¶ZO’,:i¶|r’ø[§#â Zý=³¨^ðþf]ñ·ø;½„ÄßÚ»Ðñºî.ñ6*®L¾¨x‘ûs€ŽæÑ Ire|Žø[§#âkðcü»·þN‹¿ƒÐ›#4þÖÜ…>;Û=ÃÃÙ 8Û$QÙŸw¿Út3Ñù>Éã>.þÖéˆè—¿ûŠ¿ÅßÑáoÍ](mŽçåDí©®ÑQ•ñp§‰Î÷IÞzv±ø[¯#¢ø[ü-þ¢ ­/›ž7M{_2:ª2>Bc»‰Î÷Éìº(þÖëˆ(þ›(ï°+ê »#\þöêBœâ~“‰¢*îÖ`I(uScT{›ý—âo?Žˆ•»€ÿ=¶·Ífë1âÛàý]CTý=ýZ›Ívúå·‹¿…2rQ‘ðùÛÝ…YI©Ú>1Xi²¨œégn ¡njŽÊyÅNñ·ö#¢= yšòïPoR¡ùw }лÚá‚U’ ã'5ôAÕß®‡k¿íŒî*W&¹BÛÉ £¿ €%YIMCË´OÿMT¿ÞV:o ‰n4Eµ¾ÎŠ¿5íi€4 þ^ÓòÆ¢š÷©mü»ú>¨øÛq{ÓÅÑ^Câo_­¶Žga•ó77'éçïjû`"°”wÍwŠ¿ýà¿DC©$W?ü";RkœãÅÚ¦³<¯·š&*7×ù±BF¨¢’ñoí °ï~àíoÎ0G7WÛ¥,LzEy-ùÕ…74AŠ#J%£†¼#TóO*ÜÅ`¯æ½´œîyýÃD³DåÆuf[eD%þ®–œòšV“¿y°A7Wוûw65ÆFù5Lÿºð?¿—=wV”ä¾X…!šÿ]a²š»Rìm¦z\ùcÓ?L•‡¯ÐÏèŽæTâoí­öž)Š¿ »g©i¯çiôwu}°‘rÿü'ÞÄGç[¸T jHÝÀŸõ¾SÆ®1ÚÛ!¶ƒJûfèoލ_ÖíYåª×Þöÿ6¼Ò´–•ø[+iÏhž‡Òä65ú×´~UK—¼«ëƒu€IòV vnTWQ ]ø×zVö·×à›¯a?×…ÂßåG!«¼+åP7¿3âDõ-€–oòø­®ý;•¦-*ñ·fÊ_T¨òžÂT ÙQ5ǹÕßÔþ9€þ¥ÁõÁž%É­€¸×J¢¸Šì‡N‡¥ýQÍð÷¬»ÎõŠñþ.?·ª»R.+§ïfEæˆêD Ķm0lqhF)3ý»¯Hü]cœTó<[6€SoüŠSÏ€Z—-¨j¿Œî§Wp|ál@ʸ’í·Õ€ÓnWw^JòÀÊ#ÊäÞÞ(çïkV´T±äú7»Â@S–…ÌÕ·îŽÊµ&óÉgÞùéd»z—U”â}Q!9²û eý½ý<›Ífë’Å=WØl6›­ç|’Ó:Øl=JÈÃo÷K'Æ"1<1kpÜj’;Ûˆ·Ùl$éšÙ@L+=ѨÀ¿ÙÍFšÒû$6Cߺ7eTzðgHçÖU¢ÕÞÁ}кù÷ž@Ëã$]w˜¤œÙl‰‰ßÆ…Ãã¸n€'—ŒJP§]lÍžtÊÎÎÎ#ylpËwÖ­ÒÀZ—‚“kžõ"SzKIß–lΨô¸ôz½žÑ}ÐÂã'M\$y(xWùÃ.Œ#Í sè²›Ïøþ>Ï´¹éžñ“’+î&É/€Z¿[§\2tm³A™Òû$V×3IsF¥Û ¿œuëÚBïÑŒ07"Îßã$ÉG€k•?|ˆßIò+ƒJÉųäþ~“I’®Î–ZJ"WW gJ¯{™íæñ·QQ+pSFɤû³½yû …ýÍ‘À$ÉM@‚2Ì5¢/Irƒ÷-ó®ú•ü}¢…ûÏ·q…–)—]Oƒ4eÙIl–®uoʨt;áÏ’Þ­ ecRÝ­ìï€ÄB’t4>&ÉüÚ’$·({Dhb%Œ?~üøñãûøÙ:“¬ç)cÐM?Ë“9ô[µÞœQ½+sFÉOòœÑäë— kØ•®}К5¨tag5]™õQÒ/6>áñ7«ñ÷½¨3¶ ËÌÙÊϺðH®#ø]¥'HË jWêX`}v%Q…%ªHî ªÀ/œž´¹™i5ïÊt}мþæóÊiŠ«gbv’Îö¨É߃QÛi½ëu#HRž9vU~}=‰*ò¢Š`ò*<‹'8 æ'kÙU¦Ô vïEÌNr!¾{˜L~ï¾è[“¿/Ðø­ˆJ7Ruk­úíÊÑUDGÉTXL6( fiÙ•)û yýÍ¡ÀdòŠ®1èàâµR›¿Ï–Y;Ù’²+ÙUлŠd*®®ÄÅaM»’ôÓß9@ׯ˜A^,Ý;]£¿^‹MŸ°@‰8*=9ªÚg·Ê®dW–'«òóˆóܕԠ¿þ.m,¹ét9 ;©QFOÚxÖ·8Ò§8òK$½r t- ƒw•'»Š ]E0ù>J!ÕÈ]I úëo> \;•dQ$´¹‹Õûû÷r6_xÂ} öFäHŽúFRìJvÔ®"ùŒ4ÙW1d·+©Aÿý½#hQH’¬soÞRnþw=`¹»XZ¹HÒq*Pëm’dáðv'#¿@|6°À†ûdW²+í̽ú1åö·#Çì"I:_<Í ]"×g)À¸]Eh †ÕßÜ uußpî 'ù¨»Pš¬°@yØ}Wx’ìJvÌ®jd€+Hò €:N’¼Às&èi¾‹!Ǩ]Eh †Ùߎvî¶ò^s÷:&žèvãn’÷^¤üg4I.k ÄŽ;IÒõY'¨3ñÊ# †S QE_T5r°5ð‰ûDø6’äOqHØdGN†j0ÌþæJ÷sä ÐðÁCß}¹[}éZŸ=ç÷bk”GºñmÇ.QE_T5“ÿáZåEñ§9ê§ì2A—È7¾@Ó,Qƒaö·Põ]˜ÛŽDéQE0ŽPû[jPü(¹Æ·‡D}QE2Ɇh¦Ô øÛÈ“ÅdÙ•ì*˜]YpL1ר]I Š¿/Ÿ¤Ë®dWÑz}×ó Û•Ô ø[ßd#Gv%» jW–;'M2nWRƒâo]kªìJvÜ®,—Óä·+©Añ·®g‹ù²+ÙU»Š`|¬áè„¶]I Š¿&U¿ådW²++WyÈÃa认Åß ¤êvµCv%»²äIi’ÝØ]I Š¿uj­Á=1Ov%»²^RÔ3È´íJjPü0öd}šª¾»Ê—]Eø®"™Lý¨iWRƒâïÀ)ÈËJKKϲ;dW²+wÑYMNFZZfn~¨v%5(þA°*âoAñ· ‚ þAÄß‚ âoAAü-‚ ˆ¿AÄß‚ ‚ø[A ‚ âoAñ· ‚ þAÄß‚ âoAAü-‚ ˆ¿AÄß‚ ‚ø[A ‚ âoAñ· ‚ þAÄß‚ âoAAü-‚ ˆ¿Añ· ‚ø[A ‚ âoAñ· ‚ þAÄß‚ âoAAü-‚ ˆ¿Añ· ‚ø[A ‚ âoAñ· ‚ þAÄß‚ ‚ø[Aü-‚ ˆ¿Añ· ‚ø[A ‚ âoAñ· ‚ þAÄß‚ ‚)ý]²Î°]Ûçg¯,‰€òwmøfÞ.—9Cûr®ô]8>÷]?Þ}xkØÝ÷Á|©-ñ·6ÓÛ4Ès€ø[ü­™ ò÷EƒÕÑä¼8 ½Ë¼eÑZõÅ £Éb[Ÿ#þÖ‰×àÇøwï0ú{ˆø[ü­™ÇŒñ÷þú{Ý/ïpÔ´EoÞýjÐÍ\±ïøðqñw8üÝWü-D³¿ß¹ÛóòkE@%üÜi®ˆn=»Xü-þÄß¡ö÷—ÛÚ¥P ¡±ÝTe×Ý@ñ·ø[‡Úß^üt€:(îÖ`‰©ÚÛì¿ëíï¿Çö¶Ùl=F|kv/»éL›ÍÖçæ­Ruâïpúû3àEóW3ýÌæ èŠ+œâoòïPoRa$äߣÑf¦K*Nü^Cü>Ó×ÀßW¢é3ÇÌÑëM÷Pü­¿¿·µî³«Æ7›ÁßsãÆHµ‰¿Ãìï0Éìåÿëmõ ó6󄴾Ί¿õ÷÷š–7Õüfø{züë’|‹¿ÃîïwеØäÅ¿yä ê@{ÓÌs<Ù}4Åßúûû»„§µX1ìþv=“ðÔ™ø;ìþ>Ô¼a^$TAá <ô§êÅþ5­_Õ2Œ—¯ž$ÉO<(7Ÿ–šÔßÛ¦ûõ߯ó¦ró'n3mÍ|  å…<þE«kM¨oñ·N¦ÍŽª‰8ÎÝRí›?Ð?LkÖïm\áRqŒ>"U~66(2£¿ß:¨4ºúV½ç=O*?ÿmi«æD Ķm0l±)sñ·lØN½ñ+N=j]¶ ª÷þ2º œ6r\èþuÿxH³‘ém áÐÝR}aæŸÎ0¥¿’®5™O>óÎO'¥$Aðaˆk þAП’+ö•;uþ{ÝÏ;½78¶’¤}ù.e£k×Ê#e,ý“dAÞê#Uî¿ø”ó·cÓ²M¥âoA„ Ùts£¦mÐøßî'¤½Ò=¦cc´¹O½¨Wôõ-MSÈ%g¸xY2¥#€ ”뮵[_Éý£ëq×î ù÷Ml6Û|’ÿ>Çf³ÙìdñÐÖfû?’ä®q´xô¸ø[!\¯Öð¹¢6ZþN’ÜÐO¢ó‹Hø‚$ïo ¥ð¾˜vÖ’KÿìÙ¤ßùõN’/upåê–Ê5º&ëHr€l’,}ÀVòHö;fggÿH’_59ÓüË€ä¿Åß‚ ó,F8Ir(Ð$·5Æë$É-‰À§$O:G»LØOnl Ln÷úIrg{à’¹s;g$_[°áVm“dÕß<¨ø›´—ŸüPû>’töFŠ¿Aæ{ÔÝKRYa/é웺ô›@ƒÝꋸÅ$ÉÌ&I¾ìÖï“@Ì|’äÓž%É.n—TöwA«Äcžï[%þAgLP^•^‚þrŽzëÉc €;IòÏ ÜÏ€fnïã<’ä«@e“£ `s‘ìêö·£²¿_ÆÕÊ»÷xMºì¥$žçãÝ4vüÎãï%¯Ú’$_®Tß? €½÷Æ9ãjBâïœd÷=4é~Õ¹=Õý¹T¿ž_Yîþ\r–‰ºp€Q92“ÔÏ%e8¢$*kpYÚq"8ªHã1`m¹|¼!ð“û?3ÔÛËü½Âãïõ@½ŠþÞàÛêý}8Žõð`üíH÷¾ ÒIdy.]{ËKòú\ªYšX Qå§z}.)/*¢²–UÀ'‚£Š8†;½ÿ({nèR_Víï:ý}Jò^¿×Ïø ¡ð–’(×¾ühbåÛ—YšXÀQeUø\ºÃòQYŒ@Ë*ÐŽÉQE}€%ÞÿßÀ³Ðé3üð7›*â®Æßó€»BéïüJhMÕ¦–䊟KÒ6N—^é ÍUN¥ÏeZ>*khYÚq"9ªä|`ZÅüÛýàoXè¿Û˪÷÷wÀåøûØçU¤n©•Ÿ°­)Éˬü¹4MùAåÏ%…?9 4ª‚¤Ê´[<*kpYÚq"8ªHd0ÆûÿίiÙØã‡¿±ˆ+¨Þß?q‡<ßæ*ÔÃßÛÿsymܨUÚZJž¯ÏeÖ0µ‰?ÔXST>º ’FF•î¨,F eå³ãä[;ªHäI n»û?›¦’ƒFn«þ t¥þÞ¥®ßµêùßGc€©žo=SÝ20¾ý﫾‘\ó>“|~°æ”tŸŸË5Ý艶¨r|~.ÃÒQY}ôD[YÜq"8ªˆd9€êJUÇzn!ç lFáûÀþøû`Ižížƒ¸Ïíï“${MÝ÷ͯêP¤ÏøÉ Uø; yØŽÝSº%Ð¨Ò í/æŒJޤ_«4ç+ƒÕñÉ#Çì y–U '’£ŠH\縳$÷öz€¤³gÙÓ /DÇB’üÖãïå@¢g„ÁãïËÔ÷Ÿ‹³KHrp‡ç|ç7’,°‘$ù€s”…±6|—úøûñ*üí;Å«ù*™ï¡æk,¾„p_c 8*ßg!pX8*$ Éi摤Ó\ò8-«@;N$G™¬‰pÚƒ¼3®þ¹$¹¹!ð#Ir)ê)sÃg­•wÏ Ü]¿ ˆq¨þŽW]ó9üF’|ˆéâ®±ýÚ;I²A.ü/‹“4~xîâã—Nþ~ª 'Ä·Šsuþ5v G¥Ý,'É»]^¬íïW²`ÕÌ…‡¼6–þ1çç"’«<;)Êí§úgáŒEû+<*×ío;Éž§ûä-/ÿ ?—o*7LæÜùs~Õã'fñwx§8eéükr,•µ°ë\ÆéŽ*Zñ¾'@’‡ßî–NŒEbIþ<(@ç÷Õ¼ÅùMJËÕi|ï?žãÌKíêžY¯Ã·Uù;-ÀT-+À½Ý”ãßGeèÙ„9£²–Uš¡ç8æŒJü¤¿àºž$]OÕy"wìӛ$éƒAä–$\¤.P¾¯g­Œã,™ž˜T…¿3l(y6‡)OðŽ*ÕÈ£‘9£²–U¦¡ÇHsF%þÒß‹æ9tÙÍÀçäd|E’ÕUWd™|O’/s•a˜³ðIr>ªðwn€• ¨”ؼCƒ 4ª CgCš3*khYÚq"9ª(eJU+š0~ò€A¥äâÙN®FŠò—Û–$ÏTçÆyÀh’äX$©Ãë—TáoGR€ãe>ÏÔ4Üá›eÂáïÀ£²:ÐlΨ¬E epljਢ”Iîñèàï ~Q7 Ä”²~¾äÅê K«Hr#ð´úÞWáoŸ!ZÖHò¹6‡–ó4_§†™ò4Z˨a¦¡ƒAæŒÊZZVvœHŽ**)½¨·['o ^¬<‹ÁÊszFxäÞ§¾¤êï Hr<°XýpU×/}ÎRÓt»VN€£ ¾¦,&‡}…²¨2ýo¨¼Š®Öet#7*kpYÚq"8ª(¤p\gh42½T/«ÿÿ¸¶ìA=ekÜ›zЋ$ÛÛkôwåS.§[i®pWyT %’½€•'ªÔÿÆt솎S˜3*« h+«€;NG…Ù÷V7.ýýªÏå¢6¥w™€“5ú»Ò3^²ü•‹»h=M«ð„ÔüüT<_@*G´ÐüØ’ !ÒùQeæŒÊZZVwœŽJÐÍß«W+½ùåÊÄ7\ªø{=€#5û›ÌMò¶©M,90C8Ò*´/G:îQ:%ªe`ÈŸVï=VÙ\ï_¡tÇ¢Ê22*‹‘HR1ÙQ :ùû!àÙòïp>sÆ_¤Ûß?øU‹¿Y•–Ééþf;rÒ“ )-Ë?Cäf(ÙEZf¾§­†<÷*¤’Àóþ…bÏTH©±’¹9£²ØŠ»¬2ü;Q)ë8§°²9£ôñ÷ ê(·B¡“|µ6°Ìß›e‚xþ&É»= ,ÍèòÂv{~ù¤!üƒàÉÈ!™È`N¾ÝN2ÈU¶Í•õ†Qìö€>§tœ\ƒVÙ6gTBðþþÀïî?º®ÉãoµÐ^þ.Šn×ìïÀãÒçÆ> ‚ç+­='pÝêš3*¡ÒQ6C¢´ûûPmàb÷‚mÿîuŸÐ53¿¨{ÐãïÌío ‚g*³ó ‚¸ŸÈ€TלQ 0gª+ ¸¹ü½¥lþ7¯ps Iº^ª½¼hy’$Ÿ:ä,÷(ï½dr‡êq4û;#à®öfõ÷Ž3”ä+î3U;0x‡‹k{#æ1'I<Åý¨²%±À{$G50q_¿jøTÝÙqÝV‘ö«’Äß$ÉùëœEõª÷whÁ}ÿšIµ>t‘,}½€Å¡Ou}Gõi«õÊØÎ™ÀH³%à““¢ÕßÁ¥º?kõwÞ‚“¼T»¿C1>À¤þ®•ÒçÓËî·Ž$‹Îŵ¥$y¢7p7Iò&Ï£&»)þ.>ÑH=íéBtQ(ßÃiH*O6+ô®Áß!÷ùkŽ×uø6€1¡Ou}—ñEkÕs€Ž }TÕ±2>'Zý\ª»ÎŸËmh÷w(ð!&õ7F¹Hr€¾$ybÔGÞlM’wyüÝGñ79 Hü†$÷Ä›Irs=÷HÀbñ·‡¾5ú;„ƒà>Ígg»¯`8{g‡>Õõ•ýy÷«]@7†>ªj8ÞñáãQëï R]¿üý˜þAnZ«Ï+`5¹'ýݽXÍ­Ç{üÝ×íïÇ=c¥y$y=ªË9!þöÇß¡÷ùkÒæx^N Kª[CÿÜÉÐGU ·ž]½þ*Õ5Ìß!HÀ‡˜xþ Iþ`ù*p«×©=Öúö÷S÷¦‘Ü‹KÊz¤øÛ‡lÜç¯Y_6=oúT·†2þí }TU“]w£ØßÁ¤ºÆùÛøÜìþvµ†’Iî¿îðJMþ¾XÙô0Jü˜¿C5^Ó¯™L}ª[}TÅÝ,Ѽ«P$à{›ý—Ñìï`R]·¿J±Ùl¿¦Ÿ¿OÀU/»éL›ÍÖçæ­¦¨ ïûwR€¾dWà÷gmà^þ¾ xÄ:þÎLSÔéÆú;4ƒà5ù{<°ÒïT· #ÇÀ¨œégnôc_úEUe@W\áŒn{R]G–¿Êôäß;€›ö處Ÿù÷h´™é2I]xûûfà2²÷t­vÀíý=Ìó+ø; 0Úß!¯Áß®3ÑCsÃTSÝ‚ŒÀËFCT_‰¦ÏÓ¾/ý¢ªŠ×›îatû[MuYIH Ðß'.;e©¾ã'ÁD埿çÆ3ÏrYÞþ~H#»ªÓPH’çkô÷`¨uüm¯èï@³9ÍþÅ x þ^¦uú·'Õ-Èì˜sÕQýz[}è¼- QUÁú:síþÎ YI€ßcΪ¿œßcWÍoöÓßGå—¿§Ç¿î2OUxû{,ð:ypŠ'ÀŽ@–FzZÇ߬èï@§µû;ƒà5ø; ·ù±³`¬Z8ÆDµyä ê@û£¡Ê7'»fÔû›É–¤”±ŸC~Š¿·wt\ÛýôwàQùáo×3 ߘ©&¼ý}9°™|Àr÷ u]Äü]Îß}ªô÷S@ì.ëø;­¼¾>%óÇ߆‚WïïŸìÇH .õ”N®qQ¾ÐÀS¡Ê7÷ŸþøÛñ”§Œýj^ÄrMË šÖIóÓßG¥Ýߎ;NùÕTUáåoGk\Mò¯ø²ù¶ÃIò^ÏœÂÎUú{ €Ëü½%Âý¥Ïð·þ6z¼ZS–t=e—ö])c*™ÆEE®nœú¨|² V=(D±¿Õ1 …Tÿýý]ƒÞeóR*ñ^@þ&*Íþ~äj|c®Êðò÷ ÔÛ@’÷MO*›Aüz’̺”’äºXà?$É'<þ¾Pñ·ó, ÑN’üÀš÷·]Ÿáo?ý]i¼@×ñ”jMyWõÙ;ØþRÓ¬˜€¸’G勃­c”û»œ'øwÁfÓ˜êÙ0•x(•f7:1›¿q$y¬KÌÇÊëÙ€²ÈúÁx‹$¹À=%üçÅŽ©@ê ’œ\«ì¢ð:I~ ã:’ÛÎ0*﯈ö7õþö×ßÁ©Iºþ¦ªÿ6µÞOÚAN’¯é㎶À¦Gå‹Ç1x˜Â Þ°aÞ‹r{v¿óï™@íeî ß]\‰wüöw°Qiö÷c=€AN3UHIOàÂ\ ìUß½Œà>ˆyö$¹ã‚Ø·ÕMÃ4ì€S~ áóäÉ.€­€$Wè$É)Ðï–Ôz¯ÀYŸD¶¿ÓtþößßåÁÓõÍ"«QÚ¼ßznl©Q´#€?C•/&Vü†Œ.E‰,¿ýÍ;€Söjx³v•fOÞœ“=«­èÕ³„öq Çì.+©íÑòʾõ†lpo9: FâøÚÿ÷þ!ò‘öÐjäÁÃ×Ö€.ÿ!ÉùÉÐòcâÔ‰¿ø7ÉÆ|þÎÒeø;{ ‚gé<Œ[µ)—&úó8°\]ûKþ~±E!Êo—ÒuRRR¶á“üŠªLóßß…=€~Å:ú;ب´û›3€˜“ÕÉþ¥Ÿ}¶üd¹M® sgüXn¤g÷7 ŽÜp¸Ú]mþrf®ƒ\Rêw6]Kµë2üˆ¿=ƒàyÊ—çïïµMgy^k™9”©ÓÝMÚü}ÆUYtªÆ mŒFòÌÄ,eþàæMë4ø1þ\T~ø›··Q0½¿©Ëðw@þVÁÝY…~CàU™ò–Ó=¯˜¨eO:ö—šüí:3´í)Ãø^íþ®t–ãW×PïßÉð±žþ.*ü]Øè^@ÁôþNÓEçäoæ%!©·î)ª0¥½gB€ëǦhÚUjùþR T¾B?­ƒq:F%þÖv–ã×,û_Ôén·ukœíô ?ׂ‰JW+÷_njŒtQ0»¿³t9oàüTf2û6åÞ±TÚ7+[ü½†1žTÝú‹Ï¨_ÖíYE¿{Ûwü[ë®tŒJü­í,ǯֽˆ)¥’Å&m¯áÍ#€KC•6®ž$ÉO<(7½¿íz  i ŸüwsÔiܧ)u è¢_ùkF™zGõ-€–oòø­®ý[û¾ô‹Jü­í éÏÉ¡ë ËÔD-çWkÁí‰@Óý!ˆJ{›W¸ÔD£ˆ³Mîo=šöËèþ1pÚÈq~+¼ü€^R¾N¿ÈÇÆËÊ}S³"­;Ë׫¿øŒêD Ķm0l±_¹N¾±½XüMÒ‘ؽ2ií þµ/sþ5€³ÿSÕ[ß:¨4ºúÖMG¥‰¯ûÇ@ò˜Lo ‡îk›Ûßi¡¸¦)RúHGÏsuºWÆ÷‡]k2Ÿ|æŸN†+*Acë´KT‚{ZV(æ¡iÉ%tÐÓYhYúôsF%TƒÝà{e¬•ø;ÜÍ!ôöÓ*/ ¡Çµ8½Ò ]ú‹9£4žå¤IT‚)Ït œLªÙ@ºë> ¦ÇsF%h<ËI’¨Sú;-\M!3ɇÀu×Ý”eý“¬• 5ÇÈ—¨Äß&Î1ÂÄ9²’+ <ø!pý/è$¯Œ9£j 5³ì­•ø;ØÃ5üM’¹Éú0!ÃsÑ?×bQ ZÏr2$*ñ·9£ ë™X^šï!p{NFFŽÝ$¦ô\3ʰXT†&¦Œ*ЃdÿÜÌô,»C¢Ò)£ “¿ æÚÉ<§b©yáêçéåžì.*C&D甥·JTVnWzɨ|ãÌ’¨jðUº£ Ÿ¿ÇË*'ò£"£%ª@)ð5°—.QENT:ûûPÛŽ{È“ã0XÙøyƒ„ÙE,x!7’Ü4¾)€EÓ ÿŠìKk²³³÷ïç0ŸéÚ‚ŽÊS&Y4*FK»ÒqDÀ 3î%ª°ùû_ø€$É—“$?‹©µ†$ù)Ðx+¹~ÞÃ^éñÃsu0¼Ù°ñ;ÌXÚ:De€)-U„·+ç+ƒ§)¯æ^ýX!IòHúµº~•ï_îûf$ªpùÛÕ³__D’»âFõ¯#€žN’G´Í'7M;j¤¿s|—vfx‹H‡¨ 0¥e£Šðv5 À<’Ü @’›âÚ¹Hò3,#É=§WB’é€MÇ{T_¥$àÿ§ ŒÌZþM’œŒ3”Ue³ÜÉšfÑØc!9Ó5ÅYn°QsÁÛšQ™³Í>‚’/Q…4ªïQW£˜¡ÌÐÛïÞïÃP“zg)yz×Hr–’$÷À“ëèoï˜Å“ž IiY&J’‚ŠÊ°,U”µ«€ÉÍPHi™ùU¨£z Xëkû®G›Iò@c æ÷²“g£ÝØ2t\ŒÒ¤w"9ìv®®ïtÍCoø²XTæ¬Asb·›ñFpsÞž®kTC•6º ;oÚ£ª¿ùe<€&o*¹vC£.å‚‚ñÆ3ö†]kEeΤæ½è,©hïϺu\àâd·¿¹0 zÿAò$ÐEü-þ‹¿Åß&ˆê|`Zù-Gcx!éåo¹=@½ÅdРXü-þ‹¿Åßáj0¦Ü†Ò‹ÐÁ=ÿd¢gëš‹4>JÖ¾.{ó ñ·ø[ü-þ‡)ª'¸íîÿlšJN…:¡Ðíïwþ"I׳þGö®ó|x•ŽËgIsK/¤æýb9€êô¼c=·WÏ’$Sý}Ï{tûüIr"€…êgvÒqöª4gñ·ôbAjÞ/\縳$÷öz€d`(Iº®Æ’ä=C”wnf“4]A’ÜqÖ zþ*inâoéłԼ_¬‰pÚƒ¼3®þ¹$G˜MתÁ§}ø÷@Y§j%î'9@ܨÙK?½«nïÄßâoñ·ø[ü¾¨fŪ7töü›$@Û¦ ïçè‘´’÷ ã:’%C”5O¤xÞÑur¼4gñ·ôbAjÞ_V÷€Ä×Ô{yߪ e‹»M“÷ôêÖàªGï?£“ú,³“Ï4€Vo–èû«¤¹‰¿¥ Ró~³ã›Ï6•-erpþœm$yìËÅ…$ÿvÒ±mþç›ËÞP²|vΟN½•47ñ·ôbAj>2ËZš›ø[z± 5/þ––)þ–^,½Dj^ü-þK/KÍ‹¿Å”âoñ·ø[ü--Sü-½Xš‹¿%*éÅÒK¤æÅßfh®Õ¦3eÑŠ/üeº¨JVf/=hÎ^|læÏÒÊ *c×S¶ißI5½)ŒQ‰¿-ëïEý˜Ì”ûnO€óç›*ª#ÀùËÌèï[p³´r#ÊøØÜ»ÚK5Þ¾¨ÄßVõ÷âK“ù{y3÷’ ºÌÕ–v@`†ùüý9Ä߯”ñËwœí¦¬¾7…+*ñ·UýýÓå]j›Íß»1èÑŒá-à-ÓDu¤s‡óàôæ@Ãfó÷ž¦âoÃÊx§fSÖԛ•øÛ²þ>öÉQf˜ÌßΔ¤U$yôv‰GÌâï»ú%IþQxÛdþv¦&‰¿+ãºMYco KTâo çß$¿1™¿¨í` à#“Du¨‹{ñã»[Mæï)IÓÅ߯•q¢¦ü&dþN‹¿I.0™¿Çßã~õwà^“D5û÷«€»ÍUƒ¿Õùökñ·9L¹@ü-þŽf;O/›7Ø«ŠT7¬ó¿ß‚²°½ij°¨ÛxŠ¿ÅßâoñwøMYº¾ìõ(`²éü=½JMUƒ÷œyBü-þ‹¿ÍeJ^ |n¶¨¶Õë´ÏT58?~ Åß!0åôkm6Ûé—ßn.kŠJü¡d¦)ê‹t3ø[CT$É®hRd²¨Žö½úˆ©jðÀ)ÏQü¹Ë8­ÀW¦ëj‡ V….ÿÖ1*ñw„’… ˜Âߢ"É¿já“Eµ¶:Rj¢t í[*þ{Å2Nò•麮ývOÓÑß:F%þ¶JËÌ1ƒ¿5DE’Ï¢ÅA3Eåüv` )0O ¾×ðOŠ¿ƒ¢¨î©˜Òq{ÓÅ5ïIÏñý¢[¥eæ›Áߢ"y²²LÕW#w‹€á.³Ôà–Pü4iÕ#¥‡t¿ ‹¿«‹êÛ¦žg©—ü÷4‡I¢òp7W Š¿ƒ'«šcd#åNõOª|žŸÖÞ¶¨"Ýßüç÷²E+fEmËô9¤û&[rWÕ’º TÚ%àIsDõs>3•e&rã®)5W Š¿ƒÇ^Í1²ð#IÞ Äέa?Õö¦°Eñþöâ£ó£¶eú<=ÙÀ‚û»ê¨Ö4ô/¨µËQ= GN)÷¿\ï‰bsÕ ø[ŸTyŒ\àY’<Ñ ˆ{­¤º½Tߛ••üýk½ó£¶eV<͸®Ä]zËÜÐú»Ê¨7+7ij9¢â&ÔkÓ,}‹¹jPü­iU#_82®dûmuà´ªŸ~ZSo OT–ò÷¡Óq~´¶Ì¤@Ì!Q)Ï›òø‹Óóœ‘Vƒ‚6²j¾p-Q…Ößv’VQþs°€$÷öF9X³ú`Ô´Ìt3™ÒzQ™³mØMyŒ´[öÈ rûy6›ÍÖ%‹{®°Ùl6[Ïù$§u°Ùz”‡ßî–NŒEbybÖà¸Õ$w¶o³Ù2HÒ5³7€˜þK£¤eæ˜É”Ö‹Êœ5(hnTf!>Ýaù¨tö7‡“É+z¸vÄ ƒ‹×^Hmþ>Xfý†™\ñ [IÚFÄÒ+=¡5O¢’¨,J e•Sés™–Joç\¿by9°tOìtþL,ûû k6ÌÌÊOÊNÓtâVùsI‰J¢²æàI€eUTùƒv‹G¥»¿KÛKn:ÝAÎÆNjT Ñß6'Ý>Ò§8J¦¦³0_M@›6Ž*'À¨Ò%ªÐGe-.«TŸKvX:*ýýÍ'Ëc§’,j‚„6w±z$“$¿ð„û0ì K¶L_M¨ùü;Ýççr%*‰**FO´•UŽÏÏeX:*ü½#hQH’¬soÞRnþw=`¹ûçµr‘¤ãT ÖÛ$ÉÂáíNZ±aÚ`€‘m@¢Šô¨tbãÈ1ÊÃ9ޤ_«þ¢¹W?ú;é-«4ŸŸK¶tTø›€gH’ë€ó<[°ßýŸ"ß‘$×XB׋Ë8 .yñדZ5YoÉÄÂ÷¡¸æ«ù¾?—*QITúá´$G Hrs,ðp¨ã¸¬|ŸÁaᨠñ÷$V^ƒÿ¹7–Þ Ãý`æ÷á~J³«Ðø†s9ÉGÝ¿­É kžf@_$*‰J?¨ã$ÉnPÏŽç¸"Ôqäê\Æv Geˆ¿ížS_½×Ü}öõài€n7î&yïEÊF“䲦@츓$]Ÿu€:Yt`/Õ”m@¢Šô¨tbpIò {Il |ê02u.ã, Geˆ¿¹ÒýðÎÂ4|ðÐw_îV_ºÖgÏù½˜V%É”½_¢Šô¨t¢øÓõÕYÇÕQƒ׆<ŒtSš2=Šü-ø&Mç6/QIT–#Kç2αpTâo´Ì'rÛ =•¨"=*kpYzŽcΨÄß¡$/ÀS)G€Ö¨¢#*kpY¥yŒ4gTâïPRà»*k^E!9°YkUtDe1-« Cçhš3*ñw(ñ9|ªá^Ú,CO%ªH*†k.+»¡ÍæŒJüÒ<)À‘°T#¯`KT‘•Å´¬2 ¢2gTâïP’à9t¾¡g`U¤Ge--«Êkûj]Ü7r£‡wEãJdY†ž{KT‘•ÕGP´••ÝÐq sF%þgÓÔü(޼ò'î:?~K¢Šô¨¬E eUááH:?ªÌœQ‰¿C{r˜XM:ÒŒ9õnb~E•.Q™#*kpYerhì¨Äß¡mš9éÉ”–å_Mæf(âHËÌ—¨$*‹hYÙ3•D'5#/j¢‡Úáv{`—0ìö|‰J¢Š-«|»=ê¢ ‚ D'âoAñ· ‚ þAªçÿi`ÿÂ[Ñ2%tEXtdate:create2013-09-23T21:01:14+02:00!`Ñw%tEXtdate:modify2013-09-23T21:01:14+02:00P=iËIEND®B`‚stxxl-1.4.1/doc/images/install_windows_boost_cmake.png000644 001411 000144 00000057643 12350112610 023011 0ustar00tbusers000000 000000 ‰PNG  IHDR트§_jIDATxÚíyØ$EçëyöÙ}vçØ™{ÇÝ9vÕqtZ^uÚÒ±½p¼Fi}ÛkT eJ9ZhZl±›K 9º€ÆWŽæQ[iZ)ÁºgVÆE_@PuÖùcñÏßVäU‘‘™ufU}¾<_ºÞ¬ÌÈȨªÈOþò‘î¿W0ÆcŒ§Á¯|å+¥ÐJ~ó9GÉMý—GËo¥~î1òÛ©ŸwŒü·çû¯Ž•ßQ^±.òï*?ÿ#‘Oùåãä÷•”—?P~Aì?|ÁzùÃÆþ£ž ô×=ÿ÷mÈüÇ/Vþ¨üñK>*ψ|¢<ãoN”ÿ‘zåÇä¦~éFù“ÔÍò§ÍÇ~ÙÇåÏ^vRì—Ÿ$ùòçû ù_©_±Iþ·ò*åÍòLåWÆ~Ö+O–g½*ö³_uŠ<ûÕ=ÿÅkNÍüœ×ž–ù/T>=òs_—øï>)ÏKýú3ä¯R¿á Yñ†3c¿ñLyþÏŠý¦³ä€È[ä€7o‘…ÈgËÂߟ-/Hý–sä…©ßzŽüõ[Ï•¿>(ö‹:O^´ºç¿mkæ—¼½ùo•ϼrò‘_úŽÔJó=¿ì]Ûb¿{›¼üÝÅ~ÏEò·‘?%ûŸ’WD^’W¼wIV¥~ß§å•©ßÿiyÕû/Ž}ðÅòêƒ/‰}Hì×r©¼æÐد]{Yæ? ¼=òëS¾<òßýcê+äõ­žßðÁ+3¿ñCWe~ÓáÊWË›Ž¸ZÞùyó‘×Èß§þðµò–ÔG]'oM}ôurÐÑ×Ç>&öêc>#«ý¶co·­‹ývå|6òâqÊŸ‹¼æøÔŸ—w¬O½CÞyBÏïÚð…Ìïþ¨ò#¿çDå#ÿÃÇRï”÷nLüñ›ä}©OºIÞҮ؟Ø%âK±7Å>dÓ—åͱU>ùæÈk•OéDþ€ò©Ê_‘ÔOS¾Eþñôž[Ÿ¼5óÏøj橼;òág¥¾MŽØ’úv9òìž?|αϽCŽ:wOìób}Þ^9zkìc¶î“cÚ±U>ÿÎÈë”/P¾K>¢|¡òÝrœò¶ØÇoûšQìõŸº'ó KÊ_¼áÓ©ï•^œúrâ%=ìÒû2o¼ì›=oÿ¦||û?žüŸä¤ËÿYNº"ö'”¯ü?‘7)_¥ü-Ù¬|µòýr²ò5±O¹æ_ä”kcŸzí·åÔëbŸ¦|ýþȧ+æÈŸ¼!õƒrÆg{>ósßÉ|Öç¿ÛóŽïÊ–ÅþÂCrö¾û‹ß“s"_ιñûr®òÎåÈç)ߤü°lUÞ»½ëi)öù_zTÎÿrì ”oþAä •;EÞ¦üåÇå"å[”(Ÿºµç¥¯þ¨çÝ?’OïþqìÛ~,ßöDìÛŸK"ÿ«\rǿʥ‘Ÿ”K÷<)—¥ÞûÙžzßOårå;c_qçSrÅ]±¯T¾ûg‘¯RþÚÏ#_­|ò/äå¯+ÿ›\«|oìëîý¿rÝ7b_ß/gÒ@;B@;дí@;дí@;B³ íÿùEÚv hÚv ½~Ð>ˆºý°ê´#4 ´w7“Wý—gí@;дí@;Ð^Sh$g|Ðí‡U ¡ ý?þ΋»?–=²© í#„öß}ÇnyXÀc»åµ@»ÚÏz¨ÛH?—í‡ Ú·.Ç¡’{ovCûqßÇÕû?þ†¼¯_hßöp¼oÞšƒöÓþ)~ëîOí@;дíƒ@» ÔhG¨†ÐÞhlêZ"h/mÖår‹}ßëŽ+å7ßv«$Ø%o?£í'þK²ô'ráâ8 ýzùªU½G®j;¡ý W?e®xç ~h?x¯<ªÖy|¯¼9ƒöÏÉmÖ¾½n[uhÿäws·pÃ%^h?ñµÆCò±`¤ý&Ù•ô=ùxM¡ý࿈Š}|çg€v hÚö)ƒv{}ßr ¡¡=޲Kí{JEÛS`R.8ÈŒ´gÐþƒŸÈÃ?¸U^Aûraô÷˜¡}ßõI¤}«,E4ùSYú Ú7?“ྡྷ(ûGö%Ð}õù´ÿéK?k‚¹ íw}.‰´o“K¢}ýL.ù@ŸÐþµKDÚ/•í?ì®{ÏMé1õ‡v"í@;дíã‡öÐ6U#í6 »@hGh@hO£ìÊ««¢EÑöW\údTþò¥ŸÌ¥Çô ýV¹à‘[OìBûÛ»Ë î´/~5‹ÈK™ßâ„ö£ö&+tÿ>PÁúIûÍ »M,HY—ÀøW7y ýñ=òÆ—œ(oL¢î»7{"í§<è€v3=fý]ñ*·Ÿ6hû ?Ï¢î´~·¨æÜ³¥—Óþ±¯[ÍòÙí²ñëvìþ{²7y`îcŸ¿<‚öw}>ŽtË¿.W¤ïí¸2‚ö÷ìHß»WÞSÚu=qŸ¼_Aûùɧ}ß-´Ÿ|_rSãÂ.´_ØuÚOûf~ дí@;Ð^/hwEïIAhˆÐ®GÙcoŠ^EÛ¿=†§[6„öU*%æŽ+åÈÞÏ4¡Ýˆ´o‘mÉ{ÛÖXÐþñoÇ;Û{mi×mòˆJu¹âœ(Òžü#Wœë‡ö÷Üm㌴¿¤Dáµ4š4ÊÞ´¯Ý§Ï iÏôsÙþA7´/FË’`_“¬£@½L¤ý¤è+ò ¹âè]ñû]`wiOß»ò˜/˾Øûˆ´¿ï‹IÚË×õí=’¼þ*‘v hÚöšC{(UhGh@h×£ìj ê|(Ú^Ú£´˜øõ«ž“‡öWoÿ‰©6´ÿ$ƒ÷“ô˜¯ø©{©}×¹¡=vEwÈëíœö÷Ý¿ÿøƒ²[ƒ÷¾"퇦À®Þß'o±sÚOÿŽVáŸÉ¥‡õ—óœ“Ô˜Þ-‹´ëQö´/¼ér¹âGéÚß—“Œô˜+åÊ÷ÞÛôGzL{YÛÛ/äªä¡ý%ç/g Þ´ŸšDÙﺈô hÚv"íÍ)´§3ƤÀ¾uëV9äC2hEÛ˦ǬÒg± =ö‡·Ÿå³µÇíÝ¿~,I¹ülÚÓH{pö˜Mh`ù€¬sÌ“¦Íìޜ̳ùAs0jYh?õAm_Êú~f) í;eOy¿ÔœòqË÷Ì ™¯ß€ö$Âî„ö$‚ö‘v hÚv œv ¡¡½eß#s€´ÛmÙ»wo4µ8Ú^f jÚ?¼G4h¤Ç¼3øŸÊEïúˆü^–Ͼ_Ž A»a¿=а§S>¾þªü¯n÷fŽI¢¦9íj jöúš ÂÐnGØûò±dNûsÎL§zôÌÓ~Ä×ä±hS`º°þf_zÌ÷åo,=fó70?_Ëiâ>9¸$´zÓ¿ÅMzÓ @;дí@»gÖ–²îQA{ƒÙc´÷rÙ÷HcO÷G¥þëþ«à]‡ö`n{Ñ”ÐþÛϽZnÕÒböAûó?"¯½kÚÚv hÚv hÚš>hÇcŒ1®£v´ícŒ1ÚÚ1ÆcŒv„€vŒ1ÆíícŒ1Æ@;BÓ í»ÖÉÂÂ:éÔéÇ]Ç:M›iCŒ1Æ@;BSí ÞÔƒ– ¯–¥ê† w;d½¯Ø ëwL8;ëWT¯^IB°kßEõÚ1ÆíM´Oî)wÈuŠ€Ý(o«¬)îu_ cŒ1ЎМB»+ÒnDåWȆ]CM£ìÕ²Z®mkDÈû‚PèŽc1޵¬Ñî¬Ù–¿k°pØ¡žv2ë¼aÑ.gÇPí®Ï­ðø0Æc ¡)LQpkÁô ‘Zcû8²m@ìâVÿr½nZ–µú µOŽ7Ú‡õÛV;/0ÚÉSgˆU9Ã<GÊQ#ô¹mè9>Œ1ÆhGhÊ"í>¸´;£îÚ…ƒwy ÈC«‡Ús©3V´Úwg¢èXFy EŸ›wÌ‚çÎÆc ´#4%Ð>Ìè¡@»™ƒÞø–I%´áŠ ½lúÐŽ1ÆhGhÚsÚÍ´ážì?=fM6XTå˜÷›Á¨vû¤i-:—MqAû0Ž!˜ÓîùÜ|ЮÆc ´#4íQƒÁÝP¢¦Ñé>¦,˜ò1ƒÜ\z‰ _Z¬6ÕŸ3À1TˆÚèåÒ;ÓgÈgÇc ´#T?hÇÓ1­#LJ1ÆhGhÇ@;Ÿ7Æc ! cŒ1ÆhGhÇcŒ1ЎДC»úA`Œ1Æ×Å@;BDÚ1ÆcL¤! cŒ1ÆhGhÇcŒ1ÐŽÐŽ1Æc ´#´cŒ1ÆíícŒ1ÚÚ1ÆcŒv„€vŒ1ÆíícŒ1Æ@;B@;Æc\O£ñJ8ÐŽÐŽkÁã:9tÎã|8þ¨9ÐŽÐŽkÔ)cŒq\tÎã|´#´ã¹î”B¨Úv„€vŒv„ÐŽv„€v ´#„дCohÇhGíhGhŽ }×:YXX':vu®ÛŒµÁP }¹-Íf[–‡}öU¹! íÕÚ ØSð×X!v¥ïo•5Õ²4(¬²}] }rЮ@¹ÑP®æ¦´—ClÝ”¦o…AÀhGh/‚öègöYk¶Õ¨×êµ°~ÐŽÐn:‚òäugýŠ.ü­èub) Ö Œöú@{EPî´P´#„F ív ªç ³f‹[v„€vÓK‹½Žbi± ðÛV÷®ðÓ×IǶaÑ0¢IYÆúÉEk=Ogª.²HH®CÝ!´hÄa‡V«›uüÎ}Ûiõwuö®c:`«>%êR¼]ï+1rÖ]ÛOî.ʈ¡=ŠÂÛ€Þ‘V£ÕýqÏꕱ,ífïxší˺ë6zQù¬¼Ž{ùn ! ½"´wÏq Ç…ç,Çy z?í“Õy)~]|NqõÃ[š??ÎóÐŽPí =Š®GÞítU'Ðý'ÁÒ¢ùÏ`Xut ɺ6H¦‡qÛѳž«35:ÂäB¢(Ò^¥n¹ÎÐÞWÜÁ¥åE°Ö©w 5¾¨0o¹–¨‹w;G]¬å½òû´Ûé1)8» ½Ó’F«“¼Ã{¼8ym_dë) ×Êr-'ÒŽÐ^9=F 4åÎY¾¾·çkºçŒxä¼é=§õÿñù6ª“aŸÔùhG¨žQÕ6í¢Î"ô"E„™ÓìÈ÷­çñ4*à‰:¥ÇÕÍÙt`i'\Ùè^”=þ‚[¹Eu)<æÉ¥ÇtZ iuÒˆ»ª[Ü}å¥y;>·hGhï7=¦¨×`<½;yîüé9§Té—Uà)…÷I€v„j íéU÷¶ÕÚÕüên§Ô‹¸—îèÉÌ­€÷²Ð^:z íkTdåãÂD­rÚ­h¹8àÝUžŠÎ; =·hGh¯ í¾ÉÐEØ·¥çÇ$¸¥? Ï)UÎË“:íÕÚãÈ€š¥N¨\ö…½ÜvoG`¥ixp”¾Öé¬ÉFͫѓÿ^”ÛWrŸù}nGZƒvNP.Nsq»jzL¯]²º„ö3éœv}ÝîëVï 3Åň¾§9ðê_øËv„€ö~rÚý¸¯ïMÎÚùQ¹TšLšfStN Þñ.8ü|d•[ø7ÐŽÐø¡½}æí³|δ?§ÜxSæ–œ÷êß4ã›ö*ÍÿK¢–®[`à§q‘â¸m¨0ZX\ín[ûÍüqÕ¥ÌvZg™ÞBÕëR8@u 9í:Dk mNõ˜Fع(ºJ¡Ñ¢¶:zd=uÇr ! ½rN»'ø8äfzÉÆQ•=§O°ŸD`Lç# ¡úC;Æ£~¸R/Ÿ!„&í˜)Ú1ÐDölªG„Úv ítR¸¦‘v„Úv ! ©S¦0Æu2д#´cì8"„P#î@;ÐŽÐŽ1ÆOi A!v„€vŒ1Ƹ²âñhGhÇcŒñ ¦Ô@ohÇcŒ1ÚÚ1ÆcŒv„€vŒ1ÆíícŒ1Æ@;B@;ÆcŒ1ÐŽÐŽ1Æ›FãS>"´ãEE0ƸN扨õŠšíí¸F2Æ×åA>@;ÐŽÐŽ±§ƒE¡ahþhÚÚ1ÚBc‚ö~¤"д#´c ´#„Æíƒ"д#´c ´#„€v ´#4Eоk,,¬“NÝ:Š~ëåÛ®Lyƒ´E]ÛqÜоܖf³-ËÃ&_¹ýîoØå!„€öIG|þÚ´?bõ£n¬ »Ò÷·ÊšÆjYlVÝO¿ëWíÔô壀öaµï˜:åÀ6ªÓÕÜ”örˆ…›Òô­0 ²Óõv„fÚ£óžÙ‡­ÙVø®R¯~Ïo@;BÓí”'¯;ëWtÔ+zCQ'0mÐÞ/<Ú§9ÒÞÀvZ¨¯´iGh"ÐÞÈz)´ÛÁ«:ôÓýÖ hGhv¡}i±Y_Zìü¶Õ²°~Gü~ú:ù±oXt\ñÑ€¤,cýä¢ÀµžQ—²a¡W~¶ßl›ÞÅ…Ù ­–5 îýw|Âõ“C®S³êuØ¡ÕÚ¢J}í¬.¨Eíì=®^]ŒrŒu]ŸÉ€ÐEám@ïH«Ñêþ?ޏgu‰ÊX–v³wìÍöeÝu½¨|V^ǽ|w²[ÒjZwìºÛvý}ûŽÐŽÐH"íEÀ>rhïž÷‹[KœÇ´eF_šž«Ô¹#~íïw‹Îþz çü´#4UÐE×#Hïv0‹ê½µ •ñ{i1Æä‡ŸÁ©ê<’uu˜Ö; ãVžg½`ç©¶± UuX¹Û‡ºî x;5s¥#íUÚ¢J}½e[õÑ;ogýCŸKoyt‘6 h·O²èZÐÞiI£ÕIÞ‹á=^œ¼vt´žz­,×òPd\«Gt±àJñB{|‘ÐêiGhé1!`}zŒÉÇ\ç¤^«–­éö«ñ:ɹÔÙï–=_4¼Áš¡ß€v„¦h ªú!§u@it %v¹¨ˆ#Þ·^•4«ã{ ´NÙN­ìíÇ¢z…Ú¢J}CõqEL*×%îÄm?¦ô˜N+…ß„¸ûÊK#òv|n¹¶­ÅonÜè¾( í¾õ`.„F–Óîö±¥Ç´sRzÇ:òÜ9ÕÑïö{¾¨”sÚÐŽÐ8 =½âß¶Z‹ ¬îv@½ˆ{éÎc˜¹xÓíUgž) ív$¼lçüÌ«ÃûàÐnEËÅï®òTtÞí¹åesÐv„jícˆjô—ž ç¤(¾-=g&/휚ëwû𩬠^@;Bóíq”@ @Í På²/¬èå¶{üÖí·`gSbD¾æ%Óc´ýdëX€ïLCÉêïHW Õ«j[”­o/L |.kÒÏ4:±è·ÇÓn刷zo˜).Fô=ÍWÿêÀïX^Ú£(¼#E'—6c¤Ç8Òk`.„fÚÓ´Ä‚ó˜qNR}«vÎT/•&“ö¯ù~·úù¨נç7;Vô7ÐŽP} =«Þ\é>Ó6* ˆYZ¬8ÕS¦>ðgaqµ?¡:B{½P½’¨¥Û¢L}KDF–´Á©¹ã)]—4Ò£µñ(rÚuˆ¶`·-O#ì\]¥ÐèQ[=²ž‚ºcy0§Ý=ˆÔH£iµüö$ªo¬s!4ÐîJS)<éç$ÍñH¡~·ÂùÂõAÎo@;BÓ íãáJ½|v„âáJ<hG@;®!´÷¦zD¡ô\¥ú•~ ´íí(ÒŽBºT2ˆv ! cOK;`Œëd hGhÇØqD¡:¦Ùí@;B@;Æc<¥†~sæqÿã €v„€vŒ1Ƹ´Í™Çý3ÚÚ1Æcx¥¼ñCWÉ¿JÞtøÕòæ#º>òùû#¯•·|¸ë£®“·}tôõrÐ1×Ëêc?#o;öyÛºäíù¬,§ü9Ysüçäë?ßõyç ;ä]¾ ïþ¨òå='Þ(ÿð1åòÞ;å}¿IÞwÒMòþ“vÉÁŸø’¼éKrȦ/Ë¡›»>ùfY{ÊÍòS:òS¿"‡öùÇÓn‘<ýi}òVùàÊ_•¹[?Kù69bËmräÙ·Ë‘çÜ.>ç9êÜ=rÔy{äèóöÊ1[»nï“cÏß'ëοSÖ]p—|äÂ»ä¸ ï–ã¶Ý-Ç_ô5YÑ=²þS÷È K_— ŸV¾W>zñ½râ%ßèú>ùØ¥÷ÉÆË¾)·S>¾ýŸä¤Ë»¾âŸåWüÙte×W}K6_ý-9ùêûåäkî—S®ý9õÚoË©×}[N»~¿œþåä“7< g|öÁ®¿#g~î;rÖç¿+gíø®lÙñœý…®¿ø=9ç‹ß—soìz粜wÓ²l½éaÙºëié9ÿKÊù_~T.¸ùraGù1Ùö•Çå¢[”(Ÿºõ‡²ôÕÉÒîɧwÿX.¾í ¹øö'ä’ÛÿU.½£ë=OÊe{Ÿ”í{"Û÷ýT.¿ó§rÅOÉw=%WÞý3¹êkÊ?—«ïù…\óuå“kïý7¹îÿW®¿ï—µ2ÐŽ€ö ¦`=õÄÃsí´Íè¡ýÊ}’hÚv hÚv´ø›J0ݵNÖIgÚf€ºÖ©Ýjí¾c©S{ ÚïÞwk_ÐþŸŸqPd hÚ§Úµ*…ía•3]Ð>„дO´wÖ¯Póg^螀öÔ;dÃB·]BuÚû€öÚÕµ\-ӾñWȆ]ã¹ØÚÿùÞ;åªË?Ý´«ã|ÕyдíSíƒô;:´£œá@»Õ) ,€v }Ž =vãûÞ…©Åh] =ªÇjY³À9€vWû§nþ‘—n~ºzŽû íêƒöªÇ]hWþÊ—vT†öÿø;/îž ÷Ȧîù°L´}äÐþ¾;äuï‘7í@;Ð>0´7¬@ÄTAû#j²UÖ,nÚÐ>˜nµ~WùßÁ†EÕY¬–¥Ü…³¶Lÿ½¤gPÖëpÖls]€kû·£¥5€vuQ£K‹V ÀU×hYÒ.YûÆ›w3’c6ÚØj£h[kùˆÛ­ÚÏþìÏäì~Öû×~­¹Ú+µk™c,ü|'‘þ4tØ¡Ö÷=~ßü<ü“Èio46u-´—‰¶Ï´¿{Ç/¢6|lÇU@;Ð>ÕÐnƒº Ü‹ ÝûöòA»~2ôEà{jñI2-Ï:9nœH£}ÜrÚöiöÐjò»²!2ý;ú}© ç´ë¿•m«“ßÜVó·œmo–_H»·äTý³úøëª 4[®¶qÒåvgû°Ñ¼U»AûæKsz“ñ÷ãÑ¿ah¯Ú®%ŽÑ•“}]ml•“û¾[ŸŸï³œ´ÇQvÉ }O‰h;‘v hŸ/hï·œ¡@ûR ÑFÇé;‘•~c_é)ÓcR`ߺu«rÈ!´‡¢íÀö¯ˆý‘+·F9íëö9 ]ºÐþx¼Í£×\`@{i/ÈiÚv }8S>N,§]é_fæ„R'I×Éq3дO´+à,ë±ë§A¬}ÛÌÈQ_ÿîÏwý¹èß™}"ª1Pvt‘ö»öÜR Ú{Qö=rÀ1H»Ý–½{÷FƒQ‹¢í…é1% ý˜}¢A{ =æý{âh{à/>øãòg§¤ùìÊúÐþö~Wõ†KýÐþ£{ä@;ÐN¤½ž³ÇðDT´›ÐÏ1Ï´×¾mfÚ+ÍË>¥.€:Μö^.ûiìéžtÕݼëÐî‹¶D-†ößÁuòU--æ´¯ü˜¼éš§2P?^ D=t_ò=ysÚ_{±lÿ¡g ê¶Ëå?Ê¿´í@{®´# ãZ@;?´ÇQöM=`×ÿÛÔU«b`÷EÛ§÷‰¨—&ÿldÊG ô hÚ1Úù=ÔÚ£(ûªUÒ(úoO ï®§¤N´ß(w=öÏeûáÌÓ´Ï.´«>¤ÛÐ>ŒræÚ9‘)ó{Ú‡íÑ-òMÜ÷ÿ`EÛ§7ÒÎÕ€öÙ†vÕw ⶇUÎÜA;š®ˆ;ÆøÞ¢4¸Ú‹¶¯òp¥~ ´í@{=¡=…åºxî cŒ§ÍƒFið+ “… ´í@;дcŒ1Ƥ(aŒqå´? cŒ1@;BM2mhÇcŒv„ÐŽ1Æí!´cŒ1Æ@;BhÇcŒv„Ú1Æc !„†í<¼cŒñ¬hGÍ´óðŒ1Ƴú`& !4SÐŽB³(ú7N”@;Bhæ ÜGŒñ,>yŽþœv !´cŒ1ÐŽv„Ú1ÆhÇ@;Bh礆1Ú1ÐŽB@;Æíhw¨Ó’F£¹Õ©¸ír[šÍ¶,ÃDƒ·%m‹€vŒ1žRhßµNÖI‡¶œOhWЖ r³=$|3`°#­FS*­o?+`9”ãè£-ûiÛq¶y¿ûâ‚cŽ ]¨ºÔÂúœ¼0Ƴí¹¾l‡lXX!ví@»)»@8pèÖwaВΰÁr¡½Ÿ¶œÕ¶Úç ÚVËýDÆÉ c<‘v ÒC}ýÞœC»#jI«Ùȧa‘y øµˆ}벨ÌåæFÙØÔÓ:\‘ãeikûnnÜ•ÕnU©ÿnBCV{ÛKïËX?nËåv³·¯¬NÖ1©…FyÉçà(ÏŒ²[ÇQ¹Œ’m«±q<6$û¾+®:¸êš¦ú„>×÷«ãhO4ëÐÞ=Qm[-Å­¹“Wgý íG—œü2З¯Ù¦NŒék3‚olÇ c<æôÕ‡ew»ýœzíï׺ýž ïúßôk3 íÖô{N0E`¥ «cB~v1@AúP4W•—®¬¿EƒuxVÛd®úäA×ïжU÷e¯ïÊ?OßÌ¥ õ¶‹ÀXýQTÞ°Ëð¯q|½ãî´¬‹ßw%WO]Ë|&®ï‘ö9…öîë¥Åº}§ì£WrÂRËô×Ñv[eMcµ,ÁÂO:§] Hd}œ¯_ B;ýÚ\ DM#ž)ô0”DÃw[ë4bê‚§` FÎÔEƒ.CÛ[Àœ«O슶tý¢È~è˜|åUi—a”¡C{áö¤ä„¾+®r]i>ý´1Ð>¿ÐîP5{Ÿ~mþ®4Ž´;me\ò |E“û®L껀¦Ú1Ƙ‡+a }"Ðî€:¢}yª¢ ~WÒ(þ€‹B@;Æíx.¡!„€vŒ1Ú1ÐŽÚ9©aŒv ´#„дC»z1Ƴdú·ù6ÐŽš9hÇcŒgÍ@;Bh&#í!„Ь(v„ÐŽBS.ãÁ2%ŒæÚI/Âû ´#„Р}ë¢Ù€vRŒ0ƾ´»ÉC{î±»ÉC|ÝMé«?œ¡ð½æ£v½rl=hಖõõýïåøñÓ®Ç[;,‘;†*?âšÇ#´£é†v„Ðìõ ýº~ÐnÃn µ>h½•[)ØNx¹Ý4ˉ¶kI«©=IÌ»¬é>¾ôÑÁeŽÅ¸xП^Öý›GB#´£©‡ö_{æã°Þ/TUÚ'ÔÚ¼*ð6´@yñû wòë(ˆW@¯ƒ}ZV'y/ÿVÛwQ’Dï+C;FhG@;ƸîÐÞï³?jœ£¥} Ú‹ ו†¢•¡à¼aD¶£¥ÒN£éi´ÜØ¿ãž; QÙ¡H»§^Qº# !´# =èƒn‘åGo‘WLû>0Ú'iUzLYhEâ”ÛÐnl£¥«hËS°6rõm¯|,®ºïíh& =»§Ëù>)ç;DöµZ®Ÿ‚.9½O·éÞ/—[Œíºg5»_/:¦èXºív¾}w»²õ9áþ|;h>üöî\”ๅö²i"IéÖK¦gÑw3 ¥ÃØõ³šúöÕ/´—¹(AíhJ"í ¤4ˆµ³LµäÞ~ùàÐÞGý^qÉ“qÃhÛ+H¾åíý´nÏ´ \+c¹ÛŽé6•ê»ãÂ@«Ëòí÷s'Ï1´gPl²L9$W†vU^ÿQ{ƒOã™bò¯­úAù0 ]-×w^îB@;ª7´'À™Fw»½Ö 3ƒÚl]Qf׺)”{¡Ýªê"B;Ýì¸Û»/;RŸ«›ñ¾ŠómÕÍà‡ßÞ»è8¼ Õ‡§mçjÇäßóoïU7|½×…ýy¨¸,´‡žÍ1e9íZ¤8”BbOùh—áË•—h÷Mù˜›‰Æs7 ½ØØ=´Kz<ÉT“UŽ!´£©€ö(Ò›D’³³›ÀFp€ö\Y•ë…Þ¤BvÛoç¾âô•\dÛó¾;Z^Ú=½‹î…ÆíÉ>“2Üí¨ÕWµVO]zJÂDÚ=à^ÿH;Bí@;ÐB©Šr'pjÁ¯„"ÏU¢ß¡|nWzŒo_EË9çh{Ðm“^”DuOÛÏÑŽÁ‹«m³ÏÂñáî¸ô˜°íF4Üu[‚H5BhôÐ^Åhž¡=IÑ8!…OWdWƒ÷0‹#?¾T”Xƒ×ºA»·þÉzÝ‹Ž^tüþîñkƒZ«B»=Æ€ÙqðrÚ}À^+húé§1Æã™òЧ|L¢Ý®t“[²ÙL´™e¬A˜®ˆºo¹¥öFë5€wíËÈšß× é1e¢ö.v–uÈV¹ì:ÒZw'¢¦úúef«Áx¢Ò¹cŒçÑU#í´ÙœC»7ºlæ\§p©8Õg69\h™-w§þaF—ÓrÔþ|û2¦Ití«ä@Tsðl ëS>æ."Ìi,ý¹ö.×ÛÂ3(hÇ@;ÆÏ.´b]<«ÐŽ1Úëíû·ÈJ+²´vg²|åÙ¯wˆé2c›µ²Ó·®«ÜP‡ëئÑX)[>µ6ÞO¶îNYÛX”¬tGÆVž°Á*§[Æ~{ûe‹Ú¾ Þ+·ìçDˆ1ÐŽvŒ1Ð^h×ÁUs´‡^‡Ê-wW9‰w®íôþ-+Ír|Ç`ì[‡þtµ²v¥ôêbÀú{íNN„íhÇO1´«ßw?®7´ï\+ ªÃ†ö(/~?ê%/ŒuÔ¶&´+ðWúÅ€k=Œ1ÐŽçÚB³#õ»Ä5NI"Í£€ö"(v¥Çhe(Ðv¦ºA»º1ê¢Rc’r¬÷ÄGû%ÂŽ1дϴW½}Ž1žÏWzLYhEâ`—…vøç×·Sbô}ïí´Ù¼@;B 󙣇ö¬k™Ó­W.j^iwä³Ç{kðªsÀ)é2í´ÐŽBuÍiþÎG #ØM#Ï•¡]•×ÿ@ÔÞàÓxÖ—JQwê³Ï8¶OYm§GÖ‹.20Æ@;ÚBh29íZdÙ—fâšòÑ.×+ßÙ` ¢jùð7ûÒvTý’i «Ôc ´c !„Fíc ´í@;Bíî\q׃‘ˆlcŒGíUL›í!4QhG!„fñD ´#„€v„šrU´# !„€v„š´b]´#„Ðh¡}¹-M+²Ôê$Ë›mYÖk.3¶iIÇ·®«\¯–¥ÝtGºš7æÊj4šÒ^Îï§ÙÞ]²œ¤ÞÑ6ËùcÌþlše´—ù&"4ÃЮ¿´í!T/h×[s´‡^‡Ê-÷‚:åVìH+…÷ôo}¥Ž¡%­¦V†ö~ìÆöÝ ‹–«¡Y€v;%hÚB¨¾ÐÞiICï°¡=߃@{K:eËñÕ;=fã}û‚!4KÐn¹+hÚB¨^ÐîK;6´AvÚít—äýN+ùÛ¯p ª#-¨ìBhj¡Ý~íy´#„P} }é1ƒB{@«h¹ ïýƒóøµ|{@¡™‚öÐL1@;ÐŽBõ…ö¬§"=&GîæEA•c8‡½Õ¤Ç}Ghf Ý÷¢uÐŽBõ‚v±ö@k403—û]ÐUyCˆª–‡"ë¡=­cÃ7hGh¦ ½Ìv !„êíÆ-b+RíÈ#×”v¾\ù2*“Ó•gOY/3UevÑ¢]¨0å#B3 íÃZÍ´«ó¥n„š,´#„дí9XWçJݵ÷²w}Ó±^eïrW-žD› ù…ö$ Å÷À$„!´W1š/h×aÝ–oy!Ü øJ•7À´ÅEé¯Ã¨÷´@03Ê¡:@ûÓO?1ÆÏ”‡í>=õÄÃÓíQ:h‰ÙÚ&íDÚдcŒqÈU#í´Ùü@» Ø]w\‚à^íÆ8)ç81Ot¼Ô³t[ënvwÝÝÆø,ÏóXrQesÜXÓœ™"Z§ÝjäRp–]û²ËÚ¸±·/ãâB›®L»XÇš=oŧ—==]¯WŸÞ3Zzï›Ë­H»«^F{Äût~ÖhÚ1Ƹ<´b]<{ÐîK•êÚ-@í´ÔëSÛ³¯OîölŠ ûž|îTßìmz¬Irû Ô/{¸¡±~™v‰ÁÛÌ×7Û*›õ.ªoØQ®¿ö:ûl´ ƒhmŠ¢6·ÛÃùY# }Phß¿EVZÓÚÉò•[d¿Þ!¦ËŒmÖÊNߺ®r½í~Ù²ÒéZy†\YÆJÙ²?¿Ÿ•[nö—³e¿cýýœè0Úv }ôОE…³´ÙïKADÜ·mÑ,l®<íªÐî›9tWÀõZûNK‹\Wmïq[³Å…¬h Øíxð¢«^¾)¤À:Ð>lh×[s´‡^‡Ê-÷‚:åÖÝ)kSxOÿ^»Ó_NÑúc hÚG˜“º²ùéeŸ\î…h-ŠìÛ~ÐîÛWðN€Š˜'wªäÒO ÚË´ð´Úw®•†ÙaC{¾öµ²³l9EëcŒv ¨ƒ D =˜° v­ÍvAU­WæÁƒf™½ÿ¶-xúwÑ®c,7€ßœ%/$Œ‡êe¹žHÞjiyóeÚ¥bzLh×Ófré1Žz9Ú#ÿY²@ûÐÓc´´“aC{YhöDéÛQÉû;×&»"æŽ:×Çí@ûÜC»sœû–—4j šlö(ܳ¾•žn§à¶J*J’7îÚ^,™-÷lëˆêJñíË(+ˆ*îðÒíb­ˆZí¡4£¢ôïÝÌ´OczÌ Ð^¡WwlmçZc ´ís í.H/ ëhzÅ”Žh*¡=ë©H)H—)ÜŽtŒv hG@;ÐŽ¦5§=úÛ´ù´ìß²²™® íª¼!DUËC‘u×ße#ñc hÚB¨VÐnäqY‘jGùÓ®)í2|¹òO÷ íÎ)íi"­}ä ¼`}Œ1дí!TKhÇc hÚBhwç’»€Däc¹±½:yiP­^·[á\÷ª'ÝQ¥Ç Ú}·âs`ÎI¡ÉC»sl‰ãNXØ(ÛñûÚBôE£IÑ@;tÂó¤}ÄùðÖÀL+Ÿ;Z' C í¡“I¦Ç„.0Ên=´‡.ü½ßhï ˜@µ…vu¾ÔB†ö¢¨¡“Z<£JËÜÐq+9^Ï98³êI7;Aæ/6¼¥ÝºÅ ´#4ƒÐ®ÿÎ=¿yWZ[6›V¯ŸÌÒf쾈ß÷ÔC{ éê\©»vð®ÍúVêÜ5ïìÍ´÷ÀÝ1åc:½·ž“é'ËæÆNùhå úÒOìÛÝ®[àýN¹8 hgÊG„ÆsâýÎ\}H`P©ÞO6[­ÞïÛž2X˜Zh×aÝ–oyÑ÷®9¬ÎÝ—Ž:ð.º“†Ð,@;B¡!(á M5´ûôÔ},˜îþ=ŠïCh‰Q@û4^°íhÙ™.ŠÄ7j®S]BõçtÏÝ@4UÐ^öüwÏ„¹©D­‡œçó\=œP›±¨wgg£l4RUQøbÛ­üþÍ;íév棻äö´ÆöñzÃ~Í&·p=„Pk¯V§WŸÞ„ŽötEÚ½S»¦íïÓh€Õ ÚŸ~úiŒ1Æx¦Í³ã8ŠRVÊÌÖäzjOýý²S»fw€u´cŒñtxÿY¹r‹ì§.@û Ð®ÃtdJdB{ ë¡ôÏs|³y÷§"æÉ]‚*U'í•fqÞÑ4@ûεæ•èÚñ #}­­»smw™q"Ù/[VZËì“MRþÚ¥¤LÃ+eËþ‚ÎØU¿ä½ý[Vï­Ü²¿Wýo½^û]õX+;åÝ¿CûpP½å»ŽQo·äµ#—~å Êשè¸íØ÷Ú5ùÏUËÍöñ%õÚø|­÷Œúa<*èµ¾kwN”Kõ 5€ö¢~¿L\¿õ~Ž¥6›ø@TƒvZŒ é‹]S6VLÑ"æÎÙjJ>¬7•©xŸÏ’®×R3(õòSJL›\1=¦ ´ÛÓMûžXhVïÊ¥÷¤w„êí1üõ`0î €]¹VÖ®Ô:ÊhÙJ³óõ®—¬“‚ÞþþNBÞú¥ïå픵ÆIÂS¯P=¬÷òûèBîZO¾ò£60ÁY?߉º°N%»Èž:Y§ £^;OÀê¢*Ø®ª~úqvÿ¶ ã‘Gª ‚û(¡½¨O¨ ´—é÷½âo½6´+0wÁ¹o¹ L®M_¬D 北xX¢#GÝ9•©ö^6UÜ9àÁ㬈Zí®ö,ˆÚpxzLÉôOj„=Ò5è(Z§jšF™ú•MÑ¢Á¹[¼ý¤Ç„@Øõžãn·Ý†™ã:îaB{™õ‚Ÿ+é2x‚é1•.Pо³d`"Ô'm¼]ðkðÞÏ…»«ß¯z±âL‹ ¾;wƒ´ÐŽšah/ˆêH­Ž×; 1^¯ßˆê8 :Õ¸}lÂsvâœ='=†@» m ªç¸'íjyÑ­rŒGíz*‹•Öú­9RNz¿ÁÐ2mÁ>¡¨.êšôok³q2i*ž§Œ¢zú. Bw\¿u_ çéòŸE™6ÚB3 íÎȯ }´ÐŽšyhÇc<ë³6鯧¥Îsüp%„ª´WYcŒ1ž&í¡™€v„Bh–O”@;Bh& cŒ1&ÒŽB@;Æc ´#„€v cŒ1ÚB@;Æc ´#„ÐŽ1Æí! cŒ1Úû”:ÙêF! cŒ1® ´§®N´ºkïVö4ØVgÄûZnK³Ù–åªïêXûÙç8ÛkÚ4ŽÏhÚ1ÆãaA»ë¶|Ë”À¡r³½<°êH«Ñ”JEfÄöÊõÐŽ5Ýv2Pþ³è»]Ð^ÆÛVgâšm3|œ»ÖÉÂÂ:éL[©wyÖ>ÃQÖ}Ö>?<—ÐîÓSO<\»"¬]´¤#SíÃ8Ö~.ª¶‘v4qhW'T-Ðh¬–¥ €väÅ­}–µUÖ4VȆ]„:î¯ÎuúFw<ãh§²ûÐ×›±Ï¯³~…Ño-¬ßÁTÍ7ýB{°—wí8Œ ¨%­f#Ÿ¢aDæ-à×¾{­ËR°2—7šecSOùpE•—¥Ý´¢ÿÆ~µ:»–ç¢üùzµ[ùc[n7µß;ZîÚÎlOíX_¸Z^hG„½íç)#ÝÞsŒq}\€:n­ G›íÔÛµLŠöÙ }>½ïW«Óû¼{éDŽïŸ/Òúl¬;I `ÄЮw„ ¤ÇЩÇ'>óai±Ï †èÂcŒ@;‘v"ís íQ¿eËÙ°8¡cÚ§ÚUêt3:F £e”_­€Ë‚ü Fcè2 ÖU–TyéÊúë`z×ÑžåEõÒ¡Níßri½t0,³ë¸—õ Wûõqìv}$Ñ<î`¹½:vZÝ×®eeöÜGòýо[Ú묭ß¿´;?sÿDèÇ íQÔº ÀÉò ‹Zô݈Ê'‘m;ò””åZö€~¢[èmŽþkûÎ꣧Á¨újWxÏY%ÏÕaAÏéµ"ÀeŽ×<+âæª{¨½ý¯–5 |šë8r¿´~Eur·»³,o›û뼋cµî;âkß6Ö1÷Ò¢´;/V;‹ãŽqQ¨öe}®¹‹Æä7TЮÞïbÅßÚÁï²'e¬Ê÷½Ìgú]vh_m_\ÿwe4ý–ã{åý¾Wý ¹Ö|‡úú÷Ñ_…>ÿÚo\V =7ðÑ€^+¾ÛŠZ¦ÑS … ]ƒWuÑàÏÐö)D-/ª—k?®(·«¬2ÀíË\û ãØ‹ÒGŠêŸy-×ëåZfFß›7–Û§]÷àEŽãûˆ´/ûrßI«™ ´g‘¤¤C4AHëÔu@ЖGÑrײґñÂÓýFõQi3v}ô;ΨžYN!(•=Þ`ûyê^ÚõO”:¤öï9WªQº¯\J‡«,o›—©SAû{¿7Ž6(s̾×x¸ÅÕ>&x¯éÖ/»HtÔeiÑ*ەꥷkÚ+üÖ|Ðîû­øÚ#Ø%¾E‘ö*m_ºþžïÊ(û-ï±W=–’ëûw^µ¿ò~þSp¼ÉçY+h×aºšªB¢ãýÖ=é9“€v;º[ «B{hœ(´‡@Ý ï•ö9Nh·ï†ícÎi÷ÝÒÎå¾7 ¨lä"‘ßÉϹ_¸ùn¿—ó²ËÇ[! ÀuqRvÿiTïâ²Ñ؆ÿ"¦ìqT¼+Ñ):a·u|GÊ´7êÛ;‰]êbÉÝ>K‹1f@—wȵí ÛµrJþ®úý|\û,SVÕô˜¢ãì§þ¡ßËHú--òÛϱ”]¿óaõWµ?Þøóœø@T1vZŒ ‚|éEéhO£ÚÞÙj ˆrzL‰¨lTÖ°¡ÝÛ~}¦Çx´ÄqÊmõ®\ºåİœ[¶<`zLh×Ófª@»=^hwzLAg Zö$¸Í<,h·£sýÂü p2*h·#EíQxáæ(k¤ÐîøŽµAè˜3X·"p•ëcÞZÊ‹ÞOÒb¶­öDÓ´-_Ô®}Ô)ø»Æç3p?PÚûÝg?¹£è·ú=–*ëëwÞOU¶­ëñiöæ.8÷-w–sð¥/…Ã7°ÏZÞˆêƒ%üåó툪—µnGÈØlµ†i/;0Ò¡ ¶õ]ôŽÛ;ÕL_T¹–•Øg§äçãj/×÷¯2´‹1ß=QëíömRm½5Ùì I®úÅŽe»ÊD ¤ÇTvûë¹-n¤•9Þà Ø’é1eöoÜV¶òpëäuªœ"åMßpÔ)8¹2=ßgxNü¹ý&Ñ7çlÁcñ´ªc·^iyõ«£4_NsšF“í¿¨]}ß…*¿µ]C‚vo;„>ë8Úìmok±öæ÷B? ´#„€vŒ1Æxv ´#„€vŒ1ÆhG! cŒ1ÚB@;Æc ´#„ÐŽ1Æí! hÇcŒv„ÐŽ1Æí!´cŒ1Æ@;Bh÷uf¨XªiÃѵ!Æí! ½dg†ÝN˜6]bŒ1ÐŽÚv hÇítU?¦!„€v hÇãš@{ éªÓ];xï´¤ÑhDnu¦œpféX†­å¶4›mY¦%€v ´íc ½ì)¤Ûò-wBV ÊÍöòà­#­FSÚËc‚Å‘‚ãŽešaÝþ—–Ú1дcŒöÍA0ꉇ+h÷ïQ„Ž£ ƒ–tÆã€ÓQ ‘v´íhÇÏ´{9pWÐîмZÒj6òi FdÞ~-bߺ,…7sy£¹Q66õ´OäÚØORGo½–¥­-knÜhFÝå´¥ÝÊßr»©ÕÕ†OûX’ˆr°|à»ÛÊXßU®§~á:{öÙ)ׯ­N¯m³m\Ÿ/Òhkû.OØÚ‡âm«³/Õšm@;ÐŽgÊ»ÖÉÂÂ:éLû>0ÐÞG´½“”a`Jé(‡[AùYT5;#HJWQå¥+ë¯-À4@Zýá­—o¡r´úªr2ï_§å» ÑÓ~J”oàn«†}!ã+ת_™:»öY¦£öÕ^gmäø BÐîjkkÿDèçÚ5ÐŽ¼¸µÏ²¶ÊšÆ Ù°«ž'ÞQC{gý £Öï˜9°*íCû>æøgösÊ•·C6,tq?¾:«å®6íçÓmúnŸn?dl§ú¥´n«e©è˜¢cIú±lûîveë£}ÿ]ß­¥Åî{\”L%´‹Ò)¼ç*‰†ï¶"£i„Ö\Áóü«‹†ÜÚë§`ê«×²geËY¶ï Ò_BÇå­gAúˆ·>vJŽ«~î:ëÑwãÎC•6.Ó¦úw#i_ö従V´ÇcžÈ–'¶²'ÛFŸÛN9´GíhÓâ„NÌ„ö¡~ŸFpü³ý9%¾k„ßg™ÚÅzúwz¡6´rá¬m¯ 9½ë½Ÿ»ˆL \+c¡ÛŽÙÂ*õ‰€Ýÿ}WuYèþ¸“0åЮÃt˜UQÇû1¬‡Òs&íaž<´‡êW²Îuö,â´Ï9´['ù`ô,9%'° ‹ + FfuýœUòÜôÄdEá®(š]‹Ë×ö™m¨ïĠ݆–P;šw"òíXfý¸]̈qºõ™ZÏ!µ_™6ô~ŸÊ~Þe?oû9Ž*?'×ïMo÷qDÀ™s ³r“¶7êúúêí…v¨ZÇxØ¡Þ}ö®¶)ýVÝ<¾´ØkÏè"3m;W;¾z9ÞφôŸéˆª@ÉN‹q–/¥¡(å#4›K’6áž­&ºá¬—¶eÒc<ÑßVVuLž\û2é1^-h«‚rsõÛ]¢ÎUÓcª@»ž6SÚí± @ûœB{02Cx.B•œ<3pQ‘%ýÄš{m–ã½õ­/7òáËt' íÁ:yêïmÇ’ë»"|ÎèæðÚ¯Lz¿Oe?ﲟÙïáT}N%~oЮ—ŸE˜ºõêÁiÚåz¡×“ åìì÷ŠúOÛôíž ŒÞÅNz祗bãnGßwÂL‡ÐëêóUÇgFë{P¸ˆ«ýås».Lúí |m3Dh¶I/J¢º§íçhÇàŇնÙgaWâöÄ'óDÔÒËÁzË!êi§GÔ¥^h2Ÿs@2šƒô˜@ºÀ° ]€óµÎiw´cÑ`º²Ç_v°\Ñç0¦œöJß§²Ð^¥ý‚Ç.hwMgÜÚö Œ+íŽ)ݲª¤LË”UâVHKXÒÂÙ3R¤ïå86Ý\™)}ß§RŸwÙÏ¿`;ãø§òsòüÞÊ@»7ºlæ\§Çíü\|õv§þaÖM?Fß¾Œi]û*9Õþlóƒä ¶u¤f•“k‹’w0ÐŽÚki} WâáJc ´#„€özXØ5&8;дcŒv„Ú§8iC c ´#„дíc ´#„€v:u hÇí!TkhWà°‹ 6¬ 1ÆhGí« q9Ó†£kCŒ1ÚB@;Æc ´C! cŒ1ÚBhÇcŒv„ÐŽ1Æí!´cŒ1Æ@;BhÚ1Æc !4cÐÎüácŒgÕ@;Bh¦ ùÃ1ÆÏê³!€v„ÐLAû0Õi5¤ÑPnI'ÿfò^ì–¾Âr[šÞ¶Íf²½µr³½œmÖnªeMi·[ùrm»ë,ûê’Öµ#-õw³×¥ÙNÊ×öé«'B¡Ú(v„Ð^ ¶ ³ìêË ÷@ºÓÒ º Ô:˜GÛ¶: ûSw!¼·Q§÷ž¾¿h»¸.ªÜh¹¶ÿ`=Bí!4ÕÐîÜåvÓoT„v+Šî(ÏŽ¶«};÷çt}ÿ¡z"„ÚBhª¡ÝÞNØvm¯GɵHx°;²zÏŽ´‡ ½è8Bí!Tkh7ò½óÑí^¾{â^˜=Î%D°ÓÜr#gݵ? ®íhºžB“«KT®Vn9YÚM²Ÿfû²Âz"„ÚB¨ÞÐŽBí!´#„B@;BhG!„€v„ÚB! !´í!„8Qí! !„ÚBhG!„€v„д#„ÚBhŽO$E!„€v„ÐÞ`kO m6Ó§¦OMžÚiÅë4ÛáíÒõ|O!5ÞOŸ¾šì«ÙŒËìî#}¢*O0E! !„æÚ;­œc ¶[ÿ[_7¸/Ò~zQüm\ÄevZ ¬±G! !„€vÉEÆíÈv; ÛÚÎÛŽ(|m×¶É.€v„ÚBhÏ+ƒt ´‘õ¢í<tc¹- !„€v !´»”æ­û#í1wß3à¹x»4']Yßt¹Ý´"í öµòº+«ô˜hy’7O^;Bí!4ÇÐ^Níf8ÊŽBí! }BЮGˤ© „ÚB@;WB!´í! !„ÚBhG!„€v„д#„ÚBhG!„€v„Ð^;h7ç]Ï=Ý´a=pImÑÒçYG!„€v„Ð>RhW<êwFÇÜT'!žœŠB@;BÍ2´s±[‘ó2Ñt'´wZZyÖƒ™’§œ¦å6³íÓh²~ZF³ÞÎØ—ãN³.ɾšÍ¸Ìî>Òvàé«!Th?÷ìÓ1Æx¬®5´Ç<Ž´‡¢é¹÷ìtõw ÞCkßj°õ¿õuƒÛù"í¡ºDïÅeª‹“¨<"ö!ThG¡IõEóíŽÈ·m·Þ·#ÛYy6@‡¶óÁv¨.Ú6ÙÐŽBµvŒ1ž¤ç#Ò®EÖ‹ä‚~äFd½h;O=X !„€vŒ1žhÏå´;sÈQê@4]]¸óäÍYj|9äQ x.ÞN?}Sw]´òº+ǹûÝåÉq‘׎B@;Æhgžö ‰p”!„ÐŽ1Æ@ûÄ`]‹ˆ“¦‚B@;Æí!„ÐŽ1Æ@;B!´cŒv„BhÇc !„Ú1Æ@;ÐŽBíãÙ‡öfC¤¡ÜYî$¯».û£åîzÖè{äf…:M£Zñ´ã0ößJ¿3­ò߉©ûü´ß‚ò$ç×o ! cŒëiïäaªYG@èŒú, {úÈP™“n÷ªû¯´~gº ½ÝéLñ÷ ! Ï¿àjæ{3ÐEù´Èªþž/⪀GEHeô~²¼ÕJþ-‚–¤ž-O™–5ŠxÏw|ö6®hkz®(¬ë½¢2Õ¾[žz6“»!íVþ}£\uÇÄwì-wÓö´{£ðŸ_éãsDðÛuÌöHË(sáд>£tŸú݈ô{“–Wô7¾gÝõZÉEAÑw"tWÃ÷½®T—VM/Òš hG夞±Rí¨œÊ@;*ß–Óí- $¸Õo)ˆ+HH÷¡¿N×+íäá¾™€J.} ÓƒµÐ{EÇ7öH»§Í €kåËkXYúvŽ yéñ©×öçÐb¤½Ù*ÿùÕÁw …ÛY붬 š~"íÍ–ÿ·ãûÎÛß¹š;¾gÍ‚Ï=÷½|—Êü¦Ú‡íD‹ù¤,´Ó^åX¯ ´Ó^åÚrº ÝŽæ•†v:fDÒ(£SÚíõRàpG[‹dúÞ+:¾‰¦Ç8Ž×®H­oz4]?>”6G í¾Ï¯ª}ÇPÆ›­þ>˾ Ýó/Úw?Ð^ô½öÕ¥ìo! hÚvÚ²†³Ç4­(`¢] VÚíòhT‰´Û)#ô´´H»ï½¢ã³ï4*DjC€æ+³h}^¹¨{ ÒÜ"í®Ï¯Êñ9#íÏ%]¿Uñ³óB»uATÚ£coõ÷EÚCßë´WùÚv hÚkív¾í²Z®¨œ±µmmõàLOÐ#§Í’ÐÞJÖ-ÌýmäÓ0¼ïuŠóÖ›žsý8Êæ´‡Ê´s—õ6sµµ~|v$ºÑò{.÷YÏm.‘ªúÜCï…>¿¢ãóCÑvz{W‰²Û9íöE¦þÝM÷YôwÝé|'Bíú^ëÒl¬B@;дí@ûœÎÓnG|¶šC¸C‚Úv hÚçÚ;þÙN¬DàB@;дíhG!„€ö©†—½Ÿ–“6~ZîÚ«{×:YXX' }*ÛhG!„€öñ÷‡“#4o½±2NúŒ<þ«Ëò,C»‚¿n†WËR†ÞŸGhß¶ÚlÃÅ­@;ÐŽBí=ß [íˆx Üi#=y³œ~êͲüÐ>ø›3hï¬__èhË–Í¿v !„Ð>§Ð~«\µQEÑÏ]÷/Ë¿ëpm÷gÈíÝò«îß÷^~‚?A®Ú«Á¿™¿F+Cmo¬;О.Oþݰ؋"¯ÙfnCkò~h›™„ö²aa…lØú´’õ‚í³UÖ4mí)hG!„€öÚBû®sSàþ¶üôWjO¿Ócºëþó/D~¹½¿üäÓqã}ëR9bû½òӇ⠀­7vá_‹ÎŸ²© í}F6© ƒÇºÛËÓÝuç,=&y?ƒJ•¢yü~/ªE˜}ÛÌ"´[Ç_˜B£Òf¼í£.ì ñZùÖÅÔ|Aûr[šÙ—´)ívKšC™£¹#-íдž6Úl´dlszŒ{i ´Ô±¿!´Ï*´ÇÑp•ŸþÃ_Š¢é17Ƀ9€þ;äÉå«å¤ÛåÁîÀ¿?fƒÿùò•‡~,¿²/ æ,ÒÞñ.O#ðcºÌD¡½(÷_¿ *Õ¦Cs0{ЮÀºÙ{ŽJðÀöåv“™öD=«hG! }˜^–{¶NWÉå£+Ø?_î‹¢æÝíîQ.Y®û“j¹¿ž”qßW6ËÛï:°Ï´Ûi œehŽÛ“·ö^å6-ñùÌ ´w ¢êÝ÷õ+›Â«¨|³éޤ·›öQ\ãè³;—›¼ßjÅ‘úîNˆuzg@{(S¼¿ø.]WßþŠŽÁW—2+½í:&´÷ÙžævMV‰Bs í±øË'å–SÓœöhW9鯖å'wÈæ(GèïRùíÑzß’ëN:L.û†;/þ¶m‡Éé7íÎåÝ׬ßaæw_.iƒMº lSG:L~ ª'Åf®#íŽ÷tï´ôˆo¢«B{þÂ,³ÚÃh7´»¡¨.¡(»}ÌÙ~ûmO+:O´!„€vWÊÌ¿ÛS3>”¤Àdêå¤Çùð±~õË_D³Ê,;¶ù÷'~Ü{ïþxꯞü6W≨<uâ9íIÄ7‚KѵTËaC»AŽRF hïíÏ~oØÐ^T—²‘ö¸µH{?íId!„€ö†# hÚûš=ÆÌÛî僛ïõò¶c ÍrØ“÷s9íFª‡?jläƒw!½eƒ³þ^º?W™zš‰gEǪKQ´]ÏåòÔ“mûmO³Lf¤A! hÇ@ûüA{]eG­© B! hÇ@;Ð^ :ÖævŸtÞvê‚BhÚ1д#„B@ûС]qØe¡¶*vYh§­Êµ%ÐŽBÍ´çgþÂ>A;®fÚsôí´#„B3íx8Ç›x¸é5@;B!´cŒv„BhÇãÏÓÞ’vÁC‘j«è8Æ?y<÷:ó¦#„ÐŽ1Æãz"jôÏF=¡ÝñDѺÈõ´U„B@;Æh´w!½š€“÷r<ÛÅ€›,k÷ÖÉXÜØNacy~½'šð\TOãI£­Vï)«…×M㩦´[uí=œµÿ6C!´cŒçÚHz¡ÝN7QkO@¹G¤½÷ ¶ëA¶#:ÝéðkÔ­D¤ÝñÖÓ>ž2O=µžŽjK.EǼ“1H›!„Ú1ÆDÚµhu¯ŽèvÃP=Ü…Ó2 ¶Ëoë‰`ۑ辡Ý]Ïü‹v Qv»Ù~w.ôõi3„B@;ÆxžsÚ-PÍ ³ ÚëÐQb'´ÛÛ¹"íFd:_Fh·÷]0ôi[‹´[D¯ï6C!´cŒçxö;ºÝ°AÖˆþÆpÚ[ÿÝnöò»CÛ™3Õ˜Ûä¢ýv¾»¾}¹«Ìè+Õ³Ù,iÏ_+ÎSO¶ÍCn_Û !„ÐŽ1Ú‘'‚ŽBhÇc ½¤ÏC9Bíc ´#„B@;ÆhÚBíc ´#„Bõ„vužÃãºhG!„,©óÆ×Í@;B!„Æ~q34šGõû£!ÇcŒ1ƃhGhÄÐŽB!4 íÚ1ÆcŒ'6‹B@;ÆcŒ1ÐŽÐŽ1Æc ´#´cŒ1ÆhGhÇcŒ1ÚÚ1ÆcŒv„€vŒ1ÆíícŒ1Æ@;B@;ÆcŒv„€vŒ1Æc ! cŒ1ÆhG¨ÚÕcŒ1ƸîÚÑÜJ}ù1ÆcŒ§ÅКGýðgAvyua°IEND®B`‚stxxl-1.4.1/doc/images/btree_uml.png000644 001411 000144 00000163754 12350112610 017202 0ustar00tbusers000000 000000 ‰PNG  IHDR.²ÓžÝãgAMA± üasRGB®Îé cHRMz&€„ú€èu0ê`:˜pœºQ<MPLTEÿÿÿÿÿÿÀûû¼ßß§êê°ôô·.."ÄÄ“¥¥|DD3YYCnnRÒÒža““nµµˆÿôô»»ŒII7ÿææÿppÿ//ÿÿ??ÿ»»ÿððÿ ÿ%%ÿhhÿÈÈÿ&&ÿ³³ÿAAÿÿ''ÿ ÿ""ÿììÿnnÿËËÿ[[ÿÿÛÛÿûûÿ••ÿ77ÿ--ÿžžÿÕÕÿ__ÿÿééjjPlÿ``ÿÿêêÿ¥¥ÿÃÃÿßßÿÄÄÿµµÿ““ÿÒÒÿYYÿ‡‡ÿÿÿÿSSÿ««ÿ~~ÿ))ÿJJÿ ÿÿ ÿ ÿ,,ÿÿÿ99ÿÀÀÿÿGGÿ;;ÿjj««€99+¢¢yðð³ÿTTÿÌÌÿUUÿvvÿLLÿllÿ¢¢ÿ··ÿuuÿ††ÿccÿ||ÿ]]ÿ¦¦ÿPPMM:ææ¬cc5 bKGDˆH pHYsHHFÉk>åIDATxÚì½icì<“žGAÞ$®r<ÞÏdÆžœ¼3ò2ÎbIÖvƱ'{â8ÎþÿF> 6{“¸€ÝUžó´D¢)¬ ¨ºŠ"6666666666666666666¶€ìmã~ÇÆÆ¶?\ÛêÆ¸`ccc\°1.ØØØlŒ 666Æã‚mw¸ˆ…µ„ˆˆRáZ̸`cccc\ ô–åD$áš`\°±±±1.F¸rÆãâ(. ¥”Ä”+¥Tf˜0.ØØØØ‚ˆ(-Tú‡rÿ Æãb\P¥£Q>.„”yZ«FQ,KUÊÔæÈ½OŒ 666¶{ÁE ãBE &- Õ3ÿã‚ípQK)¥Â!.LÎ;õòßé.²áŒ 666ÆÅÊ(K‹1.êF•dœg€¢ñ'ÆÛ=á"“¸ÐPQ b\°±±±Ý. e•´Ý.Q@I)kbô‰qÁÆÆÆv¸8¦pg!Ýèã‚íŽpA%€t£YˆRùèã‚ížpQô‰ˆ \$šþ,ÿã‚í~pK-õ@FDE™Ž?1.ØØØØîƒ5Çq!´²–ÎúŸllllw…‹">Ž‹¸0µñÁ'ÆÛá¢èb:Ž ½KÚRL}b\°±±±Ý6.b§’^oi_H/ÂB’»tð?1.ØØ.ólëã‚qÁþkŸ~†}»î"ÜOxêËý‡o7»î"ÜOÜøvócä7’û ã‚íÖq1d$çmÓf7…B¤ìøä×qq…iýÄ„†â‚óÒÞ÷¤gŽŒW™Õ;¦õ•3Ë¡æÖXJvŒ Æãâ ‡+?/rKbXöyÆóˆU¶B¾E\IJ½üÎ=XŒŒ Æã‚qñ#\@Å»%¾^<ãb!\\uçά¡ˆô–sŒ Ö*îÖâ¢PʬüÏ/vKbØ'€q±#\ &ŠÁ¸àÙÏ.ßÂ…Þ·²qkú^† }<ãb\ä5€Z ¢X–ª”6U$ežÖªD¢TJÉ.&¢á`JºÉ£ .*¢jÀ…Ó )sªj%cvŒ Æãâ”3ûmŽ=3úlÝ’ b £3ÓºîFÊ~³Œ‹Ó¸PÖ«‹VïùVÙ߀ÚELÔLµÙ!®›8Z Q(}¬ÛI@ÕÐ6)»Æã‚qq)€’hìoFŸ­[2¸(<\øgвÆ|ƒïôã"ísK *A$®F¡ÇEí×Îì& @Qt$y-ô‡n¹w5ã‚q±\ˆBG*ÆþfôÙÅ…¢ö¸ðŒ[¸~näôgq‘t:‘–dœgÆ‹K¨E%$Z@Ùƒ;m#jmê­Û¦ÒoÁšÈÖtv?é"é eúáïÒ‹uì镊wÆãb^\ôÖÑØßŒ>[·$ŒÏiÓ£#kJ¤µ!ÄÈé1..Nu›I\g*hÊ>Ç$òD<¸_š£:óT†£%ç@kš×‚=4¿,‰Ånàd‘'úùáïF?Io>ÑnZ ÆÛʸÐ!뱿vRÝ$”=.FGfZ†CÙ¤Óc\\Š‹€’RÖfb& Z"U ]¸À<S;“R§8šSà@uí>4§i¤qñ†Ñ(ÆQrb Íá¹I ÆÛʸÈt~sìoÆŸ]\Ä™ pȉ3 ¨žtzŒ‹KqáÎÿ:ó;­]j2ûóþ¹$ý̠ГÅáh €: Ë€ÎàÂkAHÍ©»qqPò'Þ‘Ãslkæ.ÒÂÊcÆþæÀÿ¸¸  3¸™ZŠä“Nqq . ¥-'§ ZeW{³ «W VÓÚ/Él“ۉƅ߂´9‹–gËââÜÑ×€qÁ¶rª;Î,/ÆþfüÙÕΊ¯Ñ‘ö£œtzŒ‹k‚QÍèwdî·Š55†ç¢Ìl¢2ðá‚Z“£ G-HóEIÿ Ù ü ±TµéìqW*%ÍÄ@Èœˆ(ÍËRP*óÑÑy Ôƒâ\J÷…2'RÄ]©ÊîðhÆÛòʨÄÊ)ÇþfüÙÇEÚ¸icSŤÓc\\œênm&º(S&g.ML©2‰ˆ1.JhXu›Ó‚4Éf¡ à½á¢hÚ~)k’ù‹aTVjt´ò„þ\ÏÞvÁŒZNnȸ`\LãBË('üÍø³ ê\ŒŽ,ø“Nqq.²RJ­làií$®Ísåä.²R&-€¶ÄÁÑæÿ:  G-èuuÖïîÂnàG¸TÞ@§EåuÕ´ý»¦ôH«®”~PÞÑItÃ.Γ¸Š./€||4ã‚m\P¡sÓc3þlÜ’Å…Þ®NN) ëÇ>#§Ç¸8 9Rqa¥k±gY]Jö`ýé‰N]h\ø-HírÙ¥ûÃEMD1P5ú%‹•žpK(¢R?¶Üâb8úd6ÂâBQœAqî‚m \$ÆïüÍÁgí–z\ˆa·ºÑ‘&¯Q˜ìˆïôà"7èm -…Ÿ»ˆ›€²a>{p¢ ÝÄ4… jµb€µ $Ðn¿)\Ä:D«ˆZ-¤Ü.QD­NÅÃÑáb8œqÁ¶>.ôè2û›ƒÏ¹ j†M@FGŠ@Ûoðœãâ”Ùjz©¨tv4É…û;ûÿU•%¦´U0%ÕÄÑæÿÌѶ¦•Ó‚™~ä1»ë»H*¥”Ò­ì瀊H‹È¥l´öLBQjçp¥ñÿ~Vã,.†Œ ¶µü—[ëNôÿïø›ÃÏ©¨Š{7{Õôü3Ó*%¾ÁqzŒ‹àl˜ˆ°¸º‹ˆñ7V+¡<¹~–¸þ_]‡ Ÿ`cÿŸ`\Ü. ”ÆÄ€ Á¸`\0.Œ Î]¸½ß„+î–Ú}kÆã‚qÁ¸Ø™åJ)v‹à"3©éÎnû¥ˆ2}@ŠC\$Wá"a\°±ÿb\ðc¼\ä@™%­Ýè@ÿLåq—Mà‚¼2Èþªî‰ØÕEE“¹Ÿ0.ØüÃÇ)誆m:üÌ”‡É&qá¬}™\¦çãbÕ2ÜOÜøv³˜zEd[WÞÏ ´2ðÿ”»[æœÅE¾ð;ÜOÜ.°Ç¶5í«éõŽëRTþÏ(6KíÄéÂÛ²<Éáá®”_w¶¡s²­ég˜Îìê"JÒöÞfŒ ž]p0Šqqåh«˜D±LuIî'lì¿üoå´• s~ìŒ Æã‚qqÜÒ®2“ýfc\°ÿb\°1.Ž[ÌœqÁ¸`\°1.ظŸpçd\°±`­" ®ìgøv3.¸‹p?a\pÿáÛÍn€»÷Æ÷¾Ýüùä~ÂÆ“qÁ‘ßH6ÆwNÆÛ â"?[±Ë ö‚òö”J„¸ö*™\Ü<÷Æû¾Ýì–è"ß*8•–Úç‹‹N/Ð2]QãškHPœúµÝ×^Ôå—Ïý„qÁ¸`c7°(.’Ö”ÇË.*“×9ûˆT‡{Šœ¿†údµ ƒ‹þ¢.¿|î'Œ‹»ò3¼9ëé¯y#Ç‘ ï£lñí ac˜QÔîLãR\¤h㳸¸¼"øµÃvN6ÞÀœ©(.€9n‚ñ·q‹Ëö“ªM%p¢x¢NÒÐ)!<»`?÷›ㆸ2'RÄ]©Jñ‰;¥”4ˆ»R)›iR$¥êÒ¼j™RŽ–ˆr[©;—Çêw‹>•)Q"K¥ênÀ…0Ç›½ï%Êýé…_\ãÂ^”wª½bïû#)•‡ã‚qÁ¸`c\LàÂTJm @%­Þµ¼$"Jôæ­õëu  RA 5I;uðÊ(ùY3›¬.œJ¯æ §ªµúßKû¿qôÅÆáŸj¯ØûF{d¥ÿh3qe\0.lŒ‹c¸Š./€œ¨D&E^QÜuÕ´}ª(TFIt"&í\SãÊ+3q˜òèÒüÎ+€²]¡]õ!.Fß{šÂ…¹(ÿT{ÅÞ7š#ÓPUmëyô‡2.Œ‹cYÄ«s¹LOž6³ þŒèžqqiÉ¥”Ò åX\H"Š3(" Ói‰’¨19 …ÌxÔÆ þ§¦ÌžÒÎ»éæ¶ÄÅHÆ. ´2uRÝžÔþ”|½—Ú_€‹keð¾Þù‚…®CÅÅöëfŠÁ¸Ø¢ŸüùôãøGÿð?gF|—, ò×É>“ªÎₘȆ·è‡DŸ^ãB¥5 ª‰Xü­©Â1ÂEV§Ónn}\xJrOÆNÛýÝRSÝɓڗ¯[©½ßö\2xGïÒ¢nzØ¡±óæ~²ÇÎùÝEAîš ~Qéò–³(æ(ß[ë‹„·=T*’Úï ãQ1=¿š^|dtO•GRzV”¶ß*6©Ö %º8õÇ8ÑþÅþ’ƒQ›ô“§r8ÝûÇÿôŸ<ò„b•÷ŒŽjؽ Yp³Sß̓‹m“(¾âYKjï^oH¢û°SÝ#bü³þ´pª›íD?ù«ñÌŠ­p1—£bØ96úÿÜ\8ÅW³qŽåJÔ®<½Kt¼¶'Æ_üá/Ò²î'ýƒY±6.ærT ΖSU¼–› iWYýÍÏ:R{7¤'‰qq51ôÄ‚q±y?ù«ñ_0+¶ÀÅLŽJåk‹‚ºÝ?¸«Hí Lt¿“ÍþËUÞH=‹–é%ô9ÅDœ>º’ÉÉþ—ì QôĬØs9ªx7÷°»ÛͶ_\¬×E„³õØéãœÍÖNWENNf«¦¾*-÷Æû¾Ýì‚î"Ù…•®Å°§ÂZÕ§¶„›øª¤ã‚qÁ~†o7»9»ˆ˜Û­öåŒ.ÅÅ´ 'ÂÎÁ¨¶,f\°ÿb\°Ý.æÞ¬1nQ]…‹KhquuvÆã‚ý ßnÆÅ⸈»R)›[R$¥êR¢D–JÕ=F)%«‰ìÎÎt²Ô›­ ŸnC¹ììtºYwzáo 's"!EÜ•ªìˆˆò¨eÊý„qÁ~†o7»åp¡Kd£µ²Õ-€Ê!3Û=&­©6|x‚2šô¾ÖYí~—ãÂ¥…×Pgfz¯bU×’ÙI¨)ú Ô…žYAǸ`?³Ùíþ†`1—éÉÓļÈÓ‚Kvà"nºjÌîÊ$*#”è ½·Z‰LмªƒlM+ E”{´˜ÀEdéä7Çfh»ñ´šÂPty¡w«î€NÄŒ ÆÅ”GZSå}¿¸˜GŸÑ+ wÉ)³¯ýåºÇ“G&l²_\äRJ Hé†vã­M­»ui‚A­©GÒiÿ^ŽOHݲ#Zæj`Øyfô͵÷Ï©^dq!‰(ÎjY2.¾GZSå͸pîÔ½b6®šz—ëÏyRpÉnÀï"jØbxB­éü¹öãÒÔ[ 7C‘’.|9:A8EËNÓBïÙœ µFß\Ù’Š6*–ÃÅP½—qÁ¸8á‘ÖTyï3‹$§pgÞJéìˆ~—¿ÝgŽ<)¸d7àw‘F)¥¥”jÜ'"¥”²ÑQ%9«RÑ5¦Úa kª‰œ’È-úmžâ¢ˆ%ìoÖI‹Ê¶!ŽK¸,.œ>Ǹ¸ \Ü€Ê{G¸X¼¢õé·Òj*OØÜ¸¸Vpɹ‹Qq«zI÷ÅÊ3w"Òè¤tspBíqN¾•B§¥ SUlüÍÔ %*-LN<ø‰¢9Œ‹›ÀÅmª¼÷‚ abÔ‡ÊCG0éÉ"‰Ò¼,¥úŒœ< e¯WtÏZ2šJá—³(ã}¦GÃ9($S9¥ö .Ù \ùF&@)¹Óö@Qç©ÅAÚ•ÐŒOh\Ô£‚ÈÓodÒê7rüÍ”Õ ˆb\0.èqJä=—Ê{$ò^Çé¬ ï•ò”‡ƒ`Ò—Eš1ŸÞHWCiõŠÞ9}KVS)'+±ºŸÎ´ãh8í‘Uá CCÇ‚Kv×¾‘}¾!öºŒé&&5@)ÅyŒOp‚QŸ ò‘‰bgÚ}3QÚ¾ßDÂä&÷Œ‹óâ”È{.•÷H低ÓÙŽò°LŽd‘i Ô•Òa åk(^Ñ?ǶÔk*Ïãât;®†Ó™¶€ªj[¢cøÊ ¢Q7†‹Ì¸ånRP˜;·P}Z¿6þ ÒIu%§ÂQ½KWúµ}3Q޶ì_èülîÂÅE¸¸E\\à!N‰¼çRyEÞ«8Eq1)’ãÂQZÁäHÍXêLc>àÂÕPš7Þ?ǶÔk*/PFlÇÓpê#•µÆ…þU(mPÇûÆöŒÊ2!Jô¶·3nõb¹ÂNULD Ùø„ÄÒùᨩù¾y«ƒoÖ?ïe¶µ'WôZšÀ­>Éd\ÌÛ9/PyO{ˆã"ïùTÞ#‘÷*NgQ\LŠ$Ǹp öÓHÍh^ÝxÀ…«¡4o¼Žm©×²\Œ‹cí¸N}¤B5ý0#þÆŒÝÀO´Š @QÀ„‘ìÃk€¬Q@XQ“2“;ÿ„Aøªt@É GMÌ÷‰ˆ¨Ò'û Qáafg—épõ5ÕŒ‹ù:ç*ïiq\ä=ŸÊ{$ò^Åé,Š‹I‘äþ0pB͘Úw®ìßHWCÙ;o_JÙ?“ö:\i‡\ §ÄjHôõ¹‡ŠÅå`7Ž nëÊ{xiÝh»Äèk[h«ÃlÕû!Š¥Îâ‚ 7¤½XÑ+Ey9œ'ãbó•÷´‡8.òžQå틼Wq:›ç.”‡ 5¡f^h¡O ¥yãGçôt¨ýø,.޵ãi8í‘ÂB_޾aõ$ç Ö»ù ŒÍ$UBDB¤DD±¨ªtêaÞ!{bâD»üÝœ}baŽrrÚ:ìD^K©H†ïë/Ø\(ãâvryˆã"ïUÞ¾È{§³.ÔQ\ŒÔŒýmpÓk(‡±¾{Nÿ›+qq¬OÃ9Â…]0.æÄÅLÍ—ö«û÷‘2T´oc\üyˆã"ïUÞ¾Èûnq1R3¦VJPg8Ê>ˆäK)Õ·ƒQ“íxNŠíeÅ8XÓBpÉn` \ˆ>ÞûÃvD7¼„ù›q? Nu‰‡8.òžSå퉼Wq:[à¢%®p1R3f†×ýž†Ò$ÆRÊá—ââT;¾†31Iqå~…û 9ç.¶ÅÕóL/œdd†„qÁ¸¸ÀCœyÏ©òöDÞ«8õ÷ŒÊÙ«!§q1R3æ€Êã.Îp5”F¯èŸÓûîdRH;µªûd;# 'ëÉdJ±4z÷êµÇ¡Œ‹‘ÅÙ,k\PÆý8GãâþpqZå=í!Ž‹¼çTy{"ïUœÎú;ÒÆ-) =Ž‹±š±Öé¬?ÃÓP½¢wÎà»Îìü2½ÓíøN}d@µvê~CÆËô6Æ¥óTšpI1.î“SÎ3â„È{N•·+ò^Åél°¹^ÉÞȸ«eV¦Îž†ÒèÝs†–jo¥×I\œjÇ×pê#ãÚ2‡»F‚Kvà‚q±Tç¼ÀCyÏ©òvDÞë8Mê]$¹˜Tº»ŒûjF¢Ø$‘Ì1# ¥Ö+ç -¤¿¹8òéD;# §ý§šR{®¾¨›qÁƸX±sž÷GEÞsª¼‘÷:Ngå‘”ž½¥í·Ö<©õCÎë .lŒ‹tÎù<’#ò^Çéì9Ú*&Q|o¶5—¦òšë-Ø 0.Œ‹%=’'ò^Ééì¢øª].ù½ Ûêµ§.lŒ‹½tÎy<’'ò^ÉéìiWYýÍÉÖLšÊ‹m Á%ã‚q±—Î9GrEÞk9‡½ÜîÜß™4•—Ú‚Ë›ÃEšKù·ŽÖWò‹¹LOÖa7gÈ&âôÑ•L΄Æãâ‡ãßYnºjZ'ŠMEm»µ´šÂEÒˆýfô¥ªŒPv¢+Ùš¶€ªj³ÖÔÉý„qÁ~æÌížWS¯·ßÂ)‹¬µñöÇ ­º8zeg”û‹÷÷‹‹\J))½ ÏPyFêyœA•zëæN»ðÔ©e2¦ÅAþ †S 1œ0SjïŸÓ¹7w1jFì”&šÕ:‡*}åq6Žd\ì. :âçt„[l`>¯¦Þ-ynV9ö¯´­µ¾D®qâÊ´ÂþŒráþ~q¡†rõê.†š–­÷göÛ'qšº+dè÷ê1­(×1ÅφBÙe¸5c/UtN±à¾¦¯t@%×.Î˸˜¡. :âçt„àbfM½{;L¥¢1.Nû’Ùpaö§•û‹÷÷‹‹F)¥¥”jŽà¢¿Ý±}±# µuØ[8;9é!E,a7ð””RÊÆ„³2ÔD•mCœªb ^3r8/]S8‡&öÊ óŽd\ì¡. :âçt„ë_[S?ÜoQ›9¾'vŒEÞâôÒ«™‚Qö:N+÷QbÞžvÀÅÐú;¡XÅÒ“ýDè¨0ýÀ}¤ÆOKTZ˜œ¼áÎã7Ó÷Õ;qƒÄ^8jzÜ·@E”eB”´v8·ŽÌ¶öô'S“C" ?hfˆBÔDSŠ¥S1.ö‹‹pñƒça ¸˜ES¯ß=uBÜ8¤  q1©\´J™íX¯CÌ·Þôâ0DéýîdP]Í8"Ü÷•ûSÂý±rÿÖv¤=‚ ª€«å«9ÄGwU@›êÙ]Q`8£ÆÐÁ²³Ëôô³5c/µ²F™v4úЀjÍ8”q±Î¹¯EAGüàyG.fÑÔkPV'ÒŽB¥Lâ"™ÂEjžO:ÆS†"%¢ôLFËû \\dáâˆpßWîO ÷Çý^pAM (a>ÔÞ¨^ŒzÙ$.¨ßh‡=#R¿¯Lã"×ñQ¯{©iÝh»D÷]}hÜèHELŒ‹ÝtÎ}- :âÏ;Â@p1‹¦6EX¸MBäµçM£p\ñ&€j$Œ§@ÖTtÇá O)÷/î;Êý)áþX¹k¸"6ZE+Xì…‹‰è?Í{û«Äiyëc…³±0G‰<ñûPJ“SÃÑBÛþ£Y=ë4ãh+“*é­’£"lÆEˆs_‹‚ŽøÁóŽ0p!íUšzeÏ’çd¹ŽìâBMç.†¶GÂxÍ  mÎáBÁ…¸G…ûžrJ¸?öiwZ«[ÍV¡¸¢ŽÙ[ÿ1.v¯ÃØ~QÐ?xÞîG5õ½KÏÏã‚t®ábeÔÐöH¯UqЬ™qqL¸ï+÷§„ûŒ sŸ³96Ó¢X~¦ú*ã‚q梠#~p×ʨ«5õ°1q.ôɸ“siC $#a¼ÎZÄyv|îvñeÁ¨Iᾯܟî3.ì¤`Žé…rwGÈ0.ó9°ÕñƒçáqqLS¯ßÞú„öÜt3‹´\;/ýêX_ 0¹‰;<“»8%Ü)÷§„ûŒ óFem< .ʸ¬’Œ‹ùØj‹‚ŽøÁóŽ0$\üTS¶£x¤=XÕÝ !„F«Üã¢Bˆª±‹FçI ËÖh\a<)¨˜úÕô4yön§Ÿ1zÊý«…ûcåþ„pŸqÑ'çˆF9m$‚Œ šÏ­¶(舜r„áâbbx}•¦¾²~ú`™^oïé-Ÿ:O@ÑÂÊöa¼–+(œXž=‰ sÙ¹ez§…û#åþ„pŸqÁƸبs†º(舜r„7€‹cšú´T5Ð9¸M­) 1ÂEV§Ç~=há=a<‘l ­è \1þH¹½p¤ÜŸî3.ØábëEAGüà”#Ü.®×ÔL}-|<*"åWÓ‹=°4#‡Wa<Å¢ªÒß:ùè«ð}ù·„û¾rÿ@¸?Vî3.Ø+uÎPñƒSŽpG¸¸8ÿ8Î1ÁAöa [_¹Ï¸`c\„Õ9oqQP¸¸˜ISïe4Màa\l Üg\°1.Âꜷ¸((\\̤©÷¬òžßR¸Ø@¹Ï¸`c\Ö9opQPÀ¸˜GSÂÂÅÊ}Æã"°Îyƒ‹‚ÆÅLšúÕm åþáb¾-ø~ØÒpz.Ó sÒ–‰8}t%“3Aˆ„ûɾÇ2··(臎ÜÿÇ¥…wývø™ÞqØßñmjo1],ZøÍ «4›ñpèèOôï ã‚m˜\DÑsôö¹Ùpúb>\¤- ªfÿö–ˆR³×fRïë\WMkk……Ê£I@–ËV{f¯É\E—@®¬+…~ûàª?0÷iqˆ‹ÈìpÞ¿ÆØPËnn]ß]Ô|L: ±ßLÿ·  ìDW²?tü'úw…qÁÆæL.¢ç(úÜÌisúâZ\äRJ HéÆbL¹™6%¢¸@›(‰¨ENÔ¬k’Èé:¹R;ws×ä!.$ÅQ©ì´ OÚcZäj8Õ ük¤Úûçt.ÄÍ]Œš±ki¢Y­sèøOôï ã‚Í™\D¯æßmìå?†«p¡†½ø• »ksc J¢ ­-@i EQ$DrºÜ¢4ƒÊ»u›<ÀÅPð¶5fÖÛOÓBǰ²¡:‹¶H£)…@Ùe¸5c/Ut•‘äºõ|ý?Ñ¿+Œ 66gr=GQôõºÙ5¼¿òs¸RJJ)Õx¸èKÙ&@GÔh/.€Dÿ?JJ)eTC±ËC\è¨P›<ÀEÿÉl7ß4Ô]n1ì%} E,a·õ¯Ñ$-*Û†8Q£wTÓÝkÆù[SÑ5…sèÁŸVF±±›\DÏQEÏ›]Åó?‰+pq4w1èB[H¢ ˆ´T £_#L ìô?ä¸ÉéEÞwÛ2i}tघVè´saÊ™®‘¨AKTZ˜œ”¾: 7ÓÿQyæLÌì¡S"ã‚mbr½䨯~súb^\hßÛ¢êPRƒ2R¢(¥1q .òq“öwêý½6Z¨õ¨êòôÚ‡¤ÕÚ¨Ñ5¥@5¢.ÅŸ{©€¢ÎÓz ޟȸ`c›˜\D¯OQEï›]Ç#§/æÀElõŸ&2T£.ÑQ…¶Ó‘ü>‹ŸHÛhLˆq“Çqa¥P&w1£ŠqÕå#Kå:£Çõ¯‘ˆ ÔfÙÌEÁ(¯{©¶p»=ò'2.ØØ&&Ñ»þgC‰Ò§/fÀ…Ñûô?GÖ"¥0yèÌ8Û85»Ð*¥ÚdÇÝ& bâB;`JpÖ°?;³²Ziï_£^šWöÉòü|î"1y~¯™¡þsnԺáã?‘qÁÆvtr½i\ƒ¸N_üÁ™š­Hrm³DÙÁ†ˆ\u‘m] #Äé‹[Â…è·õøY3¢¢Uù™ê«Œ 6¶»Fqúb#\H¸¦fk·žez¡àl9•!a\°±q0*âôÅmá"ÎÚx\ØÄ=ÉÙâ[Œ 6¶}£8}qKÁ(¢TÌrÚH1.ØØ8¥Ó7„‹Û0Æ[˜¸xüÍã.Ø60îwll®}úúä§±‚=Í7û»=×5͵gÙÓ¿~ø‹?ýë¿ÍO™m { 'gðúÅcy›/IôòðßÌå˜gcØ¿yxxxxøóû7ü¤ÙØnÑ/~Ç—ŸÃÍ}ü›‡‡?™ =3ñâéOLéÏþÎ?ücΆ±±Í‹‹€¬Oœ¾XÚæ\àòß><<ÌŽŠ^fÚùß8©‡ÿŽŸ7Û¬Ò‚‡7N_,lsêÏþû‡‡‡ÿa¶<ÁÛ<¼è§ÃœíVqÁé‹…mÖÕ-òð0c8*zŸ‡îôâŸq8ŠmÖñfPWÃé‹%mÖµóOöð0g8*zÿ˜}zñ×üÌÙØnuvÁé‹%mÞ¹þJ{äùÂQÑë,%ãÝéÅÿÈÓ 6¶›Å§/–œHÎê<­[ž/=ÏÁ ozñGüÔÙØfô!¡á‹Ó ÙÌÛrýÛ">Ï/3O/þ§ÀÏíVgœ¾XÊæÞô÷ïY—ü?Ïùðgà…7½øüàÙØnœ¾XÄf/)òçÖ#ÿ/³>üxñ¿:¸ø³¿äGÏÆ6.^B»"N_,asïøûÒ{ä?™³Ù§ç®óÿгìáŸò£gc›Í9‡Wh‚ÓóÛìõDžþuÈÿÛÌ“ËY¸6ã¯øá³±Ý..8}1»-P­ðßÿÙ2é¹¶²Äøßù鳱ͅ‹ð®é‰k%Ík‹ÔBÿƒÁŘ¹Ý—ù‚‘ü‡?xø?¸3±±ÍäšC¬’=ÓBlÆÁ¯U ýŸ³O…æ|úü‡?ýÍ…}ÙØæ±q}¼óƒ™Ï*„þ—ÿx¡6Þg-<p`c›Ã~yUÏ/üdf­/5"øë‡‡‡‡‡3Ãï3ÞŸ9 ÅÆ6ÇÐ3È«š­'Ûãr‚öðððð¯–è”3óâå?ØØ~n¯a^Ö §/f²uû/Y÷<3/žž9¼ÉÆöcûèuqúb¦ú’«XþùÃÃÃ2‰‘Ϲ§¯¼ú“íÇïe¨Æé‹9ìkÙçû–šÍýøßXžÍÆöÓQW¨Æé‹9nâÂ;p=ý»·Ø•ÏÍ‹Çß<acû.‚}‡8}ñs[|ü¿ÿ»‹‘h~9+jÙØ~d_á.bâôÅÇËo¿õ/kùqþè+jÙØ~bo¯yåôÅŸí ‰©÷åúÏËü‘4VÔ²±ýÄ¥<„çôÅÏnߥCL~½|.Ð¥x¿c6¶o¿?¯_§/~bëìì»ä#úZ qVÔ²±}ÿõ ùâ8}ñƒ»Î8zQ¢¿/àÛ¿xÎÊÆâëþó«ãXó7m­ª„‹>¡÷Æ ¬¨ec»I\púâ»7n­šç/ËvÏxñÄŠZ6¶oÙkØ—÷ò‹Ñwl½’„ 7ž—ˆ©±¢–íöfQôÎ#Áï ¾n¦}.:bE-Û â‚Óß°·å?¯ ÏcžI5°¢–ío{èÈé‹oxØ5Ñô¾ø_³XQËÆÚàp†À¯¾¸Ò~­úLÎ 6¶ëì-|\púâº'ºr”e…ùéË2Ñ5.²ÇÆvsÙAÊÓ×ܬßk÷ |îËBü`E-Ûåöøºƒ‹äôÅåökídÔÓ³™÷…zÀËïGî2ll—ÚpÁé‹Ëçú³ÅUXþ¾P?eE-Ûå¶=!§/.³· çç*Ŷ›ü|ånÃÆv™íc— N_\v›~op›¾VÚûv)^°¢–íBÛÉØŠÓ±‹IØÓJ3šç¥D|¬¨ec»p.¾“ëäôÅyÛ¨>ÈZ$_,"ÉŠZ6¶Ë¤{¹PN_„:[ëkŸ–«TÁŠZ6¶ l7‰>N_„zƒVÛ`ÁÔ +jÙØ.ÀÅnœ0×¾túµÞÚ‰øôë»Û™iø~Þ’w–<žzÛÅß× ‚½,(ýbE-Û{ÛÑ ê“€Çé†Ò±¿zÉ)&Ùcc»\D¿ù}>b›fvÖÜÖxI,²¢–íô+²§)ø#§/Ž ð·tt/k²iA^°¢–í¤í*bûÅñåIûØÖÍ­[4…õÁËAÙØnœ¾˜ÞoìäÖýúEÙøÆŠZ6¶£ö{g—Ëé‹Û|IÊǺ¡°ECFO¿yDÂÆv³ N_L¹Ï­3´«&/ÿƒYQËÆv¸àôÅáØ~ûüìÚÁ°e%L¬¨ec›¶ß{»`N_Œ†ödg×¾„·Š"VÔ²±ÝÈì‚Ó#ÏÂ^Z¯·õW³¢–í6pÁé o`Â@ømuïºt%(VÔ²±Màb±N_8n-ŒaðúÎõea^¼ñ$–íàµØa*€Ó½Ó dü|{:+jÙØnœ¾°.-”" ¯,n{[šQÏÜÃØØ¼—nïÄão~pQ´Qqî)ûúÚàKߟÿ–°±yÃ=^ô×'?¸(úõO›<Å ¸³¢–mÿ¸ˆ^¿øÉ…T_p›ÊëÒ‰~VÔ²±¹¶SYê¯»ß .¨êåŸÛ\ËòÞü•ç±ll;Ÿ]DOwŸ¾x)PòµÑlïsñ›ÀŠZ6¶½ã"z»óaßâûëè½U7Z>»ðÈŠZ66û¾í–sw¾x lÙñV—³ðöQúoûˆØØØ¢(Úï ýžÓA%.¶ÄEô´‚Ú•µllz¾_—yÇé‹çОï›ElWH.°¢–-Š¢èu¿/Âý¦/ÂJ\DQ=n7ìxY¬¨ec‹¢èklj¼{M_¼¸_ꆗô²Æ°µllûÜ4ÊÚ}¦/‚K\lŒ‹èk/ûÅ ¶»ÇÅÇžç]¦/žCŒ~lyQïkðâñ7'0ØîÜžv]=âÓá%.¢(Š^6½ª÷5R O¬¨e»wÛw±¡ûK_¼ZèmÛËú\%ÍŠZ¶;·—™¼7‰ãS¨!ôûÑ:Ò%VԲݷí<œótg{ú<‡ê¯>6¾°ÏU¾ÿéï…ÌvǶ÷Ê×oÏ÷ô´ÂL\DQš‰•2Ѭ¨e»cÛ½·ý¸£T/ÏÜ‘ŽÏ3בU±¢–q±ß¿ànâÉO!{ªÍ;ÒÓJ‰hVԲݭ½îþ/xº›á^Ð`|Ý|ÍäãJy,VÔ²Ý-.ö¿2úåNÒá&.¢(ŠÞ¶ ¾¬µl“µl÷iï7Pýå>Ò/×& Ú/Ÿ«= H±Ý¡½ÝB±°{H_/aŽ÷¾ÖE°¢–í.qq #ó{H_|†ÎõÏžÁûj:×Ï׈íÎìñ&zýí§/¾‚N_AŒ·ßWþ°¢–íþì6I·ž¾x ¿¨úS ØÖ«dÄ ¶»³Y¥zãé‹=ìuÈ ïyµYÙc»7»‘0Îm§/>÷ Hø ä |®7rø`E-Û]Ù­dìn9}ñµ‹‡J!ß§—]¿ü¾ËŠŽl÷j7³eÚí¦/v¸ˆ¢€TO+úpVԲݓýº™¿äfÓ{Ù¤=˜ùÝÓš1"VÔ²ÝÝNg¿ÕÚŸ{YIN8ðeÍ®ÀŠZ¶ûÁÅíôõ—_·ø€¾vô€6”Yµ+Ü„¢öm[ÛG7ùx»Ïö&|ß³Çý00¤%Ÿ«*nAQû@l[ÚNpñvC¸¸ÅôÅž"l!‰ÓÞW½˜ÝëòŒ‹KpqKÊŽÛ[}ñ¹'šå3ßWëì^Q˸`\\ÒÏoJØqk«/¾võt>‚šÜ­«¬~ú½ïi:ã‚qq‰Ý–ð¶ÒûJÞ¿„uóWÎ(ì[Q»%.b!Dªÿwø¿£GÆ“¿I+)»#¿óŽB0.ÖGÜRúboÒàÀævÏëøw]doK\Jÿ/yæÈ)oŸdÕ¹ï’ß·SŸÞRúâsoñÐB+ö¬¨ÝÈ€‹¼…5ɸàÙÅ5ïìͤ/¾v÷h^s—knE»VÔnŽ‹6þ6.„žYèFʸ`\\ø¸¿ãqó¾àj3®¾Ò·ŠÚÍqúÛ¸PTN”*%ãbIû}k¸¸•ôÅ÷4 ÎY®›|Û©¢v{\@x¸HºR•r˜+¤u)z\ÄÒùeÞ§>REJD$J¥”ìlê»k”’I‹ªQJšßyM1.învq#é‹Ï=.ˆ olý²6uwª¨ …‹‹Zÿ¬íÌ!%dƅйŠV'¶›aÊQUq?‡€"&"JL"¼õoôoÛ„hÜãâþpq鋯]î,ÿÞÐzýÄv©¨Ý\¥AaqQ÷¹ëŽˆ(rÙ”:ÿobQ~ƒI@CD”ÙOå3#5Ÿ8û†ßÒ. ÆöŸ¾xÚgŒð-ÀTïúƒ‡=*j·ÆE M-.:m#jýCM%ÒZûõ€ŒóÌÄ ²\@–hõ€ª’@¥q‘SÒê“FM1.ξá7ˆ‹ý§/~ítO‰çDï«ób‡ŠÚ­qA@iqQH 6¤!BlþdW9È|ôZÌ=íÈÌì#&Š3Õä:5¤¼GM1.î{¯}ñº×¼‚ ®?ÙÜŸ¢vs\Äaœy«=¥&fd‰P %¥¬M)›;%Rµð0(£Dÿï¸)ÆÅY\Üd5°}×¾xÛmIÜÏ 1ýº¾óþØÙ#Ü$d‰ÕÕR´šÒŠ „ÒèüÜ…0S„¦OWh\4.’ã¦çߥ[ÄÅ׸¹s£ýÞö0§E ößö5ÁÝTë²ûõm/˜*û¹P(m¹QFé5ᔡ­Sý”]ÝãB¬»pá6Ÿ¸S\ìoÁ~íw3ì§@Õ$³w¥¨ iÛ㤨2Ó œ*l0ªqN¯d±|´±¦†¬R6wA^0ªáezרí­Ó3×^Ó¯{.Aª†y‹äóóãâ \P×ã¢ÐÄz™vnLf†°ð(ÊÔ*i[Ç]«½¿É€Ä™£~²?õqqÐãâNg{ÜDCG1>÷|×C½øµ·Š¢hWŠÚpA…ÅEÒhK»:Bs"Sf„ÐÊÆ[Ì ˜õv©žŒ´y®LîBŸ\ê9Èã¦÷Š‹¦/žö=Û{5³ÉZÿý(jƒÀ…è÷”M캼LúM^£ (6«´Í¶„D¢Ïl·‰»¨Ûì8ØØù8wqØãâÜ4ýVq±Ïôů}Wñ| –Ñ›ˆ«w£¨ Ôô›€$z*ÑX/.jEl¯ ´e¯}Í*ðZÃ%nZªÏLTz²’¤º'šb\Üéìb—é‹×½×Nw–—MºÃë>b‹[WÓþ¯Ï!$•çÃÓ*%ê«é%ùÈÁ§•wxU¥D‰°ÊÚ¤2›BÙjzéP—ï )ÆÅ}âbé‹}'.ÂÆÅF{‰íCQ˵º—áâ–Ê•Žloé‹§ýËÔÞ޾m‹](jŒ‹Ë\ê-îbmgé‹'.¢(Š^B&ôû6sŸ(jŒ‹Ë†\óxÔ‡õìšËÚUúb÷‰‹À£QQô¾Mæ9|E-ã‚qq.>vÖß®º³{J_ì?q<.¶Ø>*Šv ¨e\0..‹—¿î¬¿]wg÷“¾xºõõa;Æ”­OÏaÏŒ‹ \7‹ý¤/n qEQôx þs#œ…­¨e\0..tS·‹è÷>ÜðM$.ÂFEÑïxñp ù§¶-mG¸˜gÔ3Æ…è¤ì¦×¿è%2ÎB™eqñ¸‹ Ïm$.æ~,ôÛˆ¿ÃÓñì‚gÛ£r»åK5qkd¿Q˜ ¢´ÖË,k±Ô݃'Þ{ý?·?…>›{ÚJ§ô®¢–qÁ¸X3xàõ·fØã+>‹X¶dÿYèÎî ÎóëvK¾¿MÒãflVQ˸`\lŒJ «U¿Áäq\=ÇWËýÆ >‹ü±·ÚÎË?–´—Íxª¢öû¸Hí¾Lß°ä›ñhÆÅ-£:ãüãvrg`é°a\„®Q}yŽnÈvðǼlŸ|úäT÷û¸?(‘m]"›q±Q¬ùa‹ ‘2§ªV2vp!¤”i^¨åÿ¥ÿDËR•RoK)¤ÌÓZ5âÇw6ìôÅÓ¯›I\Ìס–µ¯í˜¤¢v\˜¢ªlûÁÅ<›ÂƒQ­LÜ鄪 mR?¥wµÇÿÝWu¦(VeÎ+ S®÷‡w6èôÅómíòø¶Iðûv¼QQ» .j¨Ëƒ Œ‹@ÞîÙqaŠ_eu5DŸŒ©Ó¸Hû…sž˜áΜ¾¸©ÄEEO»Ð¿ow×TÔn‹È ”ÌŠ}áâ}öþ&úbˆ]‹:‘­ŽM9¸H:úŸT—aólà êFÍqgÃM_ÜVâ"Šv‘¼ˆ¢ès;^„§¨Æ…"îJUö †T*Õ‡ Ò®T2±¸ˆ»rø]Ü)¥¤¯¢—2§±Ú>ît}TR$¥êÒáˆY*UwD”ËÎ^PǸØÖæ©—é÷·¡znmÜ~mVcÔ'•Q “+O†óf¹³¡¦/n,qEQô¹¿hËmœBSÔ>™94º˜uUE4îÇ„m©q‘dÎøÐ–ÚöfOC (JMkµLöÿÓÇŠ˜: 1¹ñšq±±½.ÑßYxõÔs=û„:…‹€’RÖú£œÔK}÷Κ¾x¾½òT_;ÙÏäsÃ[ÿVtô.€¢Ë ýú¦- ªçºR&À·@]5­~aKdRä%PÀE äD¥NvK…Êœÿ@Ù‰®$Ŧèvb±Á¸Øyì`¢¿ÅU6L'R=ž@v bHr ÓÆóÝÙ Ó·–¸ˆ¢½$/¢í¶Ò³Ê z’ˆâ ŠˆÚ”ˆâmLTêO¦@c¤ó i}lÜžLL4hc¢\3E ÿS ~ ETk¨Ô7+¤ºc\$ªµsƶçCe>ªs¸(”63+™ñΆ˜¾¸½ÄE͵òs—½éö“Ÿí­1‘‚""…ÒhhÍ'’…ÍG$DŠ”ˆÒ“Ò8‰Ö¦)cï+It••OUšFíÍ.Ó¸ç`ìl"pÑè¹$ÊsÁ¨ÆŸ ÏygÃK_Ü`âb¶È*÷ÓB@ŠÚÈ–RJ™º/à ~Jld8:Ší§Æ DI)¥l€J/¼Êšê¤ƒ¬)„ H‡oþ‡ˆRÑ5…ž½d¨‰*ãV» º¸P” JJíþ%€,Õ½¨;™êÖx!‰¢LçÇExé‹L\DÑ\KyV™Ýmºµc8[‚<8‚Fq ’•PD¢ÿM 9|²¿ëM’Ý4®mN8Èr8£q七.7Ïñ}ƒ–¨¼]Õí=ç.Œf¢µOkê zO1.²RšHheã,И÷ζcÏ-&.¢h.±ÝJ¼ØvvH¸ @7Ì5j3ó(¥1aD¶ðC‡‹.zk§pÑ(ê<­¡t^¼ŠuŽqqcÁ(w]^n>¶}ÛßÀ¼ÕãóO\Ø/‚‹°ö ¿ÍÄÅ®¢Q[n¥G a(jN.Á“PD±:é8”ý”™`”ÉVêÄCJDqžM½¾ÔÓ éÿ/ŸÀ…Uïê¼ ¨s´¼L/\<ÍÞß„qûE¿uT’­‹z¸È5'òaÖ  --‚‹ <ô&.v†‹-·ÑÓ›{àÂ$¥mzÁ¸ñÄæt†º„ML;H8´Ì䯉b@âB˜©Dl¾5G[Þꢋ}áâãmþ–мJ¼”u’ÇöWn5½TTÉð%¹ áÀ¹ïl@ñŸM\ÌÖ¥VãŶó‹ µ—àBuJ±líRÚ:¥¤Ô¸È2!J´vVAÅD¤<Ý«·ª;qö¢-ä±NÅ&ÕMq‹›]t±/\¼½-ÖßN)œ6º³Á8é[M\DQ½|ìéjß7~(j/ÁTk£D5€0£½h›’ÄÃÏ5xËôjGäTõa0ª²F6¢Ußôîµ{ÂÅ×bý-@\„ºÝÄÅÞ¢QQ´uÂy{EíE¸ˆM¤ØD šPb8@kv•í°¯ô$.¼|ÚÿçiÝh»Ä$IÄd©5ÆÅúŽëu±þ .ñÓ7œ¸Ø.6Ý>*ŠPÔN¿¾¶RÞP1¯ë’ð~—¿‹EUÖH·ìž©ÛŒøí/Ýâ|I• ‡ŠÛ]t±/\DKã"WJtgƒˆýºÝÄEEÑÇÎþ¼Ï¯wkEmøµºë›Þê|O¸øÜUûù }ññÝ´½ììï{Ú¼ÅǦ²Àq!DwÛ…”ö„‹_w†‹íA/¿¢·½ef¶_‘ó¶¥¢6p\¨ÉRŒ‹½£Ö²9îìÖé‹O\ì<“§ßÛÉÃÇE3.n»š]lž¾x¾íÄEEÑëãÞ®øeûÿÛ)jCÏ]Ü4+8úÝÔa¿D7ooû[U@„p³"{ÄÆ¸àÙE€¡‡Û^q±ÛhTf+E-ã‚qÁ¸Ñ5ÜAâb§¸Øzû¨(ÚLQ˸`\\jsìðszω ¤Þ9ʬÖIÖ»³›¥/î qE3m\¹:/Øy}E-ã‚qqqœy1\Hœ[Ÿ7좟트ØÊmßCâ"Š¢èëkWÂÎ-µŒ ÆÅ®pq)-f»³Û(íï#qEÑÓç./;„zE(jŒ‹‹qñºT»Ób¾;»…羓ÄEí3yEÑs{¯¯®¨e\0..3oŽ‹Ëi1ãÝ .t'‰‹(Š¢Ï‚1ˆG´¶¢öáXbQálh²‹öç"ö®ì©¸ª2Eì~še\Ü.:)þãÕI¶¦^O‹¸+•2ÇçÒQ²[àήîî%qE{M^„°}”žú®zÇÆrÆÌšj³ ùðse7rÒ››C¥ÇF‰i)N ‡->*ÜöîP7€‹ßKõ7i ªº¾xß @x´H2]¬»#rÊ7fC 3ÞÙµCCw“¸ˆ¢(zzÝë…QÐ}]Eí9\˜’H¸èßìÚ”Njà"iq}îšqqß³‹|´=Ø.r ³“ѸêªiuŸ‰†ˆ(q />ìÖßQâ"Šv›¼æ9½®(x8™Xµy\"ŠEÞš"ª1ФD¹. 7Œ8 ØRJ-ãâ®q‘ŸÜLR5œ2¼é‰¦Îoíý3ÿ]5:tG‰‹]ã"„í£¢(ŠÞÖ»Œ‡3*wó:ú¸Ð“†6%"‰6&"*QœÌOþ®°>¢ã➃Q§i¡GÅvpQX]m¢{ ¢v(?ó]Ñ…ßSâ"Š¢èým·—HÔðq5Eí9\H-:À =ï°ÁâH‰„̉„qWª²#"Êk –édú’ÐÙhT eOd©TÝéÔeK¥º„qqC³‹TJ)åÐ+H¢Å颉@KØZY€’RJÙ˜éi†š¨rÛ˜÷ήx¸«ÄEEÑãë~¯ý-‡õ¼Òã. ý~â‚2/OQ6-Ñ}ÚCÁ›5xñh@j^÷ÈÍqÒÉšHd:¥Ù1.n&&\8)¦º?¦'¸I4©ƒS-Qé^|ا¿³Äž£QAle®c•nsB(ãÍ'pÑ Yj¢Xû .€¢Ë 'J: ²¸ ÊQØ/ÊNt u[Yžg7§Ú.^æéoS¸¨åÉ1éI«µQ PJc‚ˆ(ªX'к³+ňî,qEólŒ¿Ÿ$t¸Îµç”QF¸8 Ë$ B¶¸DëéǙܙhT‰ÎYšlHkÚ*uû-ãbû©÷ÛBýM¢ *t6ìt ¬3Z½>Ó÷=°Î½Nò°KG~o‰‹(Š¢]ò5Š«(jÏ iËô8.G+ÉÁE¬³ä—àBG£b µ³‹®²É "i¢ÑÍéÈ6ãbï¸PDÉ©pTß”Ef:Y7ì%Õ–^²|ö;»F˜èö‹sOýÑûFäs(žVPÔN§Ð !D^Cù&ƒQ°‘¨ÂŽõ,.œµWçp¡£Q9 çÈTtMÝ–²GVŒ‹Íqñ1O;²LoŽšXÕm\TD9P&DIkÓqëÈl—¹³Ë§/ÂXùµ¾ÃÝùå‡2;Z^Q;Kö¶‹Î§q¡ìÔ?éiaq¡®ÁE§“”ù°‹\fWŽ;ëþÌô…q±é|÷užþvl?u¸ªÛ*®ÛTk(ŠÃ5œEËÜÙÅ#EŸoÑ=ÚÞµ`¿BáÅâŠÚs¸ =|;¡ŒJÚ!Ãø\¤@•±=²PÔyZp‘3.¶Ÿî.‹ áâ.¨ «¦jëÊ9B.}gG~½Fwi;O}”î¡ËŽhÎ i qaÚ”´hsú.¨D£ì´ûŽ(Ý–^•úÆÅFè…ú›Ý N_ñŠæ9ÕôbaŽy2Re/}g— =Þcâ"Š¢èí}ç@@êç÷Ewø=‡ ³ïi«Lqëî(=‹ä,.r´%rÛ¸0óˆØ*£ìZ VFmoŸ õ·9¬v],ugMEßgââ¢Q!%UÔÅE'„B¶vȯܟW ŒWo9lj> ?Œt°ª[¯Ùb‹‹¸EmÒç… X¶zë*ÆÅm£~lBtãaÉ2wvÁxÑ&.n¡l¥§:Ëm VH«Å*;Òæ&HÔ›8† Gðr¸LO¯/‡©Kd2À¨‹gËç0.öŒú±)l9õ°3§~¯‰‹(Š¢×ÇÝÿ /‘]NQ{­)V1ÆEV§V¦r90L &qaÔWiÝh»¨HBÕÚ2f\p0ê$.Æ]d©;»Ð0ònQtÉ‹(œí£¢(о–Ê¥œ¬¦—ŽÓŽñ¸–^*‹MÊÒæ-ûÝÌ«ò _ãÿcNªÄœ&¡(®*ºYã`Ô<¯vgòë÷›¸¸hT8ÛGEQ=.¤Õ ¼V·¼Ù(Ô>qñ¸Ÿþ¶Ø]$jtlj‹y¦­!ð" 9ÒÓ2ŠZÆãâ Où¶Ÿþ¶Ü]Àµßsâ"Š¢èó&æVŸ!ÅÔÙ£–qÁ¸¸">˸X$pt׉‹(Š¢¯¯›ø3žCâÅŠZÆãâ \¼ï§¿-xgg÷î÷¸ˆ¢èéó6þŽÏÖ§/ ¨} 6ÆÅÅ~òu?ýmÉ;;sìèÎQt¹î(Š¢ßAíg2»¢–qÁ¸¸¦ÿ1.æwð÷ž¸ˆ¢[I^µ}Tͯ¨e\0.ÖÞ.æ Ý}â"Š¢èýV&XOÏAofE-ã‚q±=.Rq~I³æ§_Ãã/r­qggôñwŸ¸˜'ÌŽƒêyÎ[dqÁ¸Ø>u‰¢aØ€F¯ß>Ø—F‰5ïìl$N\Ì3 Æ^ãÿœŠZÆãbûÙÅu¸°Õº¶1«×¼³ŸóÈN8qqk¸jû(ͯٶäb\0.vƒ AD$ê©=õc‘·~íÖÅïì,/!'.ìø†&Y_±o>E횸èÃÌ‚1ÁÁ¨ïâ‚H!£‰,IëÕn]üÎ>þžƒ9œ¸0wó–fYï¡-#ùœé‹!nÐÖ1“b—¸xZ¤¿I(JkÕø{IGöèv ¶”×jwöíçNáó+b»¹hTXÛGé Ï<ŠÚmpa ¬²í ?ÃEQ€_ë`·{Ã…â\ùøµîìëOý×gÄv‹¸kû¨(škK•q¡ßì¤8(fø؅½-… ËeëU^œÆ… ¨¦qÑ ]÷ÎþúYúâéwÄfíãå¦þœçÐæ³(j7ÁQ‹‚Q±G\|-Òߤ>Ä:+q6”™Ð4.$¯zgèï=Flýð÷ã¶þžÏàð÷ñsEí‘jz2¥R]<•ÈR©ÚèNÒ¼,¥&°w¥R21§‰¤T]z6úLµ®£÷톽ӯ‹ô7©' T nO‡2Ëô(.’uïìÒ¯œ¸¸ÝhThÛDÑŠÚc¯o– …ºuÅl›pЯ­2b–D×vú¸º…yóOFŸ©ÐÃÈo7̸ØÈfÁE*¥”R¦..ôáÄ3@'„y ´é‘`Ö¾³?pùoœ¸¸e\DO¿ƒ›<þXQ{*–œg&\ ”è @¥-PW úm[ ®šV(*;Ÿ¬Lù£†[M±çÀ…è‹»…´º_œ eêÚî¸Pkç.~PâÄŘ¼·š lû(ýþ¾þüõÂE©'-•hcpPD%êWVéñœ‚Îr$m96>,[ÝÈ\ 3.V³Ÿ®*;‹‹ü¢ÌW„2êG^ŸãÙÖû­ýE/®ªù™¢ö(.R3»O‰DW™„ƒ2¯*Q¬ßV›²Îd”j<-¤-‰æk˜q±¯`ÔT³³Nq.2¨)\Hon²ÖýfLi¹ÄÅÛêv”2ì'ŠÚc±de_QíÐSÑ5 (µùŒJG›¤”R6@5¼õçpѪ>B=KÃŒ‹}£¦p‘D|z9Þ.ÒÖLVW¾³ßrü &.x_ŸpÞâ—óS?QÔ^KÎ3»é' +>‘Pþ|AžÝËÁ£œ½ªgj˜q±¯`Ô.ÚŽâQÌñ@W× !„:¡5à¢BˆªÉZïÎ~#¬´dâ‚qÐ[übÿãy1\äD€¢ÎÓZ{u1’¥4&.Á…j˜«aÆÅîƒQ-µ@ŸZ¦×[G“;ÒæÛÜÙoøþ%Œ‹Þâ÷õooßmÝ”AÏï„ÈnÙ¡tÌÈÉ7P¿:;ö(s!.æj˜q±.ž–Á…Jk@U£î1‰‹¶4‹¬N·º³WG–]qÁ¸ê-~1ƒÿôûmÖ×W;v  3l‹[¨^}’ê·53Áæ¸~v1[ÃŒ‹õæ±oKø²T$D{qK»Ÿq:úm<]KoÝ;ûú¾,^v‚‹Ø}>g-­¤Ì“þ;q^Í’º›õɾö¤âë{ŠÚ£¸(ŲEC·z‡†PZèšÇ]¦½z” Q¢eM^ýèªn2˜ønÃŒ‹­¦±o{ñe«ÞÙ«Ô& ¯¸Ø'ÖÍŒ-iaÖX¥…™ž]†+¦æåt_{²”Ç·Šì_¦‡¢5óýÈd€V¯€™ (E¡×Ùzõ£«ºµ}¿aÆã"¤;ûtÂ~á;Á…²:–¼uU-7ˆ‹è9ÈÝ¿£¨=K®´º*rZ·Ú.ÑéY •©õàRׯ¨è¸ø~ÃŒ‹­úØ+ãbò¾\.6Yz«¨à"²J¤&ù”éFz“¸pû¨(ú–¢öx¹š¸ræ†I•Q™ŒuzÚt’ÌÆªTŸ» 7Õìì-sû¨(Š¢Ç‹µŒ Æãb•ôÅZ;…“ênuR[¢(Óþ·qæá‚€VÆq×j÷ïVêO–Isp1j~oq˜ÛGéKû`\0.æÆÅãâ[é‹Õ×ÛâNzBhecJ-Ðæ¹òsfõ„Yp—Ž•f âãbÔü.Þâ@·Ò©øóŠZÆãbÍ\÷Íãâ֓݇ƒ‹¸€³$Û]d—¸ Ñg¶Ûäð°Æ-•èãbÔü>ÞâP·‰¢KµÇúVšûµ*Ž[앬91+üÞ>þ¾„7·× ãâæîìä;·¢—ÆÙ›ò‡7-erCà*6¥ L9Dÿ0ªZm™¤ºÇÍïä-u»Á(º@Q{¤o%¸xsIá(£Ožó½]Èý]ÏÛHKÁ¸XÕ2.¾1~\1f½m5½Þ¬'ÉÝ÷³ªR¢Dèâ‰ÎØ2­ª#‡QÒïm«é¥}ýD¿ù]¼Å/ó✢öHß*€âB?ìæ·ÖÇEÒ‚q±ª}2.®O_¬©¸çøràoq°ÛDÑ9E푾u…DM`X„¿8.+j€qÁÁ¨ÀîìûÇ–‚qú[ü2/O%ã⢺ ÅJ¸¸ö׌ Fm`£ÐÓºéMÆEðoñ{ȼ8¥¨ì[RµÌ…I©º”ˆâ®ìk”Œ> @YL ¸H¥s|Ú•J&^[q§”’Õèëóc<2÷NÊk  > £.õeëÙ–)CŸëŠíá¿Åï!¿âǵÓs Pu«7N´Ö­5[³xÐ×4²¸¨ ·T»L´¥Æ…wrÒ›Ù;_¯Næ.œ“TÛÝs0ŠíDôiå­‚;x‹_߃î¾Çµ“}K ‰P¨Œ(nºjZã˜ýB¯Ç/\¤- ªÚˆ¥Ó¨+eVÓø'—ȤÈK?öuÎIIt"f\¬ˆ‹¦ÁUÃǵ3›Œ‹=¼ÅÏAóâé××u¹ A$m»Ñ.;VÈ? ³ÀR¸Pzyf\ ‰Jý©ÓðOÖ{IÆíñšî¸pOâÜÅÚöõÆ0¸&}±úº,ÆÅ.Þâç°‡]ÓŠÚ3¸ÐãöÖ¤²ssò?j‡­ÃQ6 ¥ÑКOÔÁÉ™.æž^šË–PþIŒ‹µíqqUúbm·ðôÀ¶²}ï-þ6/¾¦†9:/-¥”nÆØâ¢I)¥ltÐÈÿ¨¶GiN$Ö…'@G±ýÔ@œÜÈšŠ®Â…{ãbu\¼3 ®H_¬_ã€gûx‹CÞ>*ЦµC.Úõ»ÊIU÷ÉëæÀ•4¸\x 9|’P'›-aÚæ\¸'1.V6s®ûŠôÅK²;y‹CÞ>*Š¢èéPQ{ . ”ÆÄÁGë° ã膹F up2QÚ•8µqýäªîá$ÆÅúQM&ÁÅÑé-<ãb/oqÈÛGéÏXQ{:w¡ì§ZÿO<ñÑ:ì(4.b+tÒq(û)3Á(¯­”ˆâ<›¨×{ÎIŒ‹õ½ ƒàâÑãñiÆÅnÞâ—Ðy1VÔ^„‹Ìä¼;í›ýN°É®´h͉:ù¡Ìz ­ŒòN®Ì¹]¿È¸ðNJŒ‹`_¶?|DŒ ÆÅ‰òx)j/ÂE” Qbô®þÇÁŸ¨SŠ¥VAu@RRj\ø'+¨˜ˆŒB×2a´ª»3{\&æ’¼“úJ¿Œ F…†‹ÿqÁ¸8i_Á¾ƒçÅPdûãâZ\<2 .I\èYØ×+ã‚ßâÓ³Ðð¾~E-÷-ÆÅµ£fÞä²Ä…<¾1.ø->=¿×ΗßÜ·ß0Þ4êÂÄ…±ß¼Å ¿Å§íwø¼ÐŠZî[Œ‹kqñÅ0¸,qa3…Œ ~‹Oûâð"ú|å¾Å¸¸Ú9×}iâ"Ú }Á;ÒîdGZ?™ü~§þúõĸ`\\ëf\š¸Ø"}Á¯ôßâÇß;àÅË/î[Œ‹«g¥Ìƒ‹¤/ø•Þã[üò{à žIî|»…3d\œ¸Ø }Á¸Øå ïe£0î[<»à`Ô‚‰‹õÓüJïó-þzf\°q0꾫§/~ôJ»{4œ³4×›æ2õw€8Þ¶ÂÝùA$·/®d2Ó5ÚKÜÅ[üþɸ`»9\üb$\“¸X;}ñÝW:-ÅUõc’¾œ¦òö—;jÃöo¥-œ£¼«~¯ÒâL;âºKÜÉ[þöQ?Š\¶sÓ±±G.Sºd`rd\rõÀäÇ̸à`Ôµ‰‹µÓß|¥“×ᢊRe£íH/À…ÙªzŒ‹¾tZ}r“é˯Ñ^â^Þâà·ùéP¤œ¤ø‘Î$ôþ²ç{Ú‘qÉÕ“Ÿ^0ã"Š¢èõ‰¡pMâbåô…óJ‹+\¥À•¸0;AËK_â¾mQ›S{\"ŠEÞÚZ)Úx\¬µYõloñó×M⢊ô>÷;Þ7ƒ¸Þ¸äêÉO/˜qEQôÁ»€\—¸0鋯Õ_éã5gÁEEDq‹êJ\ôåÐ<\h¯b*ê”§^ÎkpQíì-|û¨ŸEúª©ßð¾æ§—ábj\rõÀä‡̸ˆ¢ˆ7º>qaÒÛã"ÍËRPjò¿qW*¥Ã·y Ô2»bç"Jd©TÝI Ô2§­}aÒZ5¾{öóÌCÛRŸ4Æ ;Èý·øX;—è\£½Ä=½ÅoòÓ¡HݹÞûšqÉ5¸K®˜ü삼iÔ•‰ ë 6¥õt\™îd€¶#]ä#Wìa ›™¾­X¦ô´^¢¨q©2¯Ð™W¦¹œÄeæ„øD}Í.üKt¯±/ª¶§·øé÷ã^qqÁPDY¯;xßT:°O»RÉÄz_o `Æ%— LŽŒK®˜üì‚&<Ϲîcwæt>ûísãW:mºRƉÆ-PWM ¢¤:û®Ø;‚ÄÿÏÞÛsÉÊíz³ Á B4ˆy×rr‡;ð$Þ³ffÍ×Μ{9´¿ýÿÿ€I Ñ@ÓŸƒ <Ïžn éF¥«t—TT­hK³J­HÞ#ä…Ô †y!”=éNß .Â[ îÑÝb\NߪÓG]튔VÝq£ï¡4P—Þ%tu| r~ÉÇdÂ/9Û1¹î†.ÖÍ`˜ð OXúûÏÃLºRJ@Jß_ª€Œˆ c•rÍŒ},vQYGLãQf­Išà¡=òTR§4Ž $'§÷Á=†·8¸Çå˜YñÛŠÓG]튤Vݱãm¦u¨aÆì™Kôq¨ŽÉ„_r¶crÝ 3.óqÊ“A¿Ï2iÔ¸·Mg+1¯i«Ë@:Ž‹ÁíÁ®&éq!ì¨!­aflÅ[˜Re“¸Hí?òe¸oqpQâbÍ飮wE¤wíè«L!)¡¢ÊüÕŽ]"ë{ÆIÇdÂ/9Û1¹ê†Ý Çdk –Ê>"|aLºQJ)@)¥ºY{æÖƒTv0URJ)à0Ž‹ð"ÊDÛ”ðp!;Enn˜±¾V!ŠÆÌÆÄ(tÇ.ÃÅÑ-ú÷'.Vœ>ꮈQwìèëD3ÒjûW>~ Zê˜Lø%g;&WÝ0ã‚gsmÑF¼„/¦Ñ›ˆ W¡ËQ\ Ž "÷ @t3ÞG›YAÒ_»0sùc\('ˆùYŠ˜ºÅð#ÅÅÓçëªqq•+bÔó´R÷€R ¥ÄýÕŒ\Âù%K“ ¿älÇäªf\0.æÚ2Éùþá‹Y\ˆ~pOJÚ&Fq18¢PÖEæ‹QÍ1.ŠüqE§WF-ÆÅà÷+.ž>ß׌‹ë\ H;úöORCºçàÎ/Yâ˜Lø%g;&WÝ0㢙 —.¾˜6é̹=n0µ[[“©mzán¬š£ÒeÃ|îܱðeÙšpÒòi1Ê¿ÅÁ=F‹‹§¯Ïøp±Ô)t8úmßwê‘K4#¸(ÎóKÎvL®ºaÆÏ.® \<(|1³zÅÚHfÌ'·CrëæÜG¸ŽÖ>à\/æíª÷!.2Ý-‚/–Æ.Â/1¸Çxq±ÒôQ7qERÀ,Æî%#븿ò‘K‰Qééa~à—œí˜\uÃŒ ÆÅu‹Ç„/f5€*’67æSUJ”j·8½èe_!D:8"ÑfIJêNû…´º¥D†Ëávc›kK»Ö°ÃE+„‡½ÃX¹ß&®#D:øƒ{ŒO¯QáâWDvzî´LôÓÖtìÒ uŸrL&ü’³“«n˜qÑႳ€\¸xHøb. œÙàkÚ @YÚp @+ÂE/Á 7 È£Ð&Â)¡\y2·½®km°¶kE?øÈ%×QÃ/Þc̸˜Í'³\\äŠt»çìè+:£DšEE-Pg”Vc—H½…´§“ ¿älÇäªf\tþ1ãâÂÀÅ#³YCe -3ÙûH€®ÎÞÑ p‘Õ€nS37¶TÛPY ¨pIÈÄ0¯mRñ.ò:ó¤t!.Â/ÞcÔ¸Xcú¨k]‘.o¸Ó‚JJ»ˆS  ôØ%¬_²Ä1™ðKÎvL®»aÆãâÊÀÅ#§’L'Ý$šˆD‘úcÖU.{FpDzHÉ•Š1Uf„Ù•‰t$I©Rp$ƒ¢5a5½@Yì½NÁ-ú÷xFiÀZñËú¶w_늅’Æøö95PbôÖ/Yâ˜Lø%g;&×Ý0ãÂႃ—.¾˜1ieœ­Lß°„ºÇªÁüA™Ç×mÅ3Ùð׈‹®HâýÙýóÀ^øÅé¼KX¿d‰c2á—œí˜\wÃŒ Žu_¸°?à篘t}H¨K›v“&BUà&­˜¯¾º\¬/}Ôƒ]‘ûû%‘9&Œ‹½.løâí7Lšò¡r{ƒVßÞŒs¤Œ‹§§“)Ž×„‹{¸"w÷KbsLâÄ'º´waÂS\8?ˆ–ú:g9;¡¯3ƒ‹Á  jE[Òyo‘ãây׸ø{ã¯SQúu­ú¥}>.2 ˆ¬”µ•‡‹ÎÃmçî$'yèÎVPD ÒÑaZšõ‘TÙ$.ÒN>c\ÜQ¹¼+R™¾U˜§9æm@4þüƒÃR %*`|bì e"I Uæ¯vìC;&<%ÀÅ0vqÒ×9ËÙ™óu‚8l•U±4”3Ǹqñ¹ë, · \ÜÞi4&Ý(¥ ”RÍQ,O"iÇx@I)¥l:I _´Ûw»ÜÙÒ(S:|5Ád˜Q­B5Œùgèf\„Û·_ÇŨ+¢ÍÄ51¯yæ¿F\²ƒxXITCA ;ÃIJuj¤§ÿ}h$ol ÅÅ)_ç,gGÎcGx¿]à°‰ö`—ân»NõqûeÄ· _ÌÆ.”gAö/ɸt¸¼h:w:¶ŒDB¹Ž}ô ' ¥0ø968ÕÅ.–/ Ù .ž^^_~c®HæÖVvŒ;ö6ºNTzƒ¿XE¤!€ÔüûøŒ® ¦@K‰û«ùPç„§œÀÅ)_ç,ggÎ× >xè°e¢mJlŸû¥Å7_,ÃEÑý••´M8\ ^4;›ÄE3‚‹â´¡tfeãâ¸=ÿb:é¾%z—ZѸ·!ºÒþƒÃ@’BS‰–J´g×wdÿר‡ÖýTÚ;e'}³œ1?K#3óÁEîMÞ6€‹çǺÿÞ%ns»ðÅ,.Jç ¦½utûP"OŒò_ìÄ(cFm#C«;£ÒÙØ…°pdp²7Wѽkñ{¼˜Å…7rzîH»¹Bà©hZTԠʀl쌾—mßËê‘mŽpÎ.Ô. º³³ß¡PÖE¶1jÇK£n¸¸qøb~!­èug¹’ÛnE‰¾hí²4g$G¸@¨ÛÖÓÃ|o(v…úÐà2Ý/X/8v1‹ïõá"s:Š㎽ ÷„S ìĨÀS©QWhéÝ¢¤±3:(±NÍ!ð;ü«9fpÊ,.Nú:g9;s¾Î@Œ ~—_Dm»Ýxñq/S½Yøb&QƒJA‰Ôh<»)€*%Jm°ÒôÏÁ‹Æß*€:#’ÐIˆ‹Ô[H«[Jd¸v}¸«»B!µ›³(ïåCϹ«£HJûh+þy].œO™§9æmx§º <•¹FF  Ã¹D†îºº•Ò±í\˜ð‡“„ãNú:g9;ÅÒØExãÂöÿD3.8pqïðÅŸ¹\ JA:@ ,á’¸E³þ‹v+¸ ´(·ÿ×6Ð)¡\y2³M¯km°¶¤k…gH¼MoLº|ýu\ ]‘PEÒææiŽyþ7'Ò‡‡%rÛ÷R=CuF‰4‹‰Z Î(­Æ>Ôwa¼S¨$™§Ç‹ýNù:g9;¡¯3q!ÒÁ'Ú¬çÝN¨{·bÔß;n8¹Qøbª «$\.@×/v1x±°{] pÚdˆ‹ÚÎL$TVê@tÚæÙà"¯3_âHc}åó·qqÔjûøÌ³ñ6¼¼JÝë§RYFjÐØ¥õXŒ^S(=ö¡Î…œ’h WîœÎiYæëœåìäóÛô¼ÅÈá7@Þ( 6‚‹¦¤½Wàâ–á‹y\PrpC¹_ FÞ¿Ý6WÿÅ®<8dÁÙæÿÂnÌD:’¬Tøug’A}™°ÀL2¾”‹q14Àϵá‚d -³Þz£ú~è©´f°ui—ÆÎHôQ£%F?Ô¹0ÃSLÆFºI7—Xàëœãì |9\„7žÕ€nSàÀb.î¾8‹;5uÕ(?St§¸øôQ§ê]$]8aÄÛð Èùåè¼ÃÜ!öSgüa8¾çã]͹0#§Þ9ý…ø:ç8;k½Ž£Ž~-J©»5ÿª,FE¸¸ó Æ›„/~"œÁߦ‘T_ý+þ~[.”Ñf2}\\ë·šúÝÀ×|hqñ²C\Ü?_Ïç{¬¸ ú¶™G¹ø-+þ…t æÐ® u d×ÐîâÂüª¯+.¾v˜äëÓÿ×Xq‘ä7¯d$㨥÷[VüòïyE¸ ühYÛ¯·ú7;Ð=|Xq±Ã¤Qo¯^îiÒ÷l™¸5.RAŒ‹¹ÎòðôQs}+k+ ¯×lºƒ ó»¾N´¸øÙ-n\Ähª=ÿ;n¿eÅOµ Ô½ªv{æw}Xqñ¶»X÷£ Óü¼3.KÍðߺpÁm£íêX7.îÔ¾?ؤي—òâ›qÁmýmgûôÞ¸ÑäJ…MzOVüØt Ü·µ¿»¢Åƒ7 _°IïÊŠ?¿ÜÑXŒÚcàâá 6é}Yñ#ÓGqßb\0.Ö¸¸Aøâ*“Nü\ ^ªƒÙ3¯>É “–³Ù÷”‰tÁM„¹lj….ÕBw‹™ÞøËüº?0ã‚qÁbÔŠׇ/.5é¬2¹ö»½¨X´rüŒò¨iW‹S-ºÛl.ì¶U®ÂÌ ¹êR›–·ý2+°âןáb î €6®×ãrâØ¡8áQ0.xv±žÀÅõá‹ q‘j[š¥ƒÄÍqQe%ˆò“g˜›¹6Kõ.º‚eu—¯ú6_f Vü°ôQ úÖØÓ}‚¾Gpþﺗ{ÇÅ ‚qÁ¸XQàâêðÅL ›Óþº¤?7Ç…M½Ì¶ÏÅ… "µýŒ‚ˆQhWÖ ÃÌNßó¿Ì*¬øåQé£nˆ‹À#8ÿ÷]Ë=ЇbÞ£`\\„‹Ýdùú•Ê—‡/f d.ÄEy7\ˆ(ѧ3u^Š "e 넸0£SW‚GÞîˬĊ•>j+²£?õòß?t=.÷(FŠy‚qqIÛMÒ¨·_*íqqøbYQU‚2£MÚJ)£Ô5PËLÊa¢Ã…w˜”ÂÈ»’ˆ(‘2Z¸w0¥²RªnÍ©@- *L­d"Êd¤°·$dÑÝŒ7ØdµjBÈ ë w°ÅEWr‡Šp0˜¸ÎÂ/³+~Pú¨®ÈÃpá»—{cŬGÁ¸`\¬*pqeøbΤÍÔ[Yë2eÅ [2å!€.µÃ…Xi*”ÙC `háþÁ¶H˜þ»ÒaÊNú¥yE#Štµ(ýBj¥)ìÙ„ã… JWzˆ ²UA) “¦N\gá—Y¿=$}Ô¼+ÒVJ¦îAú¤µ¯ù/uA@äÁïÒÚÃõÐõõ(ŽŠcycÅУ`\\‹/~#paÃß77éLõAÙ.Ñ@}hL‰ú´Z‘@¤V޲#yp˜1µÌ*®Q ,<8˜Pµ¢-MPR­HŠ“i@j—ñ:À…½™@^H}z˜BÙ«à¢q³šjº„òÙ_f=Vüö÷—q!lMÒcWÄ>Üà%ç„Dÿð=×C×cÔ£8ªv:æQŒ9C‚qÁ±î5.løâç “.¤”Ò÷±*S޲0–ÒXÙÖLλ؅pu‘-.‚ÃR 5Ž+"Ò(^³²^š6vˆ¾‚±2Sÿ¤4ǸÆ.P÷×<ÇÔ)MàBÉ©Xȹ_fMVüÕô2WÄüâáKÖ#_ ÿã÷:\\qbCbÔ¡X­ŸàÀÅtøâùr“VaéxÓ´ñ«óš¶1íH\X­Éâ"<,‡$ª¡ ‰ FØÁ5Ûƒ]jÒ°ÂŽ3N…j¬mÇňè0-l{ë[ªlNÉ—áâô—Y•? }Ô¬+¢3"jÇ\ 5|ÉþÔ£/ŽÒz€ëÀõ¸Â£u(îZGlŸ¸ø·ZüZàÂ~ü¿ËMºQJ)@)¥º zæÖ~TÖâ””RÊ8„¸0r”ÐÃÃ(" ¤æßáLD™h›Þk•ãÔ–š›šÇ…“ÓcA+„E 3jŠQè^†‹Ó_f]Vüùþ\L¸"ÒStBÒJÓiü—ìO=úâ(­¸\+<ŠQ‡bΣ`\ðìb¢ý}ûÝÏÿø¾Ø¤ÇÑ;c*\q.C\9ÊàbpØHRh*ÑR‰v`Ⴣ©ÈýasÞŸfÆ™Y\¨S+{ûã £GŒàB9©AÌŽ)g}™•YñÝ¥ÓiW$q¿G3âŠH¨#øšŸzôÅ)Zû¸ö]k<ŠQ‡bΣ`\0.&¬ïë×äŸãBxñd ’¶‰.¨R3H#C‹ŠT ,|pp  ¬‹Ì×ošá@Î.Ô,.ŠÓüÜfVF-ÆÅ‚/³6+¾wú¨¥}k@Z u_ˆñ'hâÚw=®ñ(FŠsöß0.–yÞÛ\¬à+þ}¾¡IgÎ÷róy»5!â"ÊNŒò£u…–Ð-ÊýÆ?Øm UÇbTç&aìBM‹Qé¢a>wÞíàuÙ$¢“¨OŠQ§¾Ìê¬øõã—pÑ=œzÄ‘PGn‡Äø‹ã´àÚw=®ñ(F ÆÏ." \\¾˜Y½bí!3–’Ûa³uñ 8‰Î© £¹FF  ÂÁÁÂo¢\€¼Fîïž"jH')×K†y»xþ™îÖÒKc§¿Ìú¬ø¾ ¿gú–Ã>âŠX1*p;œ5òâ(­¸£Î÷(FŠ9‚qÁ¸x¼áÝ/|1“¨¡T‘´¹±”¨R¢T»uèE¸:Ýiáa €ÜÆ;SOB¤ƒƒmÖ«ÑáÔþ¨3J¤ h€ -|\è–®ªîÆ67!í²Ë­BôW¬ƒrã×YøeÖgÅ÷M5ƒ ;éJÇ\ uäv˜‘}ðb€‹ð½®×ã bÔ¡˜ó(—áâmÛ´øüZÉïüs¹I5³— v–¡”¥ü´^N„nÃuxUÆë“èÖ>úËd‚ƒ oë3×ÖvyV @iç3&ÈÐùî]’Ð@®<™Ù^×µ6\HÛµ¢†ä‚ë,û2ë³â—{nïžqEZ Î(­Æ\ uäv`ðb€ëð½®×cÜ£8ÚÕ=æQŒ:‚qq}ûÙvßÝqqMøb6k¨,¡eÖ/C ëƒó¨ÐŒÎ̓è5fåR& FØàà¬Öt›¥Â\»¶ÃEÒ˜]À‰¿…·±·Vx;t%TVê@tr˜×6+öyyœt!.N™Zñ=EÔS®HÙñ> ­<†¯óÂÃß?|/ÄuèzŒzGÛôÆ<ŠQ‡bàQ0.n ’l«\\¾8•d:é„"Eê;Œ™_ŸÆ/%ãæ±ÿëêË—#¸fzH»K™3„ÙZND$þÀÂ+tÓz&Ò‘¨Â¯ˆ“ Jß ªéÅ` ÖØu–™5ZñÓGÍö­FJŒ¹"ò¾G¼8Àuð^ˆëÐõõ(&pzcÅУ`\Ü_[ÆÅ늤¶3Ã3&­Œ_•é0[߃›ú]×-??=r+¾ß’¾®HêóÞ#m6 _çx/á:<ÁÃuèzŒz#0åQŒ9«ÝÔ1.^¶ëþ\ Ï _̘t}H¨K9ûKM„Aˆ·â²ôÚ¬ønJêêjußÇõ¸ƒG±w\lyiÔz„/þÌÚÁP¥ýVÿæô"¿*WgÅ÷Jµ:\ÜÅõ(V[}5b\lwŸÞŠ„/æL:k+ ¯ÙwJòß+' ïªõYñ¦Ç«ÃÅ]\|¥‘‹¨qñ½Y\¼®nðÇëíLzû2ñk7‘Š}Xñûç>pq×C®µ–‹Q¸¸¹õÿ!nlÅ÷IµÂ¾u{×#ÜÑîÐ9p±Êã‚­ø^sdî[ÜÑXŒZuàâÜÛb“f+6ÁÅ7Æw4£v¸8sÒÃ&ÍVl<ŒÛ§â¾ÅíR\·‡‹×Uge_6õY`Ò™H½6Ö±|åbV˜$Ò…Ì}€KÔ§6ôóúx|)[ñ/Ì–ÜÑ.lÏÛ‹u¯7pqFøbI¥PM«–UGkÐϨK™ÚŠ|¦=™·­OHZ¹ ja’iÕã(ÙŠ—9u¯ÃEVHùCŸ àŸçÂÐõ®Æú}àb{K£ÞVŸ×dÉì熸H5®ÁE ”• ʃjÜ‹pa‹&×$°õ8ë®x[ñ èõA¸Hà?‡Y ';æ¶KOtk]õû{ÁÅÖöéݵÙãÂ3ÏÎÅÅÎÀ…­pÔU;Xbò‚ˆHÔöÔ @f" íjìeÐ [ñçÌN9ÿu$.Áŵ®Æú} ÆEœí;‚Øý-z¦žòÃqq ¢Dãp&.ˆ”©ì:¬§œê¾¬d+^¨|ÞG®ÐŒ¨FèÒå_€‹«]õû,FEÙ~¢ø>§Ã³¸ÈÚJÉÔY_ÒVJÉÔ7Lÿ¥¢j™…/qá¿GD©¬”ª["’¨eAt÷Y­š îQ=eᆠ=†‹®ä+k2ù•[ñíÒGÍõ-Œ‰@'G‹ë]õû<»ˆ±=G’ýdøbΤ…-?i,ɔdžn{‹ ^R¾8ÀEðž-—iôWCSY @¢¬¯·gÓG&_šªšG¸ Ü‘î·‹vÄdÅßowÇ…u„,HH)Œç  @9Lô¸È¤çbL»0ó®FèkL¸ë÷51¶õ.†/fp‘i >([ 9Ñ@}h4zy8|)mV$á‹!.Â÷HU+Ú$Њ´ó:%€¼:çGq!„²'㢱Ȫ<ÄÕ[ñ­ÒL÷-ëH(J4tB”äÐ  «Þèpq(ÍÑò” 3ïj„gÂÕX¿¯Áb.~1|ñÇ®?”RRú.Xefã­±¤Æªºfò.¡†/YS _ q18¡²n›†""@eNº–&ÔèWê”Æq!„êlÅq#^LãÂ:ŠèTD5 ̳K­e‘i@j[ÔqÎ…™w5Fq1t5Öïkì/Û¡ÅODìûúZ`Òª_ÒÛ”¶Vb¼,m5åHíð¾dM-|1ÄÅð„ö`מt¸n\‘֌ӱroK•MâÂÑ'g+^΋חÛábÔ1OÛ@¼ÄÁL HPX\(ã´$%t2ïÂÜÀÕX¿¯±\|n' ÈsT•dçÃƤ¥””Rª›À'Îr;š+)¥” p°¾dM-|1ÄÅà"ÊDÛ”èq!G'-7’ñ˜¨5íV!Šf£ÐËV|F¿‰Ø:ãŠø¸HräÖ‘0ÏÎÈQN…j¬32íÂÜÀÕX¿¯±\l(iÔ¿¨&Jóá‹ÁÀYŽ„ ¤KóÚà%sÂàÅèȽqn¢á{tÓ dúkFv8Æ…rô9cA/[ñÓÒGM»".(…å½}LFŽ2>u.ÚS.ÌÕ®Æú}Ýàâ‡+ _Lã¢3ÔŠ(*i›5ôÁKÆÔ/Ãôð„@Y™'F5#¸(Nâ‚4ªÙ•QŒ‹³;Í÷­p1µ¶ÇÕnOœp ) ÉóA:mtÖ…¹«±~_c/¸xÛJ¬û'º/2·SwƤo–ÛÑÜîrM:‹ _êÄ(ÿÅ¡œàvÔª1*D€…#\Èn¼N‡f+^ÚÍ_†‹´Ûœçž] ¤C\í¼ sWcý¾Æ^p±•¥Qq.N†/fLZuƬˆ(·nÛzø’µåðÅÐÒŽÞ+ìòZ…ãzzœï¯m—Èq‘énÅ|Á±‹³Œ×á")‘çÆspÏ.Jƒ‹NPJ¬32çÂ\íj¬ßר .6R­û_„+¼fÂ3‰Z Î(­Œ%@•¥Æ“P׬3¾è4b!D:|/Ñf‹êN½Õ-º¥D‹çvu›kK»’²ÃE+„‡½wY¯(Q\,Vüùù\4ƒ•Qn-¬­µõñ æ]˜«]õû»ÁÅßMÐâ;ʈýtøb. \  ¬Á*ei#ƒÆÐƒ—Ìæ«6|Q„Ëb on!m3õHh ×@žL,¤ë6XHÛµ¢Ÿ×ð6½óûúçÍp1’3Jôˆ†¨LFK‡ Ô%Ò¬‚šsaæ]£]Ýc®Æú} £8pñ‹á‹Ù攽èÚOËã¿DT˜´þ‹\„'dµ Û88\Ôv¦"¡²P¢“¸Ð¶ÎÆyÝU3è–䳟%bþÜ G­ÃE¢‘'d7w{)›º}Ü%¥]œkÆ…™w5޶é¹ë÷5XŒâÀÅ/†/NÔ»H…Ÿ7T]æ÷Zÿ’q"³ðŮ܌?!=¤î4S’FY÷CŸTL“A…›°š^ C¬j£UDV|]ú¨E¸¨,!@5:Hã$§\˜yWc¡«±~_ƒÅ(\übøbu2Õ=\¹|r>[ñ|‡».„HŒOÐûBµzýRw‡t 3ïj„¾Æ„«±~_ƒÅ(\übøbu¸¡„p“V¬J|ŽÉŠ_þ=ßñ¸ëò5¸øÅðÅêpAõím>_•š•_“>êá}ë®ÆÊ|Ýàâ+ö, Ï‘Ëi£á‹õá"Éo]^@®+T—_‘äñ}ë®ÆÊ|Ýà"ú¤Qÿ"Ï©;¾X.(7ÆE*ØŠ¯áE<¸¸½«±6_ƒqKà"ú¤Wcá‹?Ä­x¾Û|Gƒ‹Û»kó5öƒ‹¸ƒ?Xüyüþp{x‹®Û¼Fƒ n[ÁEܱî—Oh¯÷á½ý¼¿¾ÿ}ÿù_ÿ·ÿvw—÷ÿ?ÞîüÄÿ»Ûɹ%/.s•Œ‹ââïó& ÿ^ñ——¯÷××ï¯ç§§§§¯×ÿóíÞ_äãÿúŸÿÞYüü|ÝÆS¿ÁOÁ¸`\—é‚ñëéýùéóõ®‹Õ>¾žž>X‘2¿÷þã‚q±G\l#paÚw¦¾¾<$ßË÷Óó·aÓ=È—§§'V¤Ì~þ/͸`\ìQŒú»¥ãžÛG^^¿žžžžà¼(½Ü5€aó±"e\¦³yq).²BÊÿƒÍq‰ŸCÊKö4²–v|[]!³é7à óI™+ý|R™2.xvqtã?[²û;nN·ÎþÜñ—w½kãÛ|V¤žžž^Îö3.ÄE ÿ9Ìë'Ð'ãÀÜÖ¹‰Œ€Š¥ ìs˜W®FpXFE‰îFKÆãbèc~oËîï¾°¡„—×<“O3Ãè?õ>hu_…©ÙšŒ7ÅE ”ÕÁE‰Kp‘Cœ‹ [bã¸ê–-ùJµ-°Á¸¸.Þâ´’[³ûû„/ŒõôôôùWüó£÷ýïÀø~îÀÁŠÔ¹é£æªéÍ4 QÐÕ¸]=¯%¸DD¢¶Õø‚ð‰(4%2è„qq/0Îýßíù•÷_ñ*z3.nlyÚÚöv¸½éLïÙÓiž_rÿÇcù½È@éÚ½"õy†Ÿ  l‹(H fŽp*¢f¸N­e‘i@j˜1;Ó@}P0£{¢úÐh;È;KÈ ©Ãa~ B({Ò1.;WY¹7.b\õw«òÃ-Ã?þ’™‡,hx³]éõ3™ÁGì]‘úZ(úc÷;H))}É璈㵽Qâ`¦î@‹ ebI Uæ¯ÖŒîL›øCæ¢äÒ,k²/ŸX¥SÇ…’ÙeXŒ‹› ÎQîg«VÃðÅ{ ×=Äûva°·_Ð7÷®H-OõÇê@ýŽ…i\$æÐ.’¹63 3\9Êâ©P Úþe˜cÏ¢H‰„›ÝH;c8xƒÿôBÚ*›ÄEêþ•3.îª DÔ¶¸°S‚}·—¿¿€Ôçßô@v®H-΂opÑ(¥ ”RÍ4.¼¿<\P ØeHf¸6r”áDêðh)q5ö"JJ)eº uÿIÉøò+ûA­Bµýè11 ýÁŒ £ÌHøoËV›ê€ÿ~U›ù¥ðûV¤^êgÄ.Ô.¨v›âìp-iqÑà²ÿKB…›ê$Qí&6x4½·¿la"ǸP]ìBLÏR;£þn{L¸Å×{ÿåù×Ëëï|¥é£àBÀEÚmÎss ¤C\m?ר¡ˆR ’¶ 7ãqQœÄiTó+£,FuCáÏÖ­>J!jàé¾üÒïY‘Z˜>êz\$%òÜäâps ”62î%£C¹¿\¼ÜfêH¼½•ÎÆ.D€…#\È~j"º 7ãbßbÔ–·ù†wN¾ì;|þ^wÞ¯"µ,}Ô,.´ègqÑ VF¹µ°f¸ÖÝièÈC©¹dnÇñÖ^ aºžæ{.ØíxC\dºßœWpìâ~öSÒ¨—/›·úëæO_¯kø…~Qáܱ"µ(}ÔLΨDI9 3^Wݾ‹.¡Á…êŒiVAµ@QZ™K@•¥FOJ½…´º¥D†ûô†»º[!„ÒnÚèpÑ !Ä¡'dÕkNJ9.~bš¾ÿ}ÛÕ_¾xyý\ÅWøUd¿ŠÔ’t 3i ä Ðs¸H4ò„ìæn/gS·» ´Ë[(Ý%I(K·®JÛ¬€È5'3ÛôºÖŽg¤-¼<·¼MïnÆ‘m}}îÁè/_¼­§Ï¯¿úñ»U¤>Nÿîs ÌMŽŽfveTe qªÑØAÒ˜´vèo4 D1º>ØY@å>!«u :‰ ]‰±zyÝ×fê¶ÿ1.6¥3ßÃyÚ„Ñ_¾ø|]T÷ËOj·ŠÔéôQóõ.ÒB¸<å.[¹ù¿‰ù§pIÌ…”xuìüRw¼N…Ÿú\©Ç€¬û„£lêÂÏ–ž Šé…Õô’%µ57ñe£1¬³+ÁDÛ. _t¥-V!qþ¶²WEêóT'XS­nuÕ(ŸÜìǸ¸ÁØ˾¾íÆè/_¼­+®óûÁ*RïŸñàB„ÑŠÛ´bÝÕWcÇE, Ï>v¦pvøbEBÔÓÓ“WñŽ©GÛóg4¸ úÓ‹|Í‘ ÆÅƒÚ^VK9/|±*!ÊŒÕkØ ³OEj~¾*\$ùÍ+ÉU×Òc1êAþönö©œ¾x^á°¸Žä2»T¤feÉUá‚2qk\¤‚»Ÿ]ì(pam~ù8÷³F”~®#[Ë©ÙôQˆãbã¸ØSàÂÙüÒ#¿ß¹_±"µt*θ`\l]ŒÚWàÂs˾óóZGÃïÕÈ@ûS¤f’åüáö»-v\¬ß–ö¸°fÉŒêcµY´ÞÖã‡ìO‘zÛhrµçÿû¿}>qûÅö¹þ¹úî‹¿öûŠ3ô®©’Êî©ÎÇ?ÿÇïWæÅ¯ZÒê ék§ää¤j¥-æL{U7·7Eêc“¼ø~úûôõýÄí÷úÕÚ í1p±è›ÿrÕ“´[—YïM‘úÜ¢Ù¼>}½mó›ÅÒžWnEû \,˜W½¯ÝËZÙFó½)RŸÛ›”¿|?=?=ý¼îwHøýiúÊ·?›é/ÿ²~ ÷ãku]}WŠÔ÷æxññcBboÿ˜¿Ö«âu°7ß&§Vo1TŠú·º;Ú—"µ¹¨ðûóÓÓ×ÛÓÓÓÛßç'n¿Òþ®ùæÞv®SN|ÿ¯(¦ã_+DÚ®©ïÍÌ_Ÿ\6²ÙëÜî‰ì½ëÝ´±ÙÕK$nãË*Y¿'Ejcƒêk÷æÅ¯¹ kžùpŸ8_¼Eó«|¯ö;R¤¶5¨¾}====}Ú¯Ä0~gH^±kýÅçh‚õϺŸ•Zô~©—--"2âf7ge\üÊä|½ÞÄ_~:ÃðÅúJ[DéŠìF‘zÞÐ"¢×àÜ~ÇrÖÚŸ8paªOŸ Q© 﫽ÛÝ(RZtj9ñùÃcÂ/ŽFk˜¿ràbøC|F¦-<¯80¶Eêm+I3܆âž^ü¦Ù¬Ôj>¿øÙ ¦Y¯Ñý$«¶ì(R?^»d¨Œ ÆÅ‘SÄ¢ÿ-þ>=­¸´ÅÜPµêxäN©ÏmÌ/:¶³õ›cÑ*M†~ûúZiÕ“må«ö¡Hm,}«Q¿:'_ãMqàbðs|Çé¯]ïÙ…"µ±ôQŒ ÆEè}ñsñÛùïÿ‡H„Õ !»P¤^7¥ß°õ‹m}™à8p1hÿ¢-wóů»ýùÅ–fë¬FñìÂ︟Ðw¼ó­(<Áí+RÛJ²Y\üá¶Ñö@xþ<=E͉Ãܾ"õòoCDܬõ‡¸m²= ®Æj¬S®H÷Í+R[JµY5ŠqÁ¸¸R'qÿŠ4 ó‹Š¶uEjK5è¾7úŒŒ‹«Ü(o d¤á‹hEn]‘zû·™¯ò³Q5ŠqÁ¸¸ÂÀÃŒ‚q†/>ãQy6®H½mÇ)ߨµE\$BˆlñÑÙAÊ"e\\І5Vã _D%4o[‘úÜÌ(ûºÍµ“[Ä… —œjPŒ‹ë„(ë¾²i³"u /¶2¿øÙfq¤ÝãBçθ˜¢¬¹EhqÝô¦©Í¤Ú¦µ{\ä@~8C»b\8Ý`Ô'2|[UÄ-+R[)q½M5j÷¸Ps¨û|!jÂ#2|ñ㶬H½ncî´M5j¸Hd¥*ÙÍ D¥”’mBD… ¤d\\/D¹·â3‚wUmX‘ÚH²çMªQ{À…Ð}0J˜V&.r0.n DÙ7#t|cT6«Hm$}Ô&Õ¨à"ƒk‚ˆ(íþlµ¥-^¹¨+R×ñb #í&ըࢠ“"·ËeK OIh@¥@-ãbù(uR‰ÐÚÿFi¼[U¤¶‘ñy‹jÔp $¢@JD$ŠÔ, Íí»Œ\Ü j¬>Ç7ø¾G*ìlT‘ÚDú¨-ªQÛÇEj‚Ù²vjQ*•vãâ–B”EJt*És¬ÛÃ6ªH½ýÿ;lQÚ>.úÖ59¼ˆãâ,Uy™úñHïð´MEj õ*7¨Fí¥2­ ¢ª¶f\\02-•>¢bÞM¼IEj飾·§FíCŒjÂ}y*1Ô`\œ7,-Wlâ _Ä<:mR‘úŒþ;½mOÚA¨[› ¶DYeÝ›Iθ8Sˆú{NÿèÂßQ{è[T¤¾¢mÿnî™lè〖Ë;«]Šc÷¢ìðÙøõ¹3»AE*úôQß›{$;ÀERÚꄨßÔ c\,võÎkb _Äî nP‘Š=}T4u}þê§te–Ñ& \Hƒq±DˆºÀÑ‹-|ñ}½Ìí)R±§ÚœµÕjz]³ñîÂ߸}8dD©)Uyo§¸˜É(8Ó" _¼l`ÝãÖ©ØÓGmNâZÝm7ÄÅ×…ûS# _laîÖ©ÈÓlNb\0.NYìÅǸÂÛ°í)R‘§ÜšŸ`\ÜAˆ²¾î?¶mV¤®ìQóbkjã‚q1×>¯’h~¢JÅôµ¿|[ŠTÔé@¶¦F1.3RÀë•Ýý;¦åF/›Iò³)Eê#æÇ²15ŠqÁ¸¸‹åÌ%¦Ùø†RNoI‘Š9}ÔÆÔ(ÆãbÒLo°,å%¦ðÅdž’ülI‘úŒWÒÙ˜õ‡Û&Û pñ}“ñæ#¦ðŦ¤ƒ )Rïñr|[jÏ.xv1îÞj°y(|ñþ¶)ãÞŽ"oú¨¯M©QŒ ÆÅXû¹Ýþ¨ˆÂÏ«h³E*Út Ï›Ú7ɸ`\ÜMˆ2-¦ðÅæ  mF‘ú+/6¥FÝ.]“KÃdò2õYœÇ#ÀÅy¥-NYñ„/6X_y#ŠT´é£¾¶$pÞ}2Ø*!"²5&ú—•à‘|í¸8·´Åé+žðÅë+oD‘Š5}Ôó÷†úÒÝp2ÃPóP¾n\¼ß¾Ç¾øÞ`Ùë(R±¦Ú’u{\""Q»jÊ{9…ZËWŒ‹ QÎÒÙdEêÚiú¨-©Qw‘B~„ "J5tƃùjqñöï.ãJ<á‹íÕWÞŽ"õ%Ë·äŒãBH‘´•j‘¨U“Ú8v[)%ÓðP)‹Q\Hè1\0Ón«ÄÅ×½ò`D¾ˆ¿¨Þ†©Ÿ(CKò@Æq!ј×ÂT¶®ˆˆÒÜT¼Å$ „#\”¨FqAyp·áâ去¡¢ _l0ؽE*ÊôQR£¦pÔE­è¦(¡‰(Ñ@}h´7òOâBÆqÑ@ó`¾J\¼Ýsm{4ÊM»7£H}F¸ÒyCjÔ$.j"j`@ˆ®6A‰“+£tJã¸ï¾X#.¾î›5–<ÔoÛ*_º5Eê;B^lGšÄEBD»ì5‡$Ò(‰ˆ¨Ò% i«l)æ«ÃÅËÝÓò|}²q³"u½X/¶£F\dRJ)eæ év”/Ì*'I()¥”‘™&qÑ !DQ:›£Àƒùêpñö€$ ‘äýÙl°{#ŠÔwtƒïvÔ¨?þ¤À›”þ(¯ ƒvrÁBÚ(Fq¡8v±>\|>¢2P$ûr_¶¹õb;ŠT|é@6³|b jˆ‹¨¤mb.H£â•Qqàâê«K§0q˜Í†ŠêmR‘Š/}ÔçVÔ¨©ØÅ.¨Kß‘,Ú¦g°pŒ 979áö¸x{X¶Ï8Â_O[oq+R/±}3Uà—ã"·+šÚÁJÚ \˜íxG¸È44/ŒZ.~¨Å¾ø÷´ýµ"õ[:ýᢪ”(ÕfÓÞä®îV!„4û3z\´Bqh`£çÜÖ‚‹ïGú™q„/¾¶UToƒŠÔ[dDßÊò‰å¸  ,1Èùt´M¯k-f¤eZ¬ Ïv2£_¼lwg÷V©ÈÒGm¥G ’ ëpí.t%hyÍùׄ‹Ÿ‡{ûQ„/¾7ìŽ^‘Š,}Ô¦q‘uEð"¢ÔUÇÅÑ;[/϶$,¦7¨¦ÇA‹•áâû|´Â,ª·9Eê3ªùÅFÔ(®Õ½_\Ü¥´Å‚OÀußöÎîm(RQ¥ÚˆŸØ-.>þýŽcCøâ}ÁîÈ©¨Ò0.¸ÅŒ‹÷_›ÌG¾xÞúÎîM(R¯)<ÛP£ûÄÅïQÎÌ×ï»ïcmTìŠTD飶¡F1.v‰‹·_Í£Aøb?Á˜Ò0.¸EŠ‹¯_΢ð¶þPò~‚Ý1+R/ÿ¢¹éM¨QŒ‹ýáâå÷c„Ÿ_«×9¶[ToKŠT<é£6¡F1.v‡‹Çeœ›š¯]EØW°;^Eê-šôQ[èPŒ‹½áâsþØúÃû vÇ«HE“>êgjãb_¸xTi‹ÓV¾öáxãEõ¶£HE“>jãbW¸X…e‡ã¯u[ÆË.§1*RŸ‘<© ”Ýb\ì Ÿkê±k_¼î.Ø«"õÇŒh‹³o€‹L¤SoÙÔ‚S)9éàCq±!ÊÞÎÊÃo_O;mÑ)RqTiÜ€u\ÈéâÛ}‹Ê€á¨þ…<²?ÏkÛÓ´öðÅî¶^Ä«HÅ‘>*~5êQ¸@™Œâ¢«ÎíθøYŸ3¿òðÅçÇÓ~[dŠÔk Ï*~5êî¸DD¢š‚ˆQhSuÛ½qñ½F}aÝዽ»£T¤¢¨}‡z .ˆòc\QªÅ\¹ÝÏëV¾xÝIQ½-(RQ¤Š¾CãBÈ"‘Jµ©ýK¤•j3¢¤­”’]d;k+%ÓRc¸Ð£¸ açÜŸµËë_||=í»Å¤H½D°½;z5jªVwž°B‘D­(5/êÖLè*ÀEP»»ÇB‰j”OON¸ÝïëÝÈ´îðÅŽƒÝñ)R1”iŒ]šÂEnFu  T9%¨¶c}¦ú €“¸B‡ \4fÞÁí^¸øÕÒqkÎ_;*ª¿"Aú¨ØÕ¨I\TfN Í_ØM€ÚÆ!*uh±de”NiàÝwÄÅǺs<¯Ú'Üw°;:EjýyñcW£&q‘Y@dý€®QQ¤D¤!O©IÞ‚Ù*›ÆEÊ£ûÝpñþÍ6¾_opgŠÔúËÀGî\dRJ)e6\ì$€‘ÜÔ@J)eˆ7ä7³¸h…¢¨a¦"£bxp¿.Ö-D™¶æ{+ª»"µúôQßqûüY€8Z Èî/oô†üE i  Ç…âØÅÝp±¢Œ‚s&´â±h÷ÁîÈ©µ§z‹Ûÿ8‰‹¢û+*i› JÝñõ\FÅ+£Œ‹¯H´”‡(ß÷šg0VEjíé£âö?¦b&J‘i.a‡ËÿtX»Žq!aC ÜnŒ‹—×X<™çõÚЋêÅ­H­¼×Ç]Ôw2Ô-ìÿ½¹FnCÞ­yWu“ޏ°»ñŽp‘ih^u\Ä!D™ö³^ϕըØ©u§‰;Ïñ$.JA‰Ôh<\@•¥FW¢¨3J+Ìîên…BH»Y£ÇE+„‡&¦Áíæ¸øSPm½á‹OvǦH­ÛOŠÚÿ˜Þ¦‡RÛÁ½‹d(e —å©Pzv›^×Úp!mטwßÕA[oø‚·^ĦH­;}TÔjÔ.T @›*ýÒ' º>¸5°PB.À…®á"¯9¿ ãâiÍá‹ovǦH­zëgÔjÔ$.(98*øåòDì©KŰšžðÿJÅôÕô8hÁ¸pmµá‹ç÷'n‘)R«N7³5 nŒ‹‡zñlÞ¬HÝÊ…_1/bž®2.+ikµðÏŸ'n±)R+N³Ÿ`\¬EäX©ÏycT¤>ÖûÔ"ž®2.kik _pžÁ©õ¦ŠXúÃ+ãb5v´Î(îU»U¤V[y+âþĸ`\¬§­4|ÁÁî(©÷µî°Œ·?1.+þ­ò¶¾>ž¸E¨H­5}ÔW´jã‚q±¢ö³ÊŒ~ìŽT‘Ziú¨x·ò0.«}V¹j•ƒÝ‘*R+M­Ÿ`\¬Ë’Öè¬~pžÁ8©•¦úz‹ôQ3.+3pvY‘º¡Ž¸Æ‰a´UTî‹ ›ªOæŒâ¬QŒ‹c_u¦ôþöÄ-JEêy•«íbu?î‹‹>Qyå*ð ˜+Á#;ã"šÖ82"U¤V™>*V5êQ¸@™Œâ¢+èÊqá\¯*Œ‹h©·NWcU£îŽ AD$jS{uP|5…¶u“¸1.:½y…á‹vG«Hý¬õ‘ªQÁ‘B~Œ "Jµ«ÍÇqá„ú^<½ˆW‘Zaú¨HÕ¨ \$­RJšúHÂVß¶ÿOµrõôüÛ6¬Õm± ¡GqAÂÎ;¸1.z]c}á .ª±"µ¾‚둪Qã¸Hµ P›‘^Qÿÿ̼¥Åàß® ‹¯Ú÷Js­c\PÎÙo럫sQ½˜©ïOîá÷ÃE…\Š¢C\$% m>ü÷,.„PæRc¸h̼ƒÛãpñ‡Û&+RÓZâÚx§5Ž N4ª!. 5ã}üûÔÊ(Ò.$À»/Œ þyxB¹3Eê{eÃsœjÔøÈ‘£Ìˆ(£#1JÙ…¯R‚Ÿ\H[eÓ¸HÙÔܶ£?®Q‘Z[:×xq‘I)¥”Y/ys c\ø3‰éY…‡‹V!ŠfÔ¨¶tÆ·M…«Ö§H½üë§dÝ©â"õòÿ= {HaØ2‚ ű ÆÅ¹-ã2k_ݰ:EÊKõºŠÛÙ.(k+À¬qu¸PP”yeçà‚L„WF1.nѤí­Y}F™³f\lN‘êÓG­"Î/.Ž-‹ˆ’"¸ @QÛÈàß'qa¨pŒ é®Âqq&.©±˜g̸آ"õæÒ¬"!ýçÏFpq°£y ¤Ý»PD¥ï üû.ìn¼#\dšUÆÅE¸Àrœu0ãb“ŠT—>ŠÕ¨[Î.TB.sGH»É‚¨êŒHB'Á¿{Cìên…BHmlµÇE+„‡§ãåÜŒ‹8q±6EÊ¥YÅH½\¥²Cy¢\Šˆ¥‚äÿÛµá6½®µD£i™Œ‹ËpQÔj)ˆY©Ê-ìRY­AD¢RJÉ6!ꦴ8šq±yEêÓÌ/Þ×Ô>5ŠqÁ¸ˆ)%¥¬­¤$}i)•J@‡ S¨+ƒ™ŒÈí,˜ZwÎú_W¤¾\–Á—oOŸ«˜í0.¸1.‹‹áò;‰.Çq“»7Èc‹™L”€f\l[‘zþúkˆñòïyåK¢S£Œ‹ à¢T¦ö=K ¨ÚÚŸ]¸¢_¤„Õ͸ؾ"e‰ñòúÂjã‚ÛNŨfðžù•jt¸Pv6q°Ó ÆÅÎÖH=ýý~{û÷?­#ŒÂ¸àƸx$.\8B¢¬2ÂD³“ÜÇE I(s›Pû[#õüõ÷ÿù_V‘?üç‡qÁqñ0\ä•”´là-¤%§7é¢P}ì"¯dªè ÇGo\š–ÛõÅ{Œ‹hqATRºÜIÙÇÀ3w0¥ÚeÈv… 6 nWÏN¹1.âÅEa' Ÿh¦@ÒhÊ7ÜÁ©ÙàÝ$ĸàÆqÁm'Õô2q09gÒBøïuQjrÏtSz?šqÁ‡ îEüìù¡ïÛÜù™s{.2?iàbï/域qÁqñk-é“›iNÇät§7Â…¼ ˜öÈ9Y%ø‰0.¸1.Óºb ˜¯ð¼ýÊYÑá"ÕüDÜÄ…-¾Æ¸x.n#F ~"Œ nŒ‹Ç⬭f\<7{xüDÜÄ…É'ɸ¸/.’V)%M1K! Êd×2¢¤­”’áüAJ¿è¶‘"i+UµdJ%×’+­2.¸1.ˆ ˆAv"ÊêJô¸j¾sÿYÚ‹ÜÎ׊ÈÄ!¼$ÑÂÖîvÅŽm jt›Ø…DS†¥’áëÀ· 6ÆÅ(.JaväšÂšïE©6KÏÈÌ3ª”(Õvfgìê>ƹt_ÜÜÕÃ:íònñ æ;÷Ÿ¥½ÈÈx–ÁŠ*/2¿»P–èB4±M/ÄÅN#HŒ nŒ‹_Ä…èVå ê´»¸†£hPóûÏâ^$u¿^BÙnV'@×Á2Ú“¸(ö. 2.¸1.~Ôt‹8Ã:íD¢P&n™OPóûÏò^”ˆÃÁN2‘R*º–‰â(Ay²<iŸÅÜý¾¤7Æ7ÆÅÍZÒUaOüjz^v"¢ìõÕô¼šïܸñ³ç‡Î¸àÆqÁqÁœŸ97Æ·Ûá‚'0çÆqÁgüÈù™sc\pc\pc\pc\pc\pc\pc\pc\pc\p‹Üö÷œ: 4·h…̈2eƒü=sf“àvÍR v:xv1ÖìöülùÞÖ‡ ("Jm Fn<»àÆb·u>ûÏÏÁ#ÿx]€‹T/N |êÐܼ]sÆ0Æ7Æ·?û·×𾾿ü ´§Q\œQžäÄ¡.sX¶×ÌmŒ n»ÀEVHùÓR¶ËeÄéì¤æt™Y(=¡ŸPºo¯¯oõÙ¿ü{ñžöËûëçà¡»4l÷ÂE¢]E÷jºV/·Èp‘ø äz‹t/ v ö‡‹þs:ù»ó8-Of‰ïÇ•гþ ¥ûæúúfŸý¿g/ÜùúúqôÐÇqQÔ@-3¢¤­”rìR¤•j3"Je¥TÝ’w(eräКÜCçéÅVpÑ×µ®’ÐɰMq-´½á¢Ê꿞ƅ§ÏÀÅIZœRºo®¯oõÙ[><===ýü}yèã¸p%cRSåD·ö×ÀÁ¦°P&Ý¡‡²/Šìª\©^J¸@Öqa‹lqÑUhæ¶U\ el ™Õ˜, Âÿ-ÀÅZœPºo­¯oõÙ~9êëßçøCÇEÚ­H Ô‡FÛ_O(UN$€ªm Hwh¦u¨mI­þP +‹ÂjÔ–p!ˆL½ˆæÈ"Qh®…¶u\ GÏÒgpáÄéŸXB‹c ãbѳwaîçoo–Ñ?ôBJ))ƒÂ¹þoÛXó7UxIºW•…¹†r‡*S\1)Í[îÐ H‡³Qn›Á…+Ð|d‘~µfn;À…”@- ! "!EÒVªrCÖVJ¦®JžöƒVzQê°€·éL>-.T‹®OH(Oœ–Ã%6c}¬LˆJ»${¨Œ7ÐD•cɬtáwÎÁe.Ð×·ûì_>>>Þßßÿ~›äK6Û8(ˆ  J‰RmžŠûÁÚˆz6zUY#£DZßÔšz>fÍIiÖV„‡‹Ò·5yd–S¦Ü'h(Fq¡8vÁ¸è¼I'ó4.j9»9Çv¦TÁs¨ŒgÀÁ[µƒË\ ¯ïîÙ/I`-)ei=Èþk€¼Q@t‡–”v2Uw¨î{DÎÛôÖ‡ 5ÄÅÐ,Oà‚Œ+Á+£ã¸èÖBå1ª$*ç6ç¸ÎÔÚ1e ŒS‰ºÏ04«tÄ(ÿ2è댋\;gÔõ‚ß63kiÚ8¸C“Ƭ®„9ênº˜±˜ýÛ¸8j#¸82˸Èí˜0À…;Œ å`¢~X£›y¨Ó}L™9PÆ©€®úXyq2va§9áe.Ð×ÁSrY_l¾QQtƒ¼Ÿ&=¤Þ1ö‡ãCEŒâMÝqàbh–ó¸°Ûñ†¸È4'”Ü8.ŽsFã¢êŒÒ v N½÷B9jlW·­Wê+ã”hL)Ýc»º­h>¸Ìú:ãâžM9/3ßè†È­ábh–c&èBÝBên½‰÷ò¡ç“Ü:.Ž}øq\P  ´]VÛúïrÔÈ®n"":5o¨¯{—¹D_g\ܳ%¹Nˆ$G>Wˆ kÎ2SW/0ËLÅôÂjz´ˆ³ÿLoì½áÈ¡n6ÔžZÊJ÷VpA™HŒÁmm¸àÆÍë?o¿îŽ Ñeõ¸î2Â_ŠÁJ÷vpÁqÁ-ŽþóóïçÞ½¨¾ÉôBYéf\pc\p{tÿùúûvß^dÄéà¢ê.ÃJ7ã‚ã‚ÛãûÏËûëó]{Qv“à– VºÜÜ~£ÿÕ-š Q ?Sú¹'ŸºßÛFNÓZÆü&ŸJÙÓܤ©v%MjmTÙëôîÐCi^’á¡Êzõ6~cŽæÕѽ‹ð6.¸ dVJ€Â~]é$ò¼/ß0´„²€Ñî¥ùVK‹W‡7<~ñð·ó¾-%~]‰+qq‘½OEá¹mHè·íý:.ºeî—N=÷äe¸¸Yü¶¾\‡^Õì"Gm§ƒÙEÚ­H Ô‡F{ãk©r"T­hK@ºC3 ¨Cíêât‡º˜ò€º0…Hš¢ìÃÏ㸰—¦ð6.¸ U£ ”v ÷O7‡æE‘ Uæ=©ƒš?£¸BÙ/|Œ‹ÆÿºÝØÞð,.€²-J è~˜PZ.Î02n± .UíLñÕÇÌ3E7©ó?3¬çν± Ÿ­¿ñ¢ØÅH¤;ø©©µz¼tþue¿¿†r‡*#Í'¥yËšµy¥YÔ˜uJ ¦ÃÁ oã‚»Y5ªBk¯œNÒÎuÊ‘AÝÞ¶û&ú‹+”Nô8ik`mpÃ3c²4XKòþÛ†ëuˆQŒ‹Íáâééçßô¢`ùý£š[;rQDtq×¼õrŸjõ)—à"…&"5æØšïo‹šSaýnÀíÁβº¡ËÉ)M7HÜ@ y ìªÒ|úW XxÞ…Q£ s3ÿt’¶4Wã•èòú™û'q*›ÄE:ò=ƒžÇEbâ"¿.nìÿqÛ.žžþÌãâ(ÀFD”jeª2öÇa8t$š9Uö—tA;ýL?(l PÈÂoúoY¥e&rzuü6 1F‹‹Â[O§j@I)¥lÌx)½’‰¶)Ñ ]i>6ê¿;T:gÝ yvD)ÌÈ· ám\p £F(½­?½sÓÅÈ”Ç]ff:$€V!ŠÚÖ¸£ŽÝ±á Ïã¢ÿ£»®ð.z .’V)%Î~2Ùµ¡Œ[ÖtžÛ¾pq`³R+-¼ˆã0:Í„*½Kº ˆŽ}f ª†« JȨs‘Óëã·aˆ1ÚØ…‚’Rò(vaÆ#“™ ~á"w›•ûC…¸üCkwŠ}²¥ï€.ÄÅà6.¸ Q£*ÝQþéýUq|SsïùÔ…éǸP#2×ð†gq¡Æq!®ÀEªÑY©„òꡌ[ÖtžÛ.qÑ؈’P‡6ïÞQ9„C‡ÑÌ‘P¥»¤ ÚY¿mä3Ã```4}Äo.n¿@ºneTç¬WÎÙ vf·ÊºÈê‘ÚùõʺÔêxÈ;ƒÛ¸à.@”‡ HÜQÁé>Š\§%Š–®ŒÞð£qQ!—¢¨¬·¤ìì¢mrh'³¸‹ÂsÛ,. éÜÌ~ªy`£ÆÈ¯(º0âq8ô(šyªô.iú»‹ˆŽ|f Œ&ÔIO¯ŒºUü–¶Êø¾³ @º)F6!FÙ½mÉpØ,:z§Ó$r~ ŨKp1¸ îÂH6ê•wUït7ñécó#bT:»Žp!Ǧ&þɸ0J4*¿K'9t6´“yl$ Ïm³¸P½Ÿ©†=Á³©n½×»ŽÃ¡GÑ̉P¥?â×þkãÓn ¼7ˆßRF± ¤K56P-)eiûôß¿òF9ÐZPÚ)<Ý¡í"\Œ›Ü¥ÃÛ¸à.,Ü+Óíwsx§K(u÷¹¡@¯\y2¥áû½ª z\׊ÑÞ „¸0Ÿ“ŸµMO UÔ˜hvCþê´#†}¶NÆmç¸ÈFFç‰p¨?"L„*eO8‹`v¡.ÆÅ â·Á…‚ : 5;R&ç…]üÏÌ,ehSààMÐÕ ÌQÛL—áÂÞEpÜ…Å…]´äÒ¹§K¨º?óh¸Îj@¦GônpÕ6ÆyŸÞðb\Ø&ˆ´\‚ ÊÚ îQ»Ÿ¼3lÆãâ<\tZ~ÛÈîñph0"Œ‡*gÄ(ï€A0ðz\Ü ~Kaˆ1V\H™‰áXÐec´¥áD‘öZ]?6¥‡Ô;Æþïp|¨°ÛÞ2‘v¯&¶º\”¸Kçî» î"1 úÿ%G§K(J‡îñú;Ý3‘å<Üp2(¦VÓKÂ0Ç Õ wÃã÷»îÿÝ]_» Œˆ’"<uöØ ã‚qq&.JóW¢Ñãb<àb>9ˆ½{?9½>~Kaˆ‘q1×D øÇ‹dJ¸e»åµ‹ ^0»PP Ù*¨Ê1°“qËš‰ÂsÛ6.N:&æ¨t¸¹v4ÊÓ£¡JéG3mDtì3ƒ``¢\:…ü\äôúøílº#ÆEØê(¦áìâpGÂÝðÚy°Äõ\¥‚õU@Þ…·C;·¬™(<·ÝãÂ. iß¾ÆÂ¡ƒhæX¨RúÑÌÚsõ„Á@“™ ‘a(t!.n¿ÝÌŸ˜p§sÁôbõû.GïY.6÷‰g.5èƒýÔ` £g'‹qQ`ª· ãb4ÀF$Ùà•±pèQ4ó8T™ùÑLüLopN 18™ŽnèTäôÊømƒËzpA焸OKÅrsŸzæ‰8xŠ® ídܲ¦£ðÜö…‹Ç¶H"¢ pˆøÙsÝæý™;?snÂE$QÓŠõW_e\°¹ó3ç¶Q\Du“‹4ægÿ‡Û&ã‚Û^pGD”ˆŽBŒ1>{n;kŒ n[ÂEDÑTã‚ã‚ã‚Ûž=7Æ7nŒ nŒ nŒ nŒ nŒ nç·g^Àm¾1.ܸñì‚Ï.¸1.¸1.¸1.¸1.¸1.¸Å†‹Luî\qÓos)ã‚[ì¸È )ÿcÒ0zs*d¶Ä0ûúS”žeo ®=yÈ S"e <J§—Ù²•–¹P—œ|òŽmBÍàø3.=héj³0.¸-ÅE ÿ9i49 ?ýìÜÐÜUH*}Æ0½àÚc‡d•8iŠ.‘îrÃ?cˆ`\<©¶},:=çä…¸?ãÒÃV¯5µ?ã‚ÛR\”@Yý×Ó¸Èýb˜Kpq&-.Ä…µÐyS´¸Xnøg ûÄ…¿‰ ×ÇìÇ'F…‚çÎÅq‘ïs.}ä‚`¥©KÜ&pqdç@3g4Ãê–‹qq.-–\{ä6íÇÍ›"Â:<×Ú=ã‚&ÊO?‰¾$7øòg{c\PµÒüQŒ n¸8²sÌ›œ+Sf,s9.ΦŅC³ÐYSd\ÜY[)™vŬÛJ©.ž”jeël )ÒJµÙà!EÒVªD¢VMz|{DÕQQµÌ¨€¶ïáDD™ôO.ºÿ»“)|ËtÁ‘ªÝÝñÃÛ±ß#••RuKÞ}ÞIÅJ§Œ nËp!%PËbÌà‚ÀZ¦ý«•~h9423Òz´ðͬ­3·v ‰Tu1¸AïïãÛì,t`Šá=\Œ¾gͽáÚ½(ãG| +«ú™"¾Ú<ãL›¿L˜¡ÖƒC$š $ :ºHwD™Ø²‚êÞÑð :”]YÈÁÙ<椌•auÇoÇ~iëO–IhðáÝ‘DÉJ+Ù3.¸-Ã…é×jÄàÂÀZ¦1®ÖNÇŒL§E`f­«[œW°X6¯^üø6;ã˜bxOæ¯QÃï­Ù3üQ»÷ Ÿqv£LõAÁüЉêC£­dTêÐæVÍJ•!  .Lì¦(¡i숲-J  ´Z‘tcÛ-ú(Ó€:Ô°ÂÇ…;y).ìñc·£r@ÕŠ¶dwhøáîÈ«QŒ nËp!ЊtÜàúÀY¦„"*æFt€(€Ü:სÃVòNGÊÝKªhKQë1\x·ÙhŠc¸5üΚ}õûÀðwŒ‹BJ))½\oÀº‚ˆ…Üü•š~QIÛÂCHš.ÕÚô’ÃÈ’ˆ’ÊÍ`3׉\·èPæ~’ÒÌ9ƒ\(4ž^eŽ?¾Æ~ñ„ˆH{‡†.½âõ+-Û͸àvŒ‹1;'B·â)0¸pp–)¡Žhq,û×@‡ƒ™ÕÁÿ†¸¨ÍXQφ7‚ÛìÁú1Ãï¬90ü1» ǸPèZ÷‹kËjóL´]Ö\)õ’‘4S8û+û‡¸—öÙçGêp®ëýnBØ Xi{.&nG´f‚S{‡†îŽ4–3.¸Å‹1;à¢7¸pp–)¡NЈX9\9噌­ê‘u¯ÁÇÏãÂ;ÎÃE¾ áuÖþ˜Ý†¿c\4J)(¥”£gâBe~n)¥” p TBi;Rxˆ{YØcäÄþˆ/m¤»ë½àäî'5SÕàbâvˆ(mSz‡>Ü;²ã‚ÛzÚóÇÇÇÇÇûûûûûß¿ïì|ˆ‹>æÎ2%4€ì.ÊDšpå‘™™ ÅaìÁ€0 ï¸ÎøçL1ÀExG¾5÷†?f÷Á¡»]HæÆã®ÉþWônx‘4w×QÇGøµë(ÒßA*¡<"è©“ÏÄÅÔíP‘{˜;ÔÿpÿVºàŽq±›öñññññõþþþþýïûýýýßëûûûûûÇÇÇÇÇ˲…´bÎàÌ‹u÷Ö`n2Š‹2!²!ˆ¡™QMTY–Œ.¤= 5 ±ƒ;ê?Í7ü1»_«ò¼\tH¨¡ˆR ’¶ ÊÆFçðŸ"Gø#~³áìB]Œ‹©Ûi”u‘Õc¸°³ Æ·Ç··Ÿ÷wÇ„×ïïïïï?o—†º§peJ –˜ÝAmM"ÕÐéñ¸@p_Pø \ î¨û´ÀðÇìžqA3 ìžtÙÉ•‰yÏHzmãÑ9¦Çö¶åçßH»÷å•‹„2]’x:*sª›ˆˆÀÔ Ì¬ã&÷`^.rÇô¹°àoNée¿¼-~÷,GÏOÐR…["Œ»'ÊþÖÍ1yFIrr‘Má~¶&Ý'H¦[ˆÉÞIàæ'íAþ)|¸\.\úlvj{}®ìÄ¥M½— š€.¥"é×>ï‘ æŠy¹Èy[ ü îY.Ž=T2¼2X¶asÈÁ},„w IÜ?ûÐZ•¬wR,?¥¬Iºýص¬¶ HaªVÆÓ6Ci­åW—>“"ª†*pé“./R>kb¹xþåçg‘ )ÅàpÈôªÂ9­dA4=!m²„:–C2:%½yýÖù/ÈzE¹ãdüðmð7¸ÿøÈ¢¯%?Áô“Þ¾cÍ“Þ7ÆrñüËÏÏ"ŽÌq9tQÝxAgóI¯þc¹8úÔñw¼„µÏ}•åâù—Ÿ_L.nB¦”Ë)nMàSC‘åâ‘6¾Òð¢AÅrÁËÏ¿„\Ü„L^P>ˆÏE–‹GšhÌË /ÔÓJÛ/-/²üüjrq25Ћ_ Š,µZ¾Œ\T’X.xùù‘‹›)~1(²\°½xÛóò3ËËÛ«Ëů²üÌrÁÆrÁödmÿã_^~f¹`cc¹`;iûoüó ÄËËÏ,l,lOÝöþûÑó7¼üÌrÁÆrÁörmÿíû¼üÌÆ@gc¹`;iûÿ|½kž‡—ŸY.ØX.víÆë´Þéjy¼Uu‰³àгJ§ÔáŸåË^)öÛ—/_~ÿú7/?³ÉÅ}@¿ çÔªºÄÙ _Îèn’OMo—‹ïZ}§«5Ü•.r¶^ Üæ°õªã‹fܵÇu_|èBÒG·ýßßÏæƒxù™åâN ß„s ôg€~9£¹$u/?7e|*¹h ãåÊ ãY'ƒÔ»@b¥ z!éƒÛþÇ¿ÿáåg¶—”‹&Œ‰zÐo îOK×ä" Êøtrÿ(èE%¨ÆÑ=8ˆcÇŸÛ…¤nûÿó¿yù™-'iðÕ§“ U8‹p èW3š™Œr¯û´”q¼ß1•‹$0ú³É…0.ÉEY'ê®&{á¶ÿúØrr‘àüéä½\..«Å3êÁýY)ãpÞúßoerQ+­•Ä.íb""JÕDbî—¿“T²êõ\gZ§õä×.IºìRµDRI1÷º÷£½z||UækVÁ’S.´|؉‚L¶jö/ží—Iø­;²mߎÀxTÔJõZ3I“"fkåÁmÿ' Û¹¸ôBœ=Ĺzçg@ó˜=ͨPzŒc3EηÙ\ÀSF’§˜2öjí9)ãL-ÎåbèÀÅÖíl¬ÎÅm1fvÏŒØDµRèFÀ‡ÓŽ\Æ#P…ÉþÑFM‘ÖwMDÚÝ’o˜‚K󳱂Cµ39ûˆ¾u ôÊEöÖ¹[Te‹ƒN,IÓ"fkåámÏ‚ÁV,—€^„ór G8÷@ÏáüèQS ' ûβéÁPF.(ø’t§Öž–2ŽÕâT.jèa„­ CD5€™lEa€q˜ŒP)n¶­4­2@›ºL{ÐÍmgÿV`4l{øÊSÐDíY/jÆks”Iá:så{S4´ÌÉE5³{E•@?˹”Oº)b¶V> íY0ØÊäâÐKp^ôçè9œ=ÎãèIFu;wˆV­srdÓ{3•‘ Ÿt§Öž™2ŽÔÂ÷¢V)¥¥‚•²2ij"Œð5ßè‰È ¥É5FãJ9e§ 1.©b—›^¤ˆH4ÐDÔÛ”3 ‰jßê zÓ‹6³#°ö’8“£Íªûq<NDîµwCSí“nŠ˜­•i{ ¶è9œ¿è%8/z„óèç8Ož 4z&£î3|´¬2”RÆ^­=7elÕâë·ø£Cc1Õ—¹r7PD#4 ‘· ¹µm¦‘ïFÑÈÔeÚ‹„Í&"ãR6®V—|÷" ÁZ8Îä`ßkü‡…BSÔö{E•³õŒkÛoŠñóÚžƒÍ=‡ó·½çå@p¾ýç)Є¦@3ðÌ¡\é¹(£Œ½Z{vÊHÔâŸtŒ:i­5 µÖSX_•¯ ÊÛ&Ûº¨ìÿZ)¥Ô·›ùª·ÿ,€!u™ö¢õáSNöOÆ;3ê“^Ô e?h“É#Ѱ¸ùTòq”íE ò3“i&'¢ÞkÉáF¶ í÷‹Ú6ë›Oš-âÏk{>pÁrq¸vqè8§b 's<ãò§3œ'@Oš=“Ñs¹Ð;rQBûµö”‘S‹r¹pÔk0ÌèiB_5U@¯œÉ"¹hS—þozÓ<‹âŽÐþÓÃÍ;*¬t¯¨ŒÝ2‘f²†`sCaÛïuÐm=fÚ>.âÓl¢fc¹xÐ pNÅ@q¾ýç ÐS„¦@ÿx¹Ø«µ¢ŒH-ÎäBøíH~À8bì1Ó3£#¢erQU¿ZU€L]îËŲªI&£:¢Î® ö"šÝ>½$“Ôaô‡8ÈMÍMFå‹êwýê¥í÷ŠÈrÁö¤rqè8§r G8&£Îpž=Åyô[墈2öjíu(#V‹Ó´f•z·fÓÔ$;hj\Å͇¥TnóÂhÍ"—®wl›Ç-DU§—‘‡>íE¤ms%™¤¦_×ÐÚÓ‰Hûù³STé>_„YGD›"²\°=÷FÚk@/Áy9Ð#œÇë‡8Ožâ<ú»å¢ ®¨*¢ŒZ{ÊøëŸÝ^”½3JcMBù%~¸MrË ž¾"ªŒ!ÜïFf&aw‡%.[@‰.#30ÖTõpµZ‹‡©¹Ãžn#ö°É$ ì²¥1¼a2wªÛŽ­÷Š*ŒÝ“×ùÓ!í¦ˆ,lO$Ù;£®½çå@p¾=‡óc §8?˜—‹(MF.ÈOlE”‘;Õí“ækíe(㯿ö{Q^;ÚøÑQo?ØÜ^2  ëàÆŒ»ÝÈq;Þb—ÂL¦yFûÓ²!Îý-¦f{’ÛÑgê4“D#‚pÍÙ1="˜÷Š:ͤX’ÆEd¹`{"¹¸èE8/z„sôÎO€žâ<zü`^.rÇô¹°âÕœÓ[’ækíE(ãû_—{‘˜ìù|?W7[Ù”Ë1fŽ×.t=ÚMÏÅ.í9ù)×<4@Kß§úðo2l¥½^DÐ¥™$’XÛ»Ž}æå¢…eŠZ€™+?’Á”‘å‚íéåâЋp~è!Î=Ðs8?z‚óè·È…ã˜2òrá’ækí5(c«EA¶äTŽ`µÆ±"ÙV›g2®Â1pä’ªVº4þùÕOåÿDÒm ö«‡q« È–.UÉÕÕ¶a’XX˯RÖE­†*Hã~ gµÂrÁö rñ —â¼è+Î=:s8?z„óè2:%½å™ó|6¥¬7\.óNìÔÚ³RÆßGjñR!|õѽÁ—l öb7{ËÛ“ÉÅ/ô*·Ÿöö‰)ãÇßGjñR½H.·z¼ÏœƒmÓíq(E– 6–‹×ºXÖdÞoŸ™2þñçyÿùëÃz‘Bh÷M·|uèèÞâËËÅ'úpßLŸ™2¾zµøñú½H4FÜÒ‹–õ;Ðï³ÏLv.jW-^¬±±\°ýrÁös(ãë±Zp/b¹`c¹`c£ßì\ÔZp/b¹`c¹`c£ß¾üóû—/ß¾þà^ÄrÁÆrÁÆvD_¿|ùöý÷"– 6– 6¶#Êøñ÷—ÿ~ã^ÄrÁÆrÁÆvHÿü¾ªÅïýó÷"– 6– 6¶ e|ujñãïïßÿøób/zï-W­ª RE÷sUéUü?㦭ÀªV)YïVNiîUWËÛËÅ ½$ùOâŽ]Îx~Êø¿ÿ~ûöçß¿ÿý–´o¼C·î}€Â¢Ç»08b“†a¿õ_Ÿ³â†7ö´jî¶™õzcçõÈyu|ÉŒ?[ž¿‚”,lŸè%É3i®2Àõ8#¸ݺ}:ÊøÿõýŸÿüþ¡½¨2îV¯E%™ƒÐëÃ& ûr±ä¬ô£ tÀTûmï¼;Ô»¶/Ï_IJ– ¶Ï ô·ÉÅU¸þÀg¬raÝ>eüy©%Ä· æ|Ä’ÒöX?4ÆMö;å"ˆ¥Rd Ì@DµBú)ä3VËÊ{=v^ãè,q`ä}%a¹`Ûú=3¾?è%ɷźÊ×8⌅2œÛW¤Œß¢<ÜGÊÂ”Þ ?ºèçDQ“Ÿ,ÕˆK/9Ìd¬H.¨?º–Œå‚í#åâœÿ\ ¿.‡œ±æ¹D.ž“2äBª–H*)æ^÷nd$f­µò±ç^k·$#•¬z=×팪¦Ö~?´>êy„?ß„go—‘\MDT©^ëq×+]z÷3|mN–U©àAŸ3Ê”$—±v©`‰®"’ªJë¹¢0cÎëâgñ’TŸ[@òŸ@]UGcß<‡‘Ϋwž­ë¬Ý{‘ûíÄEP+>eTÖðm,läb ô@Ïâüè!ί]¡› ¢À5”Ç+f€PFÊyÊpnÿûA”±úÈPFZØ«”q.@7·Í{Fɶ"a€q˜Œ­O Ó U30KA.µ+óÇN{‘ò1¸—Xòý,çP¹^¿vó`=Œ°®¢}ÎüPo-ÉNÆ4€~®‚l¢iÛÆ—×fÌyu?òu²ZæÚþØEX+.e\Öèm,l—å"z„Ž gq~ ôçW®èvî-gåb-VÌ Œ„3ò”áÜþσ(cñ‘¡ŒMa¯RÆo~ ©”” Æ<¾º‰šˆl냞hrs—}óL’Õp§´-e°/¬vm>ÙÑWïafé@QƯMMÃÔD$:‘~­DÓwaI&[˜qðm߯—ŒEk;ur2ÙMDjŦŒË½å‚- ôÎ3@ÐQôœ'@zôU +;®Éð"«‚k±²ø·RFÌ;”­]ÜN‹ el {•2~[U1‰å«[¬Ý¡±c­Úæ¢óóuU0]ª"œ¦8 ¬ÞÛb6¶éå<¸ŠÏÊEüÚm5ZE¶5xÐöaIvmèíøS‹àÛhê=¹Ø©;.-kû½j kŦŒË½å‚- ôÎ3@ÐQôœÇ@zôU Ç´t,k¬\ÜKgìPF$·SÆâ#C›Â^¥ '“ÖZZk=¥ÕTß ™Ÿc¥”R“ŸZ—àÕ²SÎ`Þ,Û¤5 Tþ¿n Ülû°$‡; T ©WgÒ•7';uB~žµý¾‹µV$ Ó²FIY.ز@Ïá<ôe@/Àùèk¾ ô˜–Žå"XgÎÈÅý”±rÆeDrq;e„>ÊØö*eœ¯]„ÕG“kMD2Ò«6KÇ¥G4DÝÁ˜Ñ¯~Û6ë÷Ï&Ékw·‹™0S™¶Jrº[G‰âE•P.öêäd#[Øö».ÂZñ)ò¦Èa¹`»¶v¡“ïB‡ŽB —à<zØ£¯=¡¥#¹ÐÇrñÊpœ±C¡\ÜO« eìöarAõÜÀDÐ+grÓ‹¦%#PÕ'›~'4ë8vÐm½ŒÒÐékwÛ>bxiûµ$;¦u»CûH.ڹث“ m¿ç"ª•D.üè‚å‚í&¹ÐQôœG@zôU ?B.ÞO)gìPFÈÝ÷SÆâcK{…}˜\ÔD$Ú ZV™DæØÚ:E ¦ù`έ ­ŸGó÷­è¬\$¯MLø«ˆ&"sm”$oݺj×£#Rî÷ÚM2æ'£²ubÿ. G–YQ­H@¦ee¹`»O.Bt½çУ}è÷ÉÅ”‘rÆe$“Q7SÆâcK{…}\ ®öf ¢Æ•fŽß\ùEïfFÓP´%{Wf˜G„;£Œ+¢¦äµÛÑ®æ6£׉;JJ’·õŽa0©uŸZ´+Îþ³ý±S'vêl"òÈE\+•[á ËÊrÁv—\Äè(z ΠÇ=ú*Ðß&UÎÕ”‘rÆe8·¢ ï#G›Â¾G.’»d2ÚnÒhˆZ ¯ˆ*»±l}3ÐÚ¡éúEmÆN(Úši|Ãc·ÈKÝ- –‰_›:SÀX“Pv³@ô ËÙÒo×’ä3V¥ Ñ¿i¡“$”=ˆ´VŽó ´ûu¬ÌìÑr”öÑrAʰ·.ºÚXN¤èu …‰Æõ0Ëæ¶âm/¢qm•z4Ì\Ùsìëu“ . ñ‡çRgbÓÛi¼øÁ6\¦ŠJ’ÏXí¶Pw•+ḺVáØÓò#_'ë±Å£¶?rÕŠK—•å‚í6 Çè(úöVòC Ç=ú"Ðß(6ˤŒ˜3ö(ù} e¬»’2”öV¹ˆ¢?ùŸBÃ:â”m&^»5É``*“ƒ÷™PP"JS •sãÝV­ Þ±¾–Hn6*È! Û?„¹ŠK²“1±:SÐ$†!S9Ϋÿ‘«“¤aâ­¿¹j%ø±–5¹¯™å‚í@OÐQôç§@zôE §´”{_–¿l–IgìS†sûÊ}ä(#(ìuÊxX_}ºìuU‡çHo´wÝ¥Þ”Þò|›±\°1Й2‡õ"‰F|@E64ê³µ}{J‘å‚íeåâå€Î”ñ„rAãÇ|u ÔôïjûËÛ§”‹×:SÆÊ…hŒ Odïh{õAãu– 6:SÆ+ÊÕ’{‘›,•ÄrÁöIå‚þëPÆoÜ2ŸÓX.Øèl,l,l,l,l,l,l,l,l,l,l,l,l,l,l,l,³VÕéÍ$9 n’©äaòAUÜ®,lÏ%­ª©èå8O­€Dö’0e¼Š\ÈðRÈÃt> I ˜£Ö­>þl<ËËÅ9Î €^ŽóÔ H$“¤î%SÆëÈE“ ñwØÎ{ѸÒ`c¹`{¹hrá߇ó;ä¢2L’ y÷IBUøÑ±t£‚^Tƒ¯`¹`{‡\<çäâ²Z”Èv2ʽŽ)ã!rqöM& †KrQÔ‹úŸp› ËÛç‘‹á¼\.®«ÅÛ®_òêÄ”ñ!r!æ^k¿P$•¬z=×DT©^ëqvi´ÖjÈ<@­ “îZzVá’SzÖ¶kЋBG­š}fë•¿X.Øn”‹ ‡8?ºÃyÐ78?úF.„Òc©"!ÕI%ÅÜë~&¢vFU3e|Œ\Ø8„0³k°ÑÀJ´Ö‰58m¿}€4ƵM°¿-ïÒ©\½(r4û(„UŽŠnÂrÁöKÈÅÐCœÝá¼è)ΞÈE7àˆD\pÓ©ó™ÖÉ”ñ!r! 0“q#: Ó ‘úYΠˆz4J¶=0l cÔ6nèŒ\´@SçÞ,\¸ÜÊõ&Z²\°Ý(;@p~t‡ó '8?z,€nçá²uV.€nn; ¥jf)˜2n—‹V)¥¥‚ñÝäQ£q 6¹Å;´3ÐD¶ù„A¿yÀÇ&WЛN´d¥—$ŽÆèÇûBrÛ³ýÂr‘ÃùÐ#œÝã¼è ÎO€žÈ…›Z—7”7Ñ@±˜2î• Å–Š5n¿rk›WvPÎöcb„&jÐÕDTgî+FAŸ¨IhàÃ8&ŽÛê~°ÜpÓ²\°½A.r8ßz„ó {œ=Áù й¶úL.Öt\0eÜ)“ÖZZk=C=¥”Rܧu [ËyêM4h¦!ÿ€_3ê3¹è„r3£©#;—9xòî,l¿ˆ\äp~ôç@÷8/z‚ó 'rq6Nðr±þ²ÈSÆÃ×.äú!5TÛ(“]«š¶Œî•|Ìä»Q'ˆÜÌdêˆ&¢Þ÷±õh(ËÛ{×.vá|èç@q~ôÉ…ÎËSÆcå¢zå,:³9èÆ¶vݤž{˜6L«\Œ ‡'+]sV¦Ú¾™j`Xw7pÛ³\°Ý'{@Op¾ ô)‹ Ç8?:ËÅ É-Ó"n(·³ÎM#ÖD$ÚÈ<à'£:¢¦>íF4[߉#¢£ßÜM$ý" ËÛûwFí=Æù>Ѓɨ3 '8?ú­rÁ”ñ`¹h\ψn„‘Ný…ö‹SvËôæ`±:¥.íªqóf¢¦_ÖÐZžˆd¹`»O.ò@p~t,uŸ=Áù Ðß-UpE3ÂÝr‘Ü%Ó}ET;—èB»¥Í.ihADÍæ*ØHK”ŒRs§º­s ©#ûÏËo˜d¹`{»\¤wFåãüèU¸‘öè Î3DV.2Iât~^‹)ãr‘šÐup£Ë¥¡& ™4Ðn¯ƒ†m—ø¿!Î=R3§º‰ˆhL:"±n…køÌ ËÛÛå¢è΀¾îp?zŠó# gÎà¥r‘=¦Ë…Õ.¦Œ »ÕÁŒCÜPõh˜¹²Ûè ˜aûÑ׊Ä~KKQÐ¥Žˆ$–¯3g>ÙX.ØnzŒó ;œ}ƒó ß!-€‰)ãƒä‚H¶K=‡÷WCEDRÖD$ä0Ô¹HÚ ÔþÁ*DZ´‚([BÚd¡£ÅÕÉì%ËÛ@q¾tÎs gp¾ t‰)øwï;º¥<&‘ZVÛtRÖL&ï3}Ûp\÷b7¸eY.Øn•‹'Áyôê¾k™2^B.ärÚÿ}nä¼b[^µb¹`{*¹¸ çÐ…ÛÆ{ƒ1e¼†\ÐxËg‡ï-nx’å‚í©äâ&œÇ@n;)Á”ñ"r!š;â’h ËÊïq`¹`{.¹¸çÐï3¦ŒW‘ ªåÍø¨ø0?ËÛ“ÉÅM8§‡½fÊx¹`c¹`ûürÁöKPÆÿ@bÆ…“ž%tEXtdate:create2013-09-23T18:00:15+02:00“!žÎ%tEXtdate:modify2013-09-23T18:00:15+02:00â|&rIEND®B`‚stxxl-1.4.1/doc/images/overlapping_runformation_small.png000644 001411 000144 00000057153 12350112610 023540 0ustar00tbusers000000 000000 ‰PNG  IHDR¼)T’®ògAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿ‡Ì¿]†IDATxÚí\ÏÀÏ.lD[E±»ý™((vwww#¶ØM Š¢`a€´Hwp×±;ÿÝ‹=¸Üã8ýïûü~·s³;3ß}óæĮ́8áå~ÿNÿz'0?*ˆ§$9ô=€/çcvÒ¦ŠAù_“JÝ6 •*¼œ/ ’rÞþ˜¤ì àoòËù‰­²œAÉ‚_ò’“’²àÿ[H˜w̆V·D¿ÒŽ>Üm²ºçl–’äŒiƒ‹ä4u€•užŠ¥o2|(ù+nfJåÖaÌÚÖwD¿–~²ÝdM÷Ù|%ÉÙ†ÈóýðaT7JÛØæ±à—è#f½×nY2ç*•€W+’F‘þ„ó¶áUѯ¯F”<ú1h¢2 ½uçJ“›ÀŒÿú¨‚—ûªÑ]É_gj]©ÈK¢Ë”Ó§É5ѯ#(Ô¿úMT/äç)SÎD(žiFQUN¯Æ÷…¿¥tîš@OßÛh—€W ’º8Aæ³àæbxw ¦!]ꨉLõ2ý¹¸€EªàAÍ$ðæÏ4^Xq–â)óÙ·bx·£ådS ¯ù¾¬@Îf*õè'}¼yæ¦YäšuN#àÅ/Ðo/oÔ¾d‡xxÄ€Ÿÿ9?ùq dÍ6¼É-ú‘ÿí ~é“Q ^zÐø>?˜4¼ÜPO÷ß(þΈ ø˜ò>EÁh*~î\SäåIÞ,zXßxxQß´Oà ÀÏ .|ý‰R<ÿA¾ÀúõÎGÀQÒ+ÿ—¥à}qxg=wë…íõ µ/ÙÁqh9ƒr#Fiæ-ŸEòryÓAñ§—‚GÃ[òuBOߟLÖP^î/O÷XPN¦°œ La~ZÎ_±…ž^"ó!jˆ™O<‘yÎLJáH93ƒ }?óA²ç´œL¤œYhª„W(oaÿN¼ø…uùЇýþ hçÞÀËVW¸)+ ï™Ðú*”0ÑàÔ‡à¹&ÇG·ý™²üâǽÖ>0/åÙŽWÝKð–ìßpÍú »xa£;LŸv#ÿ€ìI·Pxyoí,²J.tœwÆÁØIØ5ÿèorýäÜmßòa`ßaƒNšÉ÷;ühm//P²ygðž ™öZæí;¯/c]pxËL «…yéð‡½ïAá¶—†]å&/m³o÷8Ãë ~B“ ‚gu:nß&,iÙ¥»G¼–À[ðthÇkž%x‹÷ì¸6ü<›âØì6Ó»íÈ1ñ. /Ïo”e&í¼±ÓIãÅÂrþìÝõÚO¾s§Cˆš|‚½‡>Ñkh‘ï‘G«Í^’uû‚wMȰû2ŸÙõ1x{¦pН·ÜJ˜ øåÑÄ<Þg|lx>€꿆×ÜC¥ØÙÐÀÞŽ €¶»Î…dŸl§\ÀœÕ5¶”ÙàЋ• ð^ˆèª3Í^€€fWwämÈèô¢92áØövI¼ý†aBoÞÄlp6ôàÆ¯‡ ì[ûE¿šÉÍ4TÒá!ˆ6p1^!Uƒ7h=›ëÐô«†Õr¤öÞ ¶&x¿þ[øNƒ=Ôbk[Øk˜ hÛë^NöÉqXÄÌÝã%fe1ò†#ðÂç#ªñTSàTwŽe!È[/0 ‚YÝrOâhñ[hòNZøÎ-}¸Ñí׃ìцþQï#§Fs3z ) 5x BÚy€¨È[òƒ·w³ÅKmŒÖ“‰~Å;~'RÕñYä~‹ÑÞ®…3ì_Ï@Ëz‚=ãpmþ€ÈÖè€éyÝ ¥àׯ@/‹:l6¢fÚÌæÐlíé`}ðæ2,L´¡S&Èî¾ aÑÀ_8š;€ŠÚ¼ù xè,p°@2q1=~öÌdÛ„ï\ê ½ÛàHŸl¾bfï=:ýqÝ5hy 0C ù›·ó‡p¯Å]¶R xñJVÏcBçP›%‚É{WÏ @+ºὂ6ê›:®ÈÅð¦äÀËI6™ƒ4!eÐÀàjð-wIeŒa 4¼+xbð¶ ¼y Øj:ÌCÔ¼Þ:!++' ¨ÏïßmtLB) o’ÕüU«œô;üѨVÒ»žüŒo±ééó{Žf{µð>‰ÞK­Bðл*°ë×˃7½òþP,‡Q‘Ä?²÷\ÁÜ!r- ðftÙƒç >–—hƒ¦"™ôG2Y1" )g>Pžß¿fxƒ7c0½ ¼½r±ÄÌš× xñJf—%¨ÙÆIè01¶ NäÀPw'êmÕß'^vf÷ˆ=Z2ÒŽ’:ï¸wëtÏ},<ðÎ@àí‡À»®;jdÀÌìyÇhñMo‚ èȃv9”‚ÈÞÇ`Ü~V¡ßç$MePÜg:¿¼ÈPôRKÞ7õö"i"ŒöɃ7§#Ò¯æÑL`²çŽÛ‰žûY á* ïr3´œ3kîIZx»œI= ÊÀkŠŽO“A¼xÇ1£!šÈ—2É8iâ®g^¤b÷¶G͆Èåô#K5 íÞk¥áEl,ΜVQ¨ç „|ÍÄ13¦óW@¼ÜyýPWY_¼ˆæ—ê"/ü.äZóhÕøØÔà Ÿ›Ü5êzûcŒå—ûHw÷¦`|‡$Rºžbx÷¶BÍTó¦uEà}›w¼×ËÀ;l"Ÿ5Ó0€l³ÃÈmYçì(+P ÞÌ®Qx? á>X/µ¼êÝ@’ú…^2ŠÁF7Àz½w6€i^´€ëIûa^¼ò¤±Å¹¿·ÆØ<׉™àeͧ0gN‡lp¹ÑÃà‚S >Àˆö3|Âgl\΀êŸÖ-g|—4 °)Ÿ;ncòïŽAÝmþM!ʵ_¾Ø\]d'/‚à«zžÂF]ÛúC0õ¿.© ¿Ïh:oJ7DéÅ÷juÀýÈáâ35‡œ¨·))¨Íð¯ñ{j.xÍxÆ ló‰ÕÎidõ>lÔïüMßÁ›¶‡Ù<—IÙðƒ†O`ÞôîàBÃ'¿ÈÇ~„tÑè9Ÿ±~%~«wNXNú„.Ù0(´Å;ì`ñïŒIE>õk|pf ®x Û$À‰mVðáëµ^ q^Ñö}Há&9 ÏÌžÃœÚ 3®gëCî‡ÐÕ9|´áÆÔO†#‚âvÕZ!ðšÁŒäÄ»¶K˜ ¸…}c é´wˆÖ|³àè•c kç I_¦ŽxÀŽ·³¹ùyñ ]Ñ0`ÝZpáü2HØ3hyÚªôg¬/&ß³çN‡?8r=#ð mOÀËU¤;8oþ~1ÕÍzΤµ÷ àã I¦Zß ?²ïã5Éú2‚hà¸îC䂸É}ÖšmíÇ÷°¶øää3(%ðçi“<{†ç3{À´št¨ìkL§ù#€ú,8~åX"ÈØ>hgÊçÉ#²ÿØÙÜù´pОh¤º¹à¹‹d¿sÐÊP´ÅÏÇ[_N)¾m;á%öw<âzT`znG^UOWqÖ~Ó‡]I»5|Þ÷ä5ƒË8p²ç»)#ÜòØLxã>Ñú BoÀØCæ¸ ë?Ï´öç½9|ÉñÉgŽÝ}Æ,^çøß‘DÂÛ Že}¨Œ”,>ƒIç±L& вø:“ÎA/R“òÆä" ÖšÀ 4Ä@~ ^÷”LŽÈ'‹&»da&ƒÉà1™ 6ùE8EççÁlô/äcô?:ú1#­%…–Á”|ä·â”"ŽhÊ–-¼`Ò™ –fS(*'=9 Ñá<ùå¤`åü I—“+)'›¥¬œy’r²Äå¤ ËY‚•3™‚•“ÆdÒi ¼„BÀK!¼„ò÷ÀKÿ@ÿÇ«šû=žC÷/›eÞnô®—‰´·¦y.ÃF:ßN øýáíL"‘ê·»Ã=áߘwyüϸëN#ÝŽçØý[ðæt! ¥~ÛqÛ<âþ9‚ç2ûcÇ^w²qº›Hðû—À[°q‘j™Ý˜$‘Z†ãÏgþ-5È9„£x‹fšÿcœpÖ¼ýÒ½¿^úS7Õr®†nµÖWÞ aý-5ÈóÁQ<·«6„%*ùvbúˆÞEzÿŽÙ'4ª·¼ò^hñ?¸T*Òf?ЂŽM²ßô*&Àû§lI5 †-¿JýW6rÔ†ãÇnôÉ% ûç4ïü¥÷C)ÿtUGÎ^ë“C÷Â˧ýûuM¸xÿQx !„€—^B!à%„^B!à%„€—Bx !„€W"Ѱ¼©„®ýM´,ïß'0 Àóoˆ–%àýë„ëY 3‘M´,ïß&ü'ÃKÇ[Ä+Á#¶Þðê´ü0mqÌ—rê#È~r îîü­™qGÎÐ`xYu…J´7¯î yŽñ·Ì” u®ƒØI<ºlôße}-žö‰›ÉÏÚìá NÀ«»²ªs€›ßà¸þ'Mì™òºmŸ¬>ÿù5°C:Ñà¼: o§‚Z"ðž2ŠÃ2(°\1½|Ååàþ ¢Á xÿxÃ4_¯#åp:Lìz$àÕmYÙY.¼ÛÛD"©©DƒðꮬmŸÃ‡¾ ðžFÌx~ÿPdé<ê.Íçç]!B}ðê°\n¸íQæíZ›)Ô• ½Y9#Ûþ`‡ÛåPê ]<ÛO48¯îJîÁm¡I·¸Dýv9ú,ïû™cÞä×ÇÏ}‡ò]–m cìGä'Ÿ8]ÎåÞ%Ænçð"’zµíœÁšy.ó-70þ¦ý§ÿºÅË{L« -äÄ=ÜவM‘kƒë2¯ëï£zNå„—ùÍx/*¬âqCŠñ%M8Xø·ênÒúÃs¼røÞÚU Yù/7Ò9xÁ4æš”%ÁÌê¶°¼m(–¼Bz§à…7.ù'³ÓBí[ ,äŸ_½?jžSÒQ¿÷u®q>#ðÒòs‹Ê\ù½Ê o!/›œ›Ï%àÅ ¯`Þýt…€và’æ{®é'ƒ#Ûë&¼ß'm â”þ0O#xy9+æyðâ†W Þýã*àñù?›:k¼a ~r×Ixo2\/”ýP3x9oöÅý3‡Ê‚—¶øVEÔçûÍ µVs4Ì%ü D¶¨{ð¶­V–èFé(_¿~Ä€ûð'Ò–Q\nøS*^òËžÝÝñ›q#›·40з%k»øt‡n»^º>ãh^³'ºÆnÆþ«’C{u¹RZW¦_3²~O)W~%Ï:[ÿ Ïl±1F'ü ôÏ6õ愲ˆïí vŽ9ðÄaÒçJ—‡Ïn DFF ¢}«Nzìö1G;y±b)º/%*2šL‰ˆ.­*‹b"#Ë·†ˆ™ÎLˆÏÑ‘î¨~[0hZPâlò Õˆ>V¼„¢©ì«‰NÉsælnµw zƒ°VvE¼„üð¢Ã•øÞoÙcê Dì±uý+^ö—s«û+·ȯö®sý¥|4ãöÇÉêzN®>ý‘®4MÎËÝënE(Íü¾µqÇKå ûó™ÕÇßWM{2N åTnd»ïÜp3JE9o¯Ûý"S¹”óÄû’*‡÷ÖÂÄŽí„­%ªxÓW7¯N"5œ¦Äýj_ŸDªÑz[ž’*sëV›DªczW­Õé‰ÎMª‘ª5ž÷[qè½U]©VÛƒJú†‹I-©nßg\eålV )çôЪhÎ8'´œM”L¥óßEËÙî°!íbG´œýÜ•¨‘”H9«5šYÅðræla=Egšï!mÓ¼ÜÄWñ’Ñà ’Pú%)üBX7a’êKno€oè Ó4º«Æ7Ï^ts+ÅþÏm…IjlUH&ÿT]ašæžŠGIÓD·²L©üÖ̵ÝÜF±36PTÎZ;–:QG˜Fß[a6…“E·˜Zµð&ôy~·é,ªí ¤ýÚ7yYSl%?|¡–¨°Õ–+rMÑÄ|“ô®6§é‹û!ùûjоTc¯¢%dd;qÆÍÞ*ÊçkqšYŠ^®3µÅå\Wþ•ƒEI|ÀM@zdrŠ:>YþžêârîRô=²¸ ú¯åÔJœf€"Ë>%®Òêk¹U ï­!ù p@aGÇ›^ý‰và…<õ±™pÚq}:$°åKˆ!–f]AšKÕ±4·ðûŒ,°/™§)ÈøC},Í E:}?–¤î#ÙP†aiº”[õB[L£A`—Ó0oI_uŒûüØÍ{ç(x@ÿ&XšuŠÊyKRó¥‚lòm$å̪Jx9s7BÞ]]¸£ ­k÷TíÀ Þ`ð¦´Ã [»÷`ùbZKÓl ‚4m±$¤­¸2Zò­Ú 2îQ Kc£` ÇAró ² ‡%Ñ+÷¾%øâôd6ñ!Ì?2WÅ<ãM©ÁYx3ìæõú)xÀ®’·‚‚ñ+o"–¤ZgÙX6ÀÒ´øVUðî­q$ôöE~K°‚¾BðÙÆWÔ°&I0#Ì'ÓÕǾqî+Ò'“ýžßA;tþ·»/\%ð¦K*¤ACùR_ÂOM=iêHøÙ—^…7¯'ÉØš©Þ: ²Ñ“°QÿK¹[…‡É@z~®ª½Í”¹ðVÇQÎ14•ð’ê**g ,Ió/Uƒ./g:iCáõA‚Ñý·s¾¦^²8®Îfp祩ùIK{²ÛÙ÷»MƒœYÇŽL¼Ë«~~Ùƒ·Ä+lÿßòÅÝK36RAš}À¯â~ÈBsìK]?*ÈøžäµX¨È^Ü-QàgdÚKÓ>¡ÒÇký°›wRð€wbiV**ç,IË ²ù9Kc’V5ð’n_·çÅÓÂRd_Ù´}Û µÖ‘àBûöaéQ_Çÿ¦ÆõXÔÉü0òŸ{ÿàA ^øöªÎQ4`£ÅôšÂ=‘áFâ4íÂðd¶`ȯQ8`"NÒ๢|ôÅiz*t™ÆT¯#³²›“¿ë`Ö(ê–ȃ0³æ™Âr6§é¡Ðä>€•sT¡Ð°»³‹Ô]ÕGüéï6?qúä|ÇtÆOj†KÓ`í bü$fÈ%*kgÅЊɜ¦ÐÉ?^[¬ûÔ‡'D¾`èÖÞŠ ^b2ºø9ܼ۫#Òudëø*Xøšd)º¹E¢B{ÚCl[,Q\έ"2¸)¼U¦•XÇëÚ*fuàåMÈüEò‹‹éL¤\yzßà>ßÞ !úM)xAÌl´c®>\Éôä%.7pR2Ä.9!ðW58¬ÖkökR펾]kÙ) ÑÀ}ÒKà?^­d^©`_‹jW’L\”(ÕèYh9kX}¨Šæž€z$kŽþ®ÄT|((gã5Jœ»[ i:¹*Ù5U#5F‚¿^Dóò—š¢ÃbVñÛ[¼_­ðìÍóÊj^»ç{l¾­Ü¯’rÝ©í/¥ý-üýôÒ¥G­¼Ô{ÎÂE³èNùJÓÄ]µ3?â§t$ô¸W”{åQJŠŸo0\ã–]5íQðpçüí”/u™Ùñ°òrò;¯¸ |òŒútG[g·ðWÃ;Ѽàbíó<À~y¤u<ø¤ÿ¬oàÀ+})1Oµƒ ÌFõz=>\ûO=‡»ä*¸ç Êœ?¥2°‰û v†*ƒ Áº*w)â³øOPÝ|ÕfË6üÍB‚‹lºeB ٲ銋k.±×ÚñjOýU1?:Zz~Y^ËYí‘hø(:ždy£=ÕÊ6ÑæH°‹QïU-ºôà¯<§"UœµŽo±}?¯l˜v9¼¬—Žsnä"Vå¡“nÓAª³Íáèe3~B³Æl¸¶òi~Á ®ÍPKõÞYÀ<ǚ›f 7ŸÎdðþð> óÑNˆO8-¸Å0`¡F+·ˆ•£Šð›¯–êå8£Î·{s8Âû|R´ ›0Þ^- ^xÁµétü¹&Žüƒþk÷[3xùË. F媳¼¼šÀ›oï?×[sÐnr¸ª¼©#‹—Í ððj/¸>¿Õ{oZÍ¿mokoÊðóÈXž¾iA o•ÃË/øY€+!#!Ò5xÉ£ñ«^汑Á zü9 m^øãð‹|Æö9„ÙPõðÆ:áZÏÆ¼×SALìÊ‚7Kξ±ÊU/œäã¹ÐYÇF=œpNÕ²i¹ðxÿÁÚúÓðÓÛæd‚òÁËøR¢³ðÒbän/L"«//# ¾’ Á¾Rï+žtP^ŸÉU oÎÕþå©^we½ŠÛöcÛøY[_P¹ä_¼qËìÌ;¸Jèí:]»Šà…¯*^rPÕðrý†öJ‘iö¤#æoÔƒ7nM³;•UŠ' pÁ øCª^NÄ™:òBKߘ®D“}Yr¬ñ0Ls„ŽPm"Ë—~Ο–8¶¶>žè Ê oØ`“]…æ,•ÝùAÿ¹±¶¯šš÷uÓ[•UŠÇ >E>ì”f‡x¿J 7è'ƒñ9âç~/úó,k¤úFœRiðÊ›oq¨§zù×bª\Ö[‡ðËVõ¼óiYxóÑES×:bssФ·*³‰!orþÑ ]tÞeG0ºÊÀ ÏzÊàeÙ…È|öÉ òà­¿~f_ýõ% pûg¬ÜxÙ ºüf˜þÇú=§ã‰­#‘1QÊê+Ž6ŸV ðÂEùä€á©d² f¿ äõ¨^¶ÜQ_°uñ’!¦~R/T”_°U9_v%¼%7¼ô|ò÷!ñä|éU\×·W:é¼Pa~ÁKû\2™%/;Øý}Y›ÈC1¼%ùätëwäüâ²Ë$>µ¸’_½ŠâU¼µöçf.ªsÚoS m­Á‰¶‘€=r<:Þà,-»×<{É*Èì:µàe¯êeÖY¯g¯Þ>8áÍãÎðžõ^qŽ»†á…—± —Yëf½úʬed<"™ÜP ïÝ^½º4èѫשµ/þ—¸.º/Õ¡—Y‡F¦½,¿JÛÊ96ý]!^x¯›õê©×ɬ×!¾ ¼ŸÆ\IåW¼èF«P}‡4óuÈ_ÁM×Â'ÛEæ¨ñ p£eàŽ³c†µŽXƒ+Ãl€wˆb2|Ç /p±šÜPIŒ”ü1÷`Üš×Yxó¶¤.œéQoj:nx_‰v‰](ûqòþlpYàå‰Êi(:`C·DïsÒá”Àë#ŠepNÚl¸wô{%Ù¼ÈûWØÏîG‹ È_y&rJÁûp&Œb>0ð¯,xÁ aÃ,À/Í{ º¿g†"/|u‡XUð7á^¡‘ÒÉØÙáÎ5Oâ†7½³ðüTÖ˜8ƒ4¨kç0x¯ 7i•oì¸ù:¤ý~íº@®E(‡7±“pŸ·”¯õSË¥«ƒÊƒ—:lnd뙈ž/4Ë—÷–žPóò*ÞDáù…0.x§ 7öV´tíÓþR^J•ð·ä¶SÎR×\ËY^xYÂP2½Ë*1¯ñ]\fµØçÉ®rx öU“‹´±U·Ñ¨[üŠ•µ@†?V/{‚0Fž´æµhÿº²àEFã™f×i¶]‘Ž1¹£ 8mC&°x¿"ðÚ2ôÖA€;h ¿à¥lÛ–¯Ê…7OYI_A/~ÝÎá…—"؃TWî¶DÇy0noÃá6Ó²õå5‘^õ‡o«úÐÿÂÐ$u¤·¢lè~§ÃJ¤ŠXª@‹•À „¹ñ¤5ïÉéfÑ•Ru_B¬ËóÉÀÃð4—waJxÙp_øÝî½# sÝj…:±Å½dß6f?ø/¼Kà§Ì…P¹fCòáVÙ{r3‹=ŸXx_ ñ -lJ–7AÄž~¿«ìµ ?¸,ei2ètÆ%“HVÕç/”Sz+êÆn)®Í.@¸m^ðZ°W«Ì-¦ç„üÊ(Hö†yÇžÍDJä>ïä•cIˆþYk>åÉf§ñ+ÉzmkëÍ‹uè;õÔ¬•Ÿy/pGƒ. qñóýêî•·…(ÂL¸Ÿ]qö£:9MÝÉÆ /¸ƒî™•ž#Ißù’J¶¹?¼é¨1Øä“=ïy)zYkíw}Q´˜‰=NÖ´qûÇl‹úí0þDÐ}Ñ2¼´±H½Êš¼<.ŸË•§^áûh´y]=/ü6@éO"Õ“1yéÉ)¥_3l‡cp®¼~ æëD›‘Ñ(Ò&/—ÇçB|>¯t5Ãh½+žm8ˆT½“Ôe>ò >’úÿƒõô¶ø©Žw¶2øÀ3¼U /‰ÔVÕš(ð¾i(còê” íöZ8ÉÙ·©æEð‹¶×6¸×#Yªu¦ks-é™ÂW^ˆš/õÉÍ$•„ËÂËÌ—²©Ò:’š~R»äÜ‚Êr£]¯I²Wÿ^Œ|jYušÜ…ÔXýén™£ëðrË3à„>Ì«MjwT-{©È¡©UËe^µà…¿®ÜuÜ¡Rky¾“j’zžÉQÞ¬»^ù¶Tï·Õ€ÔhùOõÜ œç {›Î¼RP MÆ{5µ©ëy5²Ì<6¾ÛàÕþ¥^ÓØm-I V«WNöó}zÎq-ÔmxŸ]S?3ÖqAHîjƒÔ šù¦±ÀåÐd#U x¹…aOû¼ÃlÛ=ÍNæ¿Ô÷»µÀÛ©ËÜ· ccu ÜÂuÿZÍ ±Þb%;aôjŒ Wç[߇ Êi°Ïy ==ío¨Uε„åŒÓxyÉQÉè"üâð D´ …Ù´ÐLÿ.kÔõÇÃÅá7«q``ÁdQÈÚg!üðz‹ÃÍ™‹¼WÐq<þ±øáÍ!æ|Gtóß=Ä1—ß«¡ ˆãjÏ¢VpƒA—DçjÆ©¡æ3‡‹Ch>iÚHÑù!¤–jeãÇ–œKÓ x¹®—Þm:Àϵ×nOßQÈ|l³ô¨ñÂ#Æ]IR3¯\K,,¬§b\«Å—WºO!Ü®”]Q< *ŠQÐ l­¼Ï˜\Ê3X OSá¾6þRIàqüçLJ¢_×{€×ÎxáÈ[€õ(Pž;± cø þoÆÔZ ƒ°ñVHbT³pgÖ+çs€7~pˆ¸R­/¡1J·3¿Y~xäm¼Rw2é#–4âÓ7¼2ûR­cÊÒݱ üñ°¥$ºpë–o-ì“ÑAJ²ù:ü¼äÏ£%Aü„î6I|gƒ'¸Ëp¸¶$Þ;NSz7«x‹Þ[`4^»äì©_ñ>ßWì]'5¾»y’rºã.ç!IÈõUºoûeéP ¸‚.±ãþgœ@7ŸƒŒ^þT;¯»’¢U«‰[$¡ÒIÕ•¥«Q:OIPz’èŽR'µ(¿yµro^ë¶pÓA’s ÜeÜ›4§c?ñჰkdÀ¸÷^-qRÓJ~$Èæ¾¦åä鼜Cú=d«[¢AuÕùH7_Ä/¼¯$ç%´3¯HNj©n¡,ÝPýÑ’?†×že)œ {*9ÍÅp¬’lƶ ùcŒäü¢zÂ}ø9½$ÇVXá.CI«.¬àƒ¡îHz˜vcñ>ߨV’ÓB„Q¶Ò»HÎÞ¿œxçAº/`>ê×ps]ã¨þÒ ¢™/,'¼ ±¢-âAxe¦š}R–.dMòGº)v«B"9Œd«²lx“üJý±Ib$WRqÆKN“ÊÀ]†€ÆØ·NTpƒý’ü±÷óñ—a_j+ „ΔXkfy¸óñÃÔSµÓ:a6dD‚ô…­þÜ®†²Ûm–U"‚÷€úîëõ jŒÔ#;a‡]Ðp{ø{ꮉ(^s!¦Bð{¾bªw¹¨».ö˜Ô8†ß‡DŒSÁ ƘÆ‚ÿ[Ÿ1Õ»Z4»ñH|–U­øu(uv¨GœNÀ¼› -’r†ÙAþ˜³0¥jó¦vqÊMQwÄ–n+DJCø–H—ô‹RÃÏ›7YhöêívþˆÎ·iªü$¯²ðBç…gòÕ&v­°6 GDÕg¨3á*:¯¨Õƒ _2ùg ¨ŸrU£ã柕sDЏœ„–WÿÔ™pø)*gËÇZ) 5>µ²8Y ‚­Š%é9t¤þx鱜ðFÙ_8|“Âgo|vô½Ä­Ç0¯À^Ýq¹ú±€26·mT·y¿‡jÍ]rß kQ³¾áÂ?@ xAîžöê6k¸ZòaÒr£†u›ñà¨3IÁy9 y݆ÍÍ% “~Ý´YF&ûòÔ*ùoÇVzõô›\«„¥; Ë ‘[ õR˸f¿è¯”ÓT2‰B»Ú³iÝÆ&Õ›Iýíкní£üµ1\ƒ^¾¶duHÞuëÚÌ@¤…¸ŸÚLù®G—ŽñÇ/;+)2}‰iá?R!ÀÍJKËAê…ú3µJ„›øöÉÓ€_ã `M_ö 6Çé" ïÉçìºÃ)°ëŽ ^]îhÕ+Ñä®*£Nx îë.wUÙY–6ÊmtZN:i¾xhëí< }âQÄ„¿û Ü»Ñ2'Œ‰ñŒp³³OŸÎÖó×À˶ÿ^>xÁà mÀ›ióNåàoÝÀ×ixÓFj~B×–SÚy–8“G¢ß ü韞¶êáZ wxøá]öÿ¼Ô‰´/|ÖA+ç]Fé¼êudë ¼­Î‹ZvñsÊjË|À÷µÒ›K¾cð¿·áï†<šP¤xÿoToºæªW[ð¦uSŒØ aä± °Ú’ÌŒ‚óö6róÑCàä}‚þà¥N¼¯ xá³óÿ?Tï)U¯¶àå­­³)<üÜÇ´SŠ’G›&çîËÔÁ7³û?N >€ÇÕõ×ë®êU´ (c¡z+^:½‰Éˆ\ÆÒæcOnn¾&fî¾à§«3·©~ŸñÐÿ¼jª^Eðþÿ¨^MOy×¼€è #ãµ7JŠ^ÇqÿCgL’½_ã[þ÷ëXõòÕW±êUÏ )V½°NìÈM)îžWxùUƒEá…’nç¨/e’<Õ m_|:U xaù¾ÞœKK¶}U‡:…¾^þo¨ìÓ dËYì»MÎñ¾<ã(ŠsÙE&à- o‰§uÛ?êÀ+WõÂÞc'÷­k“‚^ùª7wë–Ý?«£z·­—¯zZÖ‰ÊΣz³Õ•1à´sm¦³”ÀËû>¼c:ox³VµŒU ^Ê$Ù`gÙ[#™y›kW^yÓlð«ü4Ü¥Na¢‡ËU½ë›Õ «Wžê 3”…—ÛsK©Ù°¶S/otiÏÈ~5á•§zCÑc„bÛ-TÞÌQ2—àD‹’û¨5!*_õ²Î¿ì¦šdØÈ.{ 5:`i«¼dæN2q?š©‚Œç&„£V©0Å´{§OŸ>h0æ|¼À[è ÜihšUvÀvYÿ†”Ê’o‘ÀÆk*‚·Ë·áCSu ^ÞÃt[\%=ƒ0zËz¬æö¶ŒWÏæõ5´†MiRÓvùgf¤{ªaóf ·½7úXÚXEÇjçW©³Ï‚F¸…s³Ô¼\q7ñóf<_ÀRŸ…e.j'µf§¤ÿté6¾ LÔÀb±«,ç«ñªNÁ+”ç+Ëèu@MÞ‡b'Wâ$½ç°:ðÂn‚@SF¥¶gOoܱCãA©ê ØâçìT/’€¿kø­èWûÕS.¬-‚(ÖR;ù‹:Ò‘[´0¨œk)Lï5XUÞ¡»Wqé·- õ€(iÇ¡à@ŒU‚÷J›dðž±·ÆŠX~UÁ›M.ŽŽúC‡R¢¨ 7¹äK2(ŽcC¾#C ï‚J*ú!®"ýuK±›¶äóíkžñjÁ 8ÇÐ}ÜuJ›zÃõ "~°:ð‚ A™Å’¶€ƒ6¬¹òIÝ (𠤲Ÿ„ê¼à‹ VÆ"I×ýâÚøìÇ®OJϘe¾ºvó½ôâ4èIu7aýãöu¿‚ŸW¯VQ´iü¡‹uBÞ²VG)üG£?2ûD»¯§ Ïa_h2öf"ûî©‹£æT´ý„Œ~Gªê|”†õ§­CmÕðŒ|•‡õ÷l-}€'\'f:À¯Á+ ³â‰!R\‹ÑÚ­Ãt£$À™Ý+|m‰Øîg8¿Lzù¼Î˜Ü%ÄwÜ…ŒL—SaÏúG*ø!È–¥M^n§ÌÙZ8i™xVMVͺÞéþh\¢ äqNé.¼ü+H9ÊsV zðÇÀb]¸7{r»£s†Ë©(DkxxÙ³']q9ØkIw ”q¤ZUh¸·ÞÊB¤Œ$‘º=ôˆPÑÅÓÂOTRÎÁº¤ºûÜ?©Ø¼ÊOy÷2TY_ñ é˜Ç¸ûÄ«˜Ý(øþ20³*–°w×&Õ8äþYe9ßz”-'ç`u’ƒŠJ¦þr›Ê«xó-çò¼lŒƒÿìebËYè§kº á%[숋˨ÐUÔЇ1MIÕ:¬V6@»Ú· ©Yß ÅIB»“ªÕªÑÆþµ’Zƒƒ&Õ¨ÛÙY‰j-q®AªU½‰ù eQ=rvtmP½õHwÅoü¸9©FÍÚ&Ê‚ïsž Õ¯ÖÈtnÐ[â\½ZêMzŸT¶‰*{[×ú5ZÛxJ&*–´©F2˜¬l{$ÿ]«õ»­K©oÃÁö?vø÷ÙsÕHÛgº^ð8ú=…ؙۨÊZ/ OX{)]ašdQ-ýÛŠ5™·±è,ÅFwƒÐ Zs®bÅš5V˜¦ñÅmøN·ëWÅ­|B\²Æäüʇ—³Vèծ卨È-LÓÌE\¥‘¢©|CÅ9ßþšXð†O:ÅÝjâœ+ïNÀÛyŽgN>ATw,´­¢|OOE÷¦"2ËÄ¡R»( Ø—ÐW|«™ ;>wqx÷ÚÝŠ½Y|«¶?¶»ø%Gd¼G+­q¯¯ f"ÅFt ¤é†»ç±r*zÙÄ¡8Û‰f)ÿ‰KÕW¡^ ÃÎWXÁªxÙ3ÛD€/Í!õRh> ­ÇÅÆ© »ÛÌÔ„«õÆzt«H?Þ^,P¬¾"ØVX+E½\©ø¼{Ýê ¸‘¢­Â¥âóöU´Å%;™´FQÃßÂb·×sWô8s¤£»«ÿ)_@óQð˜¦Ùˆ¿T|Þ>Šº˜xc,Íjá‹ü‹Ï[]áéƒÛ±/µ ©xÁËÝjî«°J¯ŠÏ\ Z%ÊÛ%6Φ(ô¸çŽ+ÞÓü^b#p4\¥Ø]ÝêÕv××u‹ðØ…!J|G8ê/T<-¿öæ†ç‡Í§ïŽ û) û•8Cð2Õ™¥$²Ák a¿}„]ùµ?º(.(vâ3v Õ–Øx2o™à–êvJÎùa-t謫𥒺°õ=çàóþ3)õ þZaÓÓzÙwecG΋كzÝ—©,›Â““z÷›~G©ûä÷:;ÓaΕÍ*ò|†˜ŽÞ‘¬,ê…)S\•Τ&nm:ÄñmÕl`Œ^3ÊÔjÑgeå侚;¸×˜¥|ºÌ»3ú›O8¡ÔÌÉÞ?ÞlÀ¬gÿFêFÜVfžÊ,N§¨JÂÏÏPy^0;+GåI$%é…ª<,PA†J"';[¥K€žQPuÌŠ3ŠT•“OΔ*'//Kåä33¯2–øê¼„BÀK/!„ðB/!„ðBÀK!¼„BÀK/!„ðB/!„ðBÀK!¼„BÀK/!„ðB/!„ðBÀK!¼„BÀK/!„ðBÈßo~î€>YO]ÞÒ´øÔô·—Ÿgk%'ø}¸Î¶ +ê¼hÄ)!匱ÃOñôáð"B}im‡7’گɆ jí¡©ûþ›ØÆ.YYwrÑÙ¶IXÛà†Ì‡¹7Ì—–3¶Yѵ󹼈ÆÊ ê7çYr”­é!Ók]ÕÖ3ÞOèœ{µAyM½+:Û6ð{}x¡”7­–70_~o^p¦à=þ3ô)òOXËEÚzf~ Ò™]Îiž÷úÕvº«yÁgYÍ z•ÞÂþ¼"‹ 7¼T4Î`^uZ}ðÓ´pÌ—¿K¢®ÃsíÖ2±&ó4€×Ò—÷ØÅBÀ;E­ƒ—#ºykñ±™£]4j›| 7½­ŽÃû~Ú32¤=xùéŽÇR8¼êÁ{bÝ <ïiM[ÞÖ4°-ãäW¦ÛðÞüuTú\Pà]zê- ¯ZðF.ú£Å§†o O!ž»ÞöŠÃÛê?Ç$ägê‘]»QÙu©¼†Íö!l^uáÍ9ðCÛO~¦µ†¯Cæ˜åÛ·¯ld¿?AwáÜæ¢(ÿ8MŸÊô]tÍàhÞ~%ƒ€W=x /¡c{­>¹OÏ$Í2ÈÝêèèøŸÞÀåÑ: oË Ë:~A:žHøš ý\[œ‡xxñúyAÉ%‡ùU[ÍGͶKNšž C”a|YwmÀÏ·2I)ûa¾©ðΧ¯iíKÀËKëÛ3Ÿ.-ÙlJ¼»ÓÂnxqóÜ]C€_{óá€ÿÓõˆ/û/†—Ar6ÜŠ;Øá ˆžu'áaßcì¤Å¶œßÛ~¶Ëù -¯OÃZ­Ý4©ÑJzÖÐ!¦oã= ¼·¥ÿÞ©/ç:\€Ô†—OUh䲋ÚaŒ­•KU8NaQÕèK*mˆ¢SSO ¤*…© ÏüãQ«r©´k $[_gÏ›ƒ´ì¾¦oÁóúWx`]Ó@À5•Ë]×ü=Tâ¨÷Ì\²ZïœQö0·&§ êš®&¼Y§lfï—ë\Ú9ÝvþyVCVO½ô]Ó’¦šc3÷¨Üå ![§ZèZ€+æCçÑ׿“÷äŸ_0ræî]A7v﬑óOË3ìáO&Ù/rÃçê)¹µÈvêæ/ò^ƒŒãólfŒ«²f÷½…ü[ÀŒ7<ŠüüÖtðiü €c-ï¿Á,°Ó$€·õNàÍ0Ü X6v|p±ûÑ3g–;&¨ïWkÁùÖÝÉTÿQÁiÊVA2_¢m-8©zNªf¦ú»‚Ø{Ë®à]7A¯Ô‹»ŒÅ‚sŸ[ì”Õ\¿ìgjwº­˵!w3Á‘ñƒeû¸Óm'rÏÀƒ]â,ÁÁí†'eµïÇa‚ö4}VU~ßH£»‚Ÿ?ìCþMk7òiìÀñ– €7Ó’ vtL N/\Þ-Cs V/'ÞC2±žÒ_t}Û/2Õ`$º4DQ;ôÞI£• 1=EÙt–ÙZàÕ\ti,Yµ«eE5aÚÚç¥Í!ʦå;]€÷{{Ñãô“ž‰Ýê‹.ÍV­{‹g‹ÒêÝ“6¬,D—Úÿ¨¢2þn³ }¨âˆæó‘ÖÈj¿È7Æà˜9„2Åðîl¾ÁdUð&/kò SÛ«‹ K'õ3ìÅWªï–Ê!ÎD|©ÞsM¬¿EâlHŽR=~ÁPñ•š§UæãÞPœØDÚú9RC|ÉF ;> âù€‹À•—X.½F›.~šj¤.eö_ª}Ke>wêˆ÷J—º´±šøÒ”*òÜPG´BlXߢ1]Ò³ÁÄéYO ðög!ð¦ð¬ço0Ï‚ ¢ô›×Ö†ÖÝÁì§ßUÁ yé?Å|„$$×çeäR ì’僲—¶b5DZ¥A1óºcÙt¸QöG`—&ªšx‚7cikm,›Í£ÞØ¥fšojƒ6u&§`žsïrÍaÇaÓù~Ù'Ý[»´L•…Ï“xoÙlÜzb—PFªDž67Y³i}*øÐuoyóf&÷f­»<öÆzŸyE¶ÝsàÜŠâgン w?ÞßÂ>vjóÈ?uLƒÙÇVžaª´yß`ð&’4“ñ lÃqÝ| ª1{&žlêi¾î v™• "¦>¡ã rÊ“Áç†xžÔV•Ï>O6M?V¼\÷£w"¦ü}Ëñ‹J@âѧRB·;ºdø¯\êÅØixøð¾gld(½cüÎ_»®¦ƒïÓ—ý mgÜ¡$˜â•~ñ÷ãê©OˆîÊó}vó7:úrç©‹ÞŒ.XaÌ\QFfI”_«…e/ÙKªhŠ¡„X6õç–½Ã$¬»' Uµ”ç,yœ¡e³YÒRrÍ›…ZVLÄbà±Êõý&’Ž@ªJÇÔÄ.MRÕß³Çbi«.›“¤'­*£±b'%ú jÃÛFÿÛa’\"œÈ†‘«|X_&M¬Iœ—=ÍNM¾u>p_Ç {ƉäÃÖ©€waõ¯ï³êcð²¦a…íš+åkîŠ]úOJ½Hú¸”‘6Ëf€”Eú§ vÉA¥yy³bê¿’êèçbÙÅë€ûÞ{œ‘Rñ‚õ±K[Uz··aiJ­ŠbMÄ.õÌ:);;ªrQ‘`ÊèöY1ŸÇEÅö@ êüü0ò{ÿàa ^pW<È­¶UŠPîf1z¥r/#®¡š¼Þð…Zè¹M­R ÆZ‚©(ÕÝ}dgt`‡ª.éÕÅù¬fW}»ñˆ;”z7¤ \‚iÃÏ*óù†Ù[Ó¤»¥›õ°Q6O'Ù%Ïn¬*Ö ð§@šë ù‰Ó'e0C©i—›>k!ce?‰Ùè‹k ;*S:4‘iUk¥Ìr¥ÏEX÷ØQ AA(³k8鑪O–É%ÖRx‡º;UÕðf$Ò˜¾$R»2^´ Âyµ¡IºÐr9c„ ¡ÆB™ö ^žTíÈàŸÒ&î"3{á(´?ªÉÑIvá ã{oÇ«‚—7m ðM( Ñ,$^zöÐà>ßÞ !úM)xAáñεIÕ Wɉõ‘²¢u R­Î'dLpðÄæˆ•lùø÷‚Yšl|ÈßݺWh¿–[3e/Å9¶ªFª[w#G2×c`½G{ëý,Ûá_ê^‡T]Ï"V7Ú.cs¤JMÈq^‡ÏF,‡z ŽàYØÈzÐé1õ§ÿ’-pÁ±NµH5êJº)°èð"š—¿¬g:Æ þéÆÿÕêÏÞ,¯¬æErŠqõa¨ÜévèÓÇñroEýrûšo6E{ìÞk°¿yoí¹ƒ>fð½«ÞÛáëí§½·’k'Éãêƒ'ÖQ:ÒvÜÈG®ÏþÈU®ô¯n×^­Þˆoœá{íN\·ûüê“Û¶©à¯Ôlˆy¹ö.`=Œ<Ü:|Ô6Ô÷À»ùÜããѪê’{wøÕò›“ôÙOÁ«ÉŠ—HdŒ|+Ÿë ùEc>)¾¾eã_Í+Æê—VÞç£ïnb\8²k:¤ l¼äÌJÎÉZ[<÷Ô_ܹïóÀeµâ}/¿Û«óÓèíå6|Ãl2AŽýwÅ N/Àóf°çÞð®ÝŠDY…ÿ íX§•Ö§É/¼¬—Î næ"–ÔRëi÷ m™Ý±˜U³C Oã6ß\÷‚¬ExAâ‚Yå5)Ϭæxý1Å Òq©ÞߣñØ»‰Šg€¡¿FõþÖŽêeÿŪ—³ B* ×yÈÀ‡‚È£p!üó¸à”=vå[÷BŸ‰ZßÞ“•ŒÊNÏÇñ®‹BŽþH¨ÞRª7åï…W;‚^Àu+Ÿáf‹®lÍUf7à±zYóî¡#•Ý»+>¡z+Ë@‹Á#—“é¼È‹>fk9¶žŸ‹v ÓIe–Å|•s±qƒ[Ž_Œ¥ª·ÊUoÜ “ïxÒ1öuâé¼ Éq¦ú_ÿõðÊV™Ñ‘a£rUyÆ Ûô9{•`Îßúæpà8©’Çgœ¯‹+’?Ãl2·já•(~Ê>;?u;*ØkÀ#þ«Q’x ^¹ToúÔÔŒ9»0Ó™ÍÖ’ê…Y•ßúÐŽµÊU/VÞ"ùl­®Ã©•ÿ¤¾04ü!U /œq¹ÔJî½r¾^ƒ7Û¿ÆLàµcæyrË¡zAú´3w‰ÝÅT—i“NjEõ¾ÚU{•ª^îǵkeg”8¿ŽÈlä,VKõBQû´â^{ÜàK’Ÿ`Ö‹ó>0€‚ˆ'.,‚)Ñ%©oS4ŒàÀøASª^þÏ•uN•y×Çn&«{‡‡0½öߣæ÷¥õ |f¾ê)Ót‹ bv™§wß]Þh_ ª7ÑbD¬çQªz9_ÍûËtÅV²ÀQKõòã¦þÖ ¼õ·ÿ×ËpÞí}Øî?c¾i óù\VŒc‹›†tõ kËYïCÓª^õcÓ²›t’Õ7|ÃF‰Édýñý¬©²ªWµ#žŒ¥‰òâæ”%š«^Ú) ›ªXŒ¦Tõò§—Y¼§¥ì CÜŇղãÇhÞZ;RâgÕŸ‘¸kÛ~ÇÚDöˆñ Þ¡Ç “»/àsÖ.aƒäNS+ÞàÑJÍ P©fÔý£üÔ»Ã/[ñÚë|?¤?áÎê&³ ê…&½ÅCxƒ—ÌfklõBŸ8V…æEU¯â׌‹À ±XR£ƒ­ä¥žêûc`6‹¡©¡ô¸þg~4sÊè»ùë[“MðÉv‘€9j<ÜhùpÇÚ3£:>AúÁh6ð#>\¶ôûðAñ)¿¤áÜûîª5Èù5J /„6o= ÈU½ù8áE%Ò9BÈ;IÈ ZÕÀ+T½9™ŠàeSŽÏqËP /¢zë2‡^΃ÿÎij;.Ÿ´ÁÌj¸UË&CÏU:¼…¢½ÓM¾È…wZYÝ‚«.ðÖmÜMVïѾm8µé¶ŽF5ü4ç9çO¡ng®ÛŒó7ö"Ù©ÛÄ;+¦¿KXÚóH¶ßС>¼°‰fÓwðçU¼|Á.Èê×Õð6`ÆÖ¸ dÿ6¤…tµà-¸„¶Yª§†Ü[«Þ©, oÁ0é]£ ÅN—Iÿzöôé3›žr¹®Xáßi*Üév^¾Ù0èÏ€‘ÒJY¼1E;•qÆCÜßáÇüNZØ0ÏI ŠÉ‘ ?#6 *d¹ €SL&Sa@ûËÌW0žÐŽ«ÌÝÌ×DÉ2‹_ÍÏ(¸’²hb/©îYH x)»]ÂÂB½îH» kB2‰‹ ^j’ÿLÙãã£ú ²i®†ÃlñȪXÁ;%Œ™±\ÞÍùSðß·],åÆ>ÚRN©~öÁÂ9D=`ŸliªZ´o0ª(œ4ƒÒŽ×qŒQðúÄ Â>ú㇗¹­Q ƒFoe_僨ÍK2ϯg—%o|ŽÊ¥|D9©£F׸¸J&)s½`ðà"9Š7¨_— êªz«—"›9©kº¬¢x%è³ô^㲓âg4{XìRoâö?o~?¥&/”ðÚ?J~Q™„†ªe~x?¾~íëë(gB‚¾µõ¾à‚·àîk_ä/)}.(³  Dý¨šH¡…‚8†r¦+¹¿Þ¼ +Žxý:¦¼¬¿×ŸReºÙÍ—ÙÅù½þF‰ãüoÀË_D"U»Q®Žï¼(jµù%j¹ÊHî´jÊ­oi›WA.£&ÐÎKº-´â‰¦Ùp AHæðÀß$Zš¾Z‹Ôì{y¾È¹h& Sç$_ ð‚ÄaH^‹5…°w#B‡´¿ £Ð—{5Ά¾ i‡>Eÿ¼j9¡ßë‘z—³¥Ó/«iù0’¿DÀÁ ~õ$‘†~üžÁÖ^P²¸:©¡Û—¸bÍ똚WqVEb ýü#GÃY’üH<ýœ¤w53õKxT…ð¦©ø’vÕ¼:©ÞhßrV_þ{AT"‹SŒ›·îs‚¬¼ÀψT[_ßÄ1 Ö^3T­ió6V5=r/z~ý–=v¤WTÂO›“êè·è²\Ã¥^)ÃHÕ›5kgë¡á±2pÈ ãæ­ÌåV¼"KâH´.eÑaÜy”¬mk~±¼o[±ÏÌæˆÙ+̦æô|àM4…e~§¼Ü5Âpaz;5kÌÂ0ÎÕ¬*ì|î°Ž¢èçš¹^Â79©™×ÏCø85&dW¼q÷¹|B¢>N‹üÔ{?.»}¨þÄë)ø²€.‰ž0 (÷s°—cckÈ™‡^ÆqÐÊÁišÀû¢™(›FO5P kQ ÿ¨3ůA6Ð-qœeŽåˆ‡g­¶™]áð’ÞRnì}K2vš½g\×·>b³ÆÇpe4Ns/;%´PƒÑ*)–±<%j‹¯ÿj„Tv‘שMÂ×4 [ìqì4¡Î‹ ÜïÁá…×Côæ äºâ9®ÙC¬;Huið d ¬À³5¡nVà6oT'/@ñþÈë;¢° »maQa\ûCx³ø&9“Âpú¬rËÌŽX6uFɹnßj®|†V—„M–w#k\ÙLi*‰{?¹ü¥š%9û¡&Þ#º¯ƒh¶’€g}<^„ã’xïæ<èI pýiä#‰b_óQ…ÛoÝþbÞÕ K¶¾Q0Í|!bóü1>ˆ7‹—uH„à¼s…·Ãôî¢yoâXï ¯×Ù»T¼ÍjßÀî;´\¬ž«HWÞ°Ö’7ßÿÜòÆNrêÅ 9ׯX¼Â•Ï~É¡ SßÊ^~;ø8®lžw’X1Ê_ªw’yj^©˜&â7ö_og£¸òFåjâmx+~ šûjäûÇNDZ[AƒoºÖÓP4qÏ>Ñ´¾hò8iâ£R«ïS1|/Ü}é×vÓOÀsܧo#ÎÀð­}±Ï }Þ—»ïåà‚7x\$åáÜ"î3¯<ßé¿xámF¤ò@Á ‹ÛŸqN²Ñw O^2vÓlkˆÐA[oq¡F~ÞŒYµ…K}¾jäçåšC'5›ðÿ1DУԜRasÍ –RµášîY‡„‹ðÛ^ÕlrìƒP Õ¯ÂqudX‹Ë`© ,³¡1'ü»…Ó¼iÔ¹ÍCpÁ›uþîóÛIˆÆò>èr;?ÛwÈ·Ào–\Ä šûrJg£‹4^Xõ{E=ÃÑnò=S¸áÅWFéëõÛ.77nx?p^—:f½Ót±JúžzúÖç+pÙ@Á)ë¶Fƒdj˜ Ïg†q½nŽ_4mÆ„Mýê¶}]Õ‡y®!§SAl#zÂ2wI;W ƒÿÞd×éÓËmqÁ‹ˆ¨?ƒÊôkjM³0³sµ°‘Ÿkó\ÑÜ:~x|:*š[Ç/R/!#µƒ *šyž *T¨)ÉÚx9˜Om³8ZhÆŒa¾%*SEµ¹/ÖW|çöÏÆ4÷vkQTD)æâ…Ww„?^á’ uàWÖ($I xAšµvØ[xüòn²Vbõ2mÃT'Š6܉þ`p]&Ä‚5–ù;ïd{“°G†Á¼¼ñZ×eµVàMµÒÎê’…wÿxßNÒÊb0†-Žà«…CºFç—Þk9‹- ‹=¹ ÐøÂ·¦èÙs__A¼¼: /¸Ý°÷#ësL{y¿ÙÆ3É9ˆŸmëËt¨7ûÚÁ-xVõððV¼¬Û6ƒ–Gþ½a“=܇ïË8¸÷Ååû,¾q€ÕN\®^Þª‚Z‘àvT€(<Àç Ƶp!Î%a¼¼U¯†BÀKÀKÀ[ðÂ¥÷e•^vì¯\-À g'qµoZŽîà Q¢ä¹ ùjÂËH‰…þáåÅ훣9¼©Ëz¶ês§)¼Ü»ãúvÙBѼé£oê>¼Ñ+}dmÔw3ªoÑ•î+xÿ‡ðæzj›­1¼´G|Îw2ø¤)¼ŽE'nÕ{ªxY»ê_Ñ}x™wëÈÄ+„"N5X¯¼¼$‡ÿGxùðºvšÃûíÒëìÕ^î'd œØé„àõÜeâ¢ûð‚7z2ðÂüÔöj hæóÿ_àå—¶yáõZ€7 5ÒÚí*¼iVb»F5tÈ·rÂ{Oò{Ôñà.—uÞw“°YÙ×zžpfppj™iÚ”ëðdôÅBQ–˜ÏçGþˆfüãð†lÛ¼ÑdÖæMÁ[tjÓ[‹Í›wHâ„·÷P^ØkÓæ¥mWlÞôLÜ|Ùk®–cæ?a÷æÍ½ÆnÙ|T¸HräwRg„z´ióŒN›6oôà üGœ);ÜJé ZóFmݼÎxþæM§è"x¡çO´Þ/¢ÕÐàeŒ­©–À{~R‘úš÷²hQµ(úïÃ亃˱L;ÁH˜Íçëˆ×MxÁNÑv¥Û"x½S|òà÷‡(Tõ¦Þ´;W+fQ.Á›Ý[¸{ç´†fÃVaåYa«ò’çJNMÁïáa ?ˆ,šÔ{ŽU?úM´©K¸ÎíëYHê쪓ðú wÄè ×´¿i¸;úùí¡ü‚ðÁ[8HX`ᱚ%½'|Àÿ<¼<áfÅÆ_4„×½fÙ õ%G¼@9à%› ãÐHÜu¼ mâÕ.,Th5ŽŠÇK¯^]3\þ†¬ƒð&µ<é0a/õ¦Y¯(ägÐ ô^ÅÁ/¼PøÒ¿V¾E§6— J„Ž‹†«¢ö.FšfiïŸöe"p0ozBå—ç(h„™¥˯:¨`=x)Ph­#ÑßÓÇ <Ø¢^»`„—9IPà¶ÝðÔ€1¹hÔ¼¡äÂøàw[0;¥‹lÿ­kãW‰ðÒ§YS«¢ö¾ Œ^®†ð2Fogœ'Þ¨I¯¾·á²`óYéóŒ-G½$´˜¼#†ŸŸýÝäH[ừ•:èÞ+ÿÖk™êÛ¼à§ÀÞ¯Ubî˜eß;¦òàå?½ƒÄάìÊËA#Õ8#Q}­ÒÊå*Û!¡p]Fì9p`ï¢HõáýŠnHl,4yyîOŠ@ÆêWå(m,ú8¥\L)::`¯ÑÀQú¢3+<ë<‡O6>U–Þ(ƒåªûä"ô|–j¢ˆKEÝfA!&ãR+Ùæ½[ég3ñç qíÄ&oáË1–—þÀå€÷jôn~óçÓž={öXÍQ^2„ÍThòr¶ur¸tís¹Œ·íˆB«Qjn.sÂcÝ„7¸5\hòþÞd¶-.dØó¥—&Eé=÷P¥Ñ댚¼Âj.º5x’þJ³UÁofXD~qDx4J £ÀÙ ÔÀ˜ƼÛ;‡S÷æSå™—kÉLlòò¨*…UÍ×Dª+2yY**,õm^À]„¡1ôˆo‰åŒÔë½F‘¥ÌîŽnÂËšˆž,$|éYET ›SL)*½ ©LJ±ê¹w·µ“°Óä#ß ñJ¨E2It¶JÌ_npŒÂdó`ÀÌãíæûN’þÔtÜí$öí3.¶3+Í~øŒ½sÌ&JN}WmôŽ ‘ cå_Sg†íbuœgŒ(—x#ÉŠþÙSTS {E¶ ‘f°*þqI€3鿵Béå9nX§>ßýÞfMé’ :îàÙòbØ«þ¡Êª!7Ö +`O§Kxcæ¬'‘ÆÊ°øÛ­× œÐ½0 µ~!×V(ð:³ÛíÎÐÑ£I¤r{/^Ð]ý(ºÂ.ífR'_¹í~bï}œ\ÿ“jï“»ŒŸýñÚv×÷tíÁK0‡çakü#f [ÎB?]ÓUo¾Å΄„„ÄìÊ9c¬ð¨!&ÈNÎfŠ”åh4¹ÆŽ±x†}¯‡ ¶jÏËê^Æõ.¨3hàK<Ø%/CïÙÄIΩðßÇ¡¥Œöà™j _넘€µ¹ËÞ3w­ÁdqúÆÌCc£7_%»ù~?²R¥íOâþPÏ´C \gä;Y¿Dú4êqÃ9ÑZƒ6þ¶=Àb×/ ož©ÀÃë”ã¹Ø›E;Ë4&y‚h-ŽÑo E!ªOIãE®7ðQMþ8q`tÞCJnjK:' Ch ¯±XP±w¬.°›&>€à?D? çÞHµ¨&·§¶(2¬L¬¸¢¢;Xeh Þãñ§yÛ;:ç!ÖJixwƸ®ñUmåeq´Ã‰R±‚àC5Ä‘w©¬½ìÁXœvéŒ?ÚŠ/ V9…ö‰ïYc¯tï*nR£—*KÔFœxô‹w‹EîÌÔ¼úx¨MÅ@ÞWnù2㬇˜­-=ì(ÄB'·P}&ÂëæâÄc¥í¡3µ°ã*8Ú‚—3»m$Ò?‚¨ùB³‰<ÔQgœ²»ÏHŠ¿^ßÎýݾ{•q(´ðèxáÒß”2iŠ]²PÉW¨}KÙlR$1Ä멤®°¿äžáe³ñÅâ’¨|—ai뺕Í&Á»ÔîÆÕ_ü/„OzxÇ”K×dHâñ[ÿ.û¤`—Ö¨|ŽeXÚ&ÏËf% äÜ%K[ð}|P²!Q¼·Œ|VÌ};¾ÏåÞînKÃèGºuéʨ ÅKŽ­Z“e¤y ìRs•ƒ†ã’3)ê—ͦ…äÔÑ’'%a$ iÞ¼l6M°(ؤþªV™qgIJÕ¨l6úXØiRmÍ'ÿá =¢Á“S€ãl™Xž ‚š+,°äe%MR¥Öéc%n¬°õC´/ þ!J„_XPPć©……<ÀŒEŒ=(=:TŠˆWÒ¡ÆA{ó2ÒC¢L›„©Tu´šš•ÍG¸žt@U6¡-%hu-›M'Éëa¡jÙ*¹§aÙlzéIhñÕBý%ñ71¸òSË5¾þ,9A¦V²OÚNR†qªY‰½$qÛ²Ùô¬])ßY¿ráÕ á;KŠæ_RF2̰KÝU«>Ò7—ͦd»„G•®øBKÉI e³ùÒ»4Måføƒ’3Žî•ͦHÒÌ-ë¾þÓ%fÃÀŒ²OêQ»´XÕÒ†RfC²ÙdJÌãÔ ^à†õêVR®^þF±6­¶J%-)˜ÜXº3öÇzÆî*{V¿VK™ü…ØmuTïa× »g’Ô¥³ØÑ/St`þµ;Ëc¿”êÎ(¾¤÷Le>O1y`®¢10ɉõÁ[2G<¤}-})©¯èR/N%7Qw\m½´#‹³IT{õn¨^…ÝÓ\fú[±I1]µß»gý[Ò÷$ñUê?¼‹èq†ÊônÏÅ}ÍÕž †“è­o"3(N"~#Á?/H]‚Ž ª›>”Õ®ÁcP{©Ö(<“µÌëÑêk¾Qvާ`zFlµöðx“~ŒC Z¶Ÿe½R(‘zŽxFFä(êÕ:\”½gü<ô=«aééBõÃÖè²Î¤PÙ7ðnÔÎo²“ K0Q½ÛÙf ›Š.º¬9<þçàLŸmsž“{À4ùþêÙËïâŒS}ÊiîŽò ÞÇ}sNà´0 ¬ž³ÂMîx5é²óœMÞø¼0¼{ç-8&÷°šû¦9ÎWSu¥þ³o-Ÿ½ú‰Üóþœwœ³ã-¾Õ%¿sÏÉõþQž¬™½ì–¶NÔÖ±¸ @‰âk'ÖÎ㨣-aídSµõU~6¼„‚[þ\<ŒÆc–b%tEXtdate:create2013-09-23T21:01:29+02:00Î8·T%tEXtdate:modify2013-09-23T21:01:29+02:00¿eèIEND®B`‚stxxl-1.4.1/doc/images/vector_architecture.png000644 001411 000144 00000102255 12350112610 021255 0ustar00tbusers000000 000000 ‰PNG  IHDR â~𠽟gAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿ‡Ì¿ƒÈIDATxÚìw`eþÆŸMB—*ÂÊqŠ`WV±ž¢râþN –; O<±œ¹;{Á²vφ={7–Ãr–Ø=£bÇ bƒ"BP%Ù$Ïï™ÝÙÝ$»ÙÝdçÝçóWöÙ™ýæ}çϼóP!„Bô/B!„â !„B)žB!„â !„B)žB!„â !„BHñ„B!„O!„BHñ„B!„¯chúðÒ]¾×¿A!„Reeòh~½ÂÐÈo9ñéz)žB! QñÞ Ý.]ãØ¶÷æ%ν¶û”\±]hBÉV+H’lŒ¦§|’lž9©€i$›ÎòÙ{}M’Ís®Þ€¥xõE²ñ«I¢Ytr7=·?f#À¶ïHñ„BQpŠ·üpÅ;üaüž}lsÚd¦sû²ø¢^Va7‰Õ?T 8àù{¯]кâµu–/.ÙÒJžF6MŽâx†gœ½¹ýá’_îäLwír_Ä¨ÆÆÒvÐïÉØç…³gÏ~ÆÚkÄl›ºTÏ2'´mÔÚ¦±9fxùϱwOww[ä?¸£©/€ ñ\?ðˆO!„¥x/wŽŒŸd¹O÷w];]g Õ¦HÊÚÞ@¯âŽ5»Åá­Ÿå^§´Mc9€bûÓˆ)§ž3¡{Tñnõ»^ÿäËžI;Ò}¢ÉÐo}ôóÏ} Ë÷R}—:ÛlØ=‘ô wƒO!„£x'8Û•Ò´™e?}W¹’µì>’dãh`‹º”/¥³4´’ãiÛáÎ}Ä ëÏ+ì³ßï8FÈJû#©ÆJÚÙ‘ôŽ•T%ÅB!D¡(޷žu§bëÔm®ÔfkÜþB’WÅ3™ªâ¥x–=줉‰¾Ý[ñŠë°v‡>ÇüÈvKà†Ž½YI÷Hñ„BQ(Šwl!{À6­ÜÉ?ô¶’'“ü¤¸„)+^Šg±%rÀòÄWX›Žr[g7+uN,i†ÝèØk™•4EŠ'„BˆQ¼9>Kü&2Éq\÷µ»­d߇\·°sCÊŠ—êYþd¥œ™äÀÓ’)í—½ïÅRÖXó/Ÿš¨x§Hñ„BQ Š€{ã›7´í냸t{ÌÃÈõçÝ¿bÊŠ—êY޲LYñìéõœ³ÿ¯/€½V%*ÞD)žB! Dñö€‘câ°ßÈ&¸ÖÜžVúx0•©+^ªg9*ººEŠŠwX’/,ºå¼§š˜¨xGIñ„BQŠ·²­qAüþS£›Æ6¥®x)Ÿ%mÅ;¢å/Äø ó¨Hñ„Ba°â=è¿a LŒß¿12æµïB¦®x)Ÿ%ŠWÿÄHñ„BQPŠw`aêüªklTmÊŠ—òY²®xó.Ø!ÅB!DA)Þ$@uG¼Æ2£®_¦¡x)Ÿ%»Š×øÜ>þ¿Z)žB! Jñ¼“ÆFÙ+J4¦®x)Ÿ%›ŠWwÛ`Д¢Ã-¤xB!„(ųºÖ=Î!?´»¶Ý’ºâ¥|–ì)Þâ‹úC¨'¥xB!„(4ÅÛp}:‡¼ÐV¼žsSV¼”Ï’-Å[wuO Ëk¬OR‹RT¼”Ï’ Å«? üŽ“Iñ„BQXŠguÅéï% èC>a7ãÔœšâ¥|–,(^ó‰€Ç(ÅB!D¡*Þé–çŒiaóW·¬r}þÙœE6l;^EjŠ—òY² xüMRàu’ä=Hj\-(^ÊgÉ\ñ^²®â !„¢p¯y Zî'÷ ð…óó¾ÀŸíﵿ÷ßäŠ7¼}gÉ\ñNµnvî¤Ì„BQXŠÇɶ| Hîúe”:?7í þÅþ0·‡õ½W¸¿ô••ܯ}gÉ\ñF[ g:wª‘â !„¢°oVd"ã}ÖÄmùÚÞ?:nu¹ÔÍö÷â&4^`'7¶ë,i+Þáñ_ØÌJ:wzÇJ;TŠ'„BˆQ<{ýXcÜC'^é÷ºsNw`B¬3]ãîö÷^w}m¹:¯]g±ïñ$¿ó‰¤ŠŒW¼öêj‹;ýÕJÛÏýÕm¥xB!„0Vñ^È~3-&pK'û€œMq »}œ­z_t±¾6Ì5¯Js?+õûóº/Ó9‹=ËÔ$¿óޤŠ7*~ò–=íÓü%šÒt“·ºÆ`+u¦ ‰B!ÌS¼h?9#oü´žlž?혶pu³»8~C‘Qµ®q²¶cíf%6Úç—4Îb÷¤;7ÉϼÚÚt˜;uÓx'<Ë>IÑíÖçúGFÁî6X´‚$#+×6Úkí¾ B"„BoÕfpÒ§Ÿí>C¿qîõ€-ë]_¬·_ŠâVgê%vâåÍ$çŽL_’ÚYìIò3O³6r%®(¶RÏ‹¦¼=ÅÄÏÖ|ûÔ…C€Íf ·RNøµá³ã"ӹ̳w»G…D!„*çC¶\àÜç˾Êã¾i1+~‘øyäÛ—}`1öoJã,oÙ©%ß$üÈúM¬MÝ]_¸ÝþÂo£òÙ<:þã~‰ ʺ¿³w¼I›…B!ÌP<.Ü"ѽ&¸†E¼¿!¼÷½£#;ÝêèOw˜ó0».Ký,k®íIßC.|zdÓž5±ÔwúFRO_I›ÝËu†.¡Fò†ØçaÖ×Ãÿ*Фܨ¤b"„B+Ï)q«×àÇýë>ÿ×AVê¦7ôòÇV“|ö¢ýû9õßvûÞËÎãŒC’«6Š|Þj>ùË#—5رϦ']ùÈB!„B˜¦xäœãûÅœ'pß:ç6ÇH \MÝ.®AnptË—È[Ø»›S>ËIÞâÞao»,aËl’¶´ûì1‘”½Ÿ´Àv›Ý¤:’Ó[9•B!„AŠG6¼váÄýG>ôÔ‡3nÑj˜vüÞ»yî‹M9=KË4¿wá¸ÆL¼ú»XÒgg¸Óç­!„BˆÂR<ÑA9b&ÆdhPÊ(¥˜”2Ê ¤xR<]= JЧ “‚RFIñ„OWO¥ “2JAIñ„OU‚O¥ ”QŠIAIñ„®%ÅSFÉ”bRPR<)ž®%ÅSéSL J1)()žOW‚’â)(Ť “OHñtõHñ”Q J1)(e”OHñT%Hñ”Q J¥ŒRPRŤ “‚’âIñtõ(()ž‚RL J1Iñ„OWO¥ “‚RFIñ„OU‚O¥ “2JAIñ„OAIñ”Q J¥˜”OЧ«GAIñ”Q2¥˜”OЧ«GAIñTú“‚RLR(‘â–âu=s É^›Õ@®žÚßëWÏoïh௦(žï¨šXáúhœ!Š|7ÔÜ#Œ«»'ÒÅ{1¾†;ÙœŒúíUKIÖݳo/gÔ«[¸ýìõŒó2Éõsg/!ɹ“|¦= ÿ#Ù¼ð«:’|w÷S¼eà Å+(ÅûÍû$_Û@Ï+ëÉï¶óôÕó›ÛêIcoð;îâuww¯{¹;¨;ŠÌª»‡¯2Gñî¿ô4%£v|ª‘ä“ûx<£þÒÒ½èMogT¯ÛI¾øÝ -[LòõŒª&6}‡\qö`À·ÕíëɦöèÅ«ßR¼‚R¼m—¼É~BÚ«ž¬;È»WÏÆSדæ(Þ65ñåëý^žW¼>ï“$£AM5JñJÞ¥9Š÷@|¼ÎŒê}s#Éå§{ÿýßG-Ý‹N÷tFõ~—¬?:ò©ïK$?ßРjbÛEdMä]úž¿œÑ«#¯y¤x¥x/ ùL´å’k½zß´ÎÎ3oÓZ’l\¶´)VÀž+ö¸âuA†ï¿e—¢!ÇFÚ(Çš¤x—ÐÅûMCüX¼MÍȨ ‹Hòþ ½_MŒjñ^¾±—3ªø$‹}îþ)É|ÆT[ýBrŸèÇH¾Ñ#÷Š×| ¤x¥x¾7H®Kxä¼^¼z6úgÝGå7,1Gñz|FÖMݽ+P¼õß¾ˆ”°2+ÞÕäc›EJß –“hâíÞhâý3¾~{ЈŒ*¹$çÁ€jâ’\7ÿóYN’¬òtFý•äÿœF·IN0¥šèö)Ég SHòá,*Þ²¯ªª—„[2¼Vo]ÍûϾ2kIc«ÇŸUkÿ±òƒç¿'μ÷ñëO½6kqëÇ`üªgÞøâçæ6£Y9gúÓ3殕â¥ÏÉ$ycü•ô”¯žK.ÝÀ®æ(Þ%ä'ÑQqEëìÎPý=­x#šx£æž`µ™1Š·Áwl0Fñú¬dÍ–ÎAšCŠLȨ¯“ä/¿osGµÁ*6Ý32¾uë’gy9£z­t7âø„ä˦T7äÁŽ„®KIòÙQ¼æ·O²ZÛKö¹êãf~{CdCí1Ñ‘+mæÇI×ô³·´wé²ß-ßµp‚UwíŠi$ÉÏÆÀo8·þzÿ„¡‘Óœøt}K¿rý#´Ç9m2ùÙ•­„óá”­ýŠÇÞþ“/Í+i)Iu&½O’»yöêñÕš¢x¯áçýŸGþl±³<­xÿâ×çGH’'£x÷rÁÆ(Þ9- õrFõÿ„$×îfDP'³ñ¨„Ä¢E$‡y9£Ž&É­¬¨q3ª‰!õ$íæLº†$çõ̆âÍÚ@ÏQG½WÃÆ÷Þ 2[J’w÷Ó]Ö5u {ëèw’äÌI½L#Ùt–ýpÑíëèæE'wÐsûñc¬á1Û¾“ôG6>°‰óDÝK[²¼·]Ýxº]ºFŠ—fNÎt%G’¯y÷êùÒÅ›Â:÷ÌVûY]òfxYñúÕ=ínqØ‘$y)Šw›Ç\gŠâu]Ä]S¼~‘dÓÁfõa\[`O’{:£!ÉA®¤3Hr[3ª‰ëIò%WÒ CdÚ«x÷v6¼=L’«oî%ö–]кâ=¹€îG?÷íš…o^aÿ÷þÆ}ô/.±[ù¦‘M“£‡9>â7ö°Ó“a’Mo[QW'{<À€ë¾\³øÍcìîåÅ»LøËgœqÆgüÛoùáŠwøÃø=##ß7™)ÅKâE$y¾+m+’äÞž½zf¢xEsù·dõ›{xXñÎýy°;Á·”$/2Dñ6þ™×ÁÅ›Hžšê¾žÉ¨bëÒßÌjI’z É =Qo“äÖ®¤?Ó5@ÁËÕDו$y•»"ü$gû2V¼G{ãC~3 ìñz gÏžýŒuð³mêbíje°ï<ûãÚ›­%•ú¼;öœP̱§±9fxiÏÈR "£´ÖwܸœÌ`‡¥vãâ ¸ÿÏìØ,ÍÀá­$ÉÆé{Y[7˜.ÅK™q$ÉQîÄù$yŸ¯“Ù…?ÅO‚·UÆFxXñÞúSb3É£ÌP¼¢WùYWcÏ7›?t3Nñ®!IV™Ô¹‹’tÍ-ú䖞Ψ¯HòxWÒe$¹ƒŠ·I2®*|Œ$9:SÅû¦ P2'öù«žãf[On±z<œä˜½á-kØeÑÔÈpˆ{¿wË[*t„µÇdè·>6ýv_èò}ܹj7ÐgQäã·´ x/wŽŒþøðIÖæîïJñR¥‚$âÞÅÓÏzªmØÖ” ú&IÛ#v'÷t+Þ[®Ò÷ɦÞF(^ˆ$ù{wbI²½,.Õê8ùi WO’RYU¼‚j#¦esI®ï’¶âåM‰Kä,’×›‘Q7qùo¡âeTÖ2ê?Îzí½ý}&\Q¬7<)NoV¿#ù­/71uTé³»¿}“Ñåc’3Š3S¼|É4k˜A\Œ{}Û.Å;Nw%;nKñ6€â„Yç¾±û¬þÝ‘¶‡ýû&&œÜnàÛБ´ÈJºÇ¹[Eâ/¨³_ ¿ïHû¶À·î3œbŸû¶3Å$¬;¸»uSlMñî˜x<»Ý´Ûq§,OØÙîp3Ø99Š•4Ź›UMŽw}õ0G#†ãÔñö›c¯Š×å€GëìÞ…c~pÌ}´k¯íf’+B[vé²ÝÅõ$gpd“ɯ;âÿþa©yÍC×^xÁ\aÞóŽ&6}5ã£_¬ƒŸØâÏ©aüÛPdÀm[»z:$”ãÈ’â¥T1å((›i­®`Æü/q ”“7u1"£üY¨}Š— ²™QÿJ˜«ôLŸ×3ª‹ÕÕh±ðmvÜyWžsÜ^]Œ«&| Z˜­³ëó´‚hÂ|ï·€É$çnÞžæÖ|Ì4k’ßÃãR×ÄÍæÓ.Å—äâ½ ´¡x[H²^a¬oà±${$ð™‰;¯±¦V>5QñNqNl5ÍæúªÝ]ì$Ǽ*>XëØ¹Z#¾Þ—Š7üöeÑ$§»—es–†?¬&—ÚÅf‡õ$ßrÍ\°ó2ò½¾P|³óGFvØú þzVEc>!IžÓÂï±'DÜ5>ݺŽnùêé˜@RŽ#;Š—ZPmÄ”« l摬ß0uÅË·—À\}¤囯}Û¯xY *«uÀmO¿» îQ¿Øãõ‡È‹¢®'DÚ5~}|ê‰ß‘œïË~L\Ml½Âžûö0 ø:’•ÚõF=3ÍšznJ\ª5—](3ų»¸¦Ž[°ÝÇm(ž=Œ!É„sÍìM}âûç=˜äìÿë `¯U‰Š711i?×7çZ‰ûÇRBpoüωÜý>ÈKÅ›Põ®=<äމ䪧/ŸtYdi·æýc½V9ç­¼.~ɪ¡‹É¦­Ü­Ù®‚öÛ¹x›ˆë~ÄÄù…¢ ²¾8<>ý3:g£MS‡’zÙQ¼”‚j#¦œe›$Gꊗo%.¾½8D¾4ÌŒŒ:‘ÍcÐ~ÅËFP9Ȩ!¥w~ß—ÛÃe7nœ|¨kž®GUMÜDòæìgT‡—¾±‘ù;nßôUrñQi·!ço¦Y=ΞLÚ´Rž™âÙ—h—›šZØ!©âÙM‡K’ìoOŠ‚ñŠ7-ÙÑÝrÞS®3Û>w”#©ÚJÚÅínÖóq –²ŒGd΃ùÚ¯ÄêvømÓÊ)Öô2ÛÙO“ó£ýJŸ'É ‘O[ä‚Xéö½á¼Ë÷_C’+76lØ0»ÓBßÙΛÖlÅÆI̶֩‡$/€×·zõä<4âÈÖ‹Ú‚j=¦Üeq IŽICñò­ÄÅu\y›$Ã÷lf@F _4Òξx™•£Œòíúï_ãf¹òjFùì‰ûç‹Þ~à¶ÿF)¿nP5á[ØÍ+1u|é¿.6’rjŸvÔçy›i·XÓ—¸SçÛoU3R¼¹‘Ÿü»ÓP<»WV}’ý§ÛÇ»(%ÅKàkç#ÙrïhÙ=¢ŸW·šŸäíp {Ñ©7{¾f¥\éƒà~!ߥ‘t®är`tÔ àa’ä~Žïøø¦K×ÜXsl3áÅß[$É;Z¿zrHqdMñÚ ª˜r”Õè5¯õÁ€Ìûç¼ýþ?‘9-×Lð|F•¼ÇϺe¨x™•£ŒÐ/Y!¼i”—3jHd¦°ë¬¦ã¢1ö¾ÅƒÍ©&v'¹¸(ë…¯3Jß^Ñg‹—ºµ«>Ï×L;,~ð¬Cñ.ÍLñbs úNY–ªâýhÿG’í¿ÒQ®ÒU¼ú'" NÅ‹LÄën3´n‡F??g©ô†-01o¯Ÿ5DxÓXÊk2Ο»ÇzR8U~>IýXI’ÁèGë}öݱ£m×ìž0ò\ëi"ébâö¿=a†ÝWI’µ~õä8tâÈžâµT1å.(ëÍÉ_¥«xùTâ¢ô9ýKg#ýÙ^ϨK¹~;dªx•“Œ²ô¼Uoú<œQvGŸÚXV•Ü`+„ϘjâF’·g¿ðuJé D'ñxן©âåS¦õµ†æN]šá\kïì{cjŠ÷Š•Ö;éíaÌþ´oÞ#ïgç4Ê$ÀiÑÏVG±…Ì;Ú*hʼnyk‹ëqÖ§Méîð½cлž$·‰îG®ÀS$7r|oëà{'û-'[ÛÆ”YÕyeëWOŽI'Žì)^[AµSƒ<ÑFïæ}‰‹23î²9ÌÛµ{£sYûö*^fAå&£¢M³sêg”=ªs$£ï^×ÜÞ¯&|óIŽÍ~áëœÒ7Cª‰Ý’tòÊJLQú|§Å–L]\fŠ—W™6Üêfø¥cõŸ=›ïÖ™*p¾¦?nq Ši»\™ìp·7Ö¦¡xu·Mù!:ÜÂ¥xöupc‚I–Ä~Á!€wŒPÞÙܦâ]`ïûC²£]noü&eÅ[|Q`èõ޵.Å«¶3ò!gg!ðçX‚µ†ÆÓf(Þo­¯&íeµóϤcÕìcèn>¶’lŽ>—þ§…ŸõU²cÛ£–ûŧÿ$ùÏô«„ì’V¹U¼V‚Š‹)‡AYÕѲÈŽâuV‰K¤G…µûòîÞͨâ³f_ñR*Çt³VÖ x7£¬×·¸»X“ô_gF5á«q÷ ËULQúüï’/õÄ^Ñ—µW·gU¶üÌ4Ÿ]ïÍ+í À7òÆU´æYù™+¿u=°Œÿ©-Å‹¼žþ<ÙÁnI³oÝÕ=.W¬qMšâRoGPƒ¿u¶ïkOñöcïì*^§fZžYM’ o]´)Ðc I.*Ê’â‘/9&õë7»UÅ‹tO="ÙqìÙ©(^ýQà_Ê6«O³©þô=I®¿ºèë> =y©Š7%¾ÃoɉŸ’÷‰úV¬¤7{ÏD’«¢¯Z÷!ɪ”˘õââÿ⫉5ŽwáéU Ù $Í8r«xñAµSî‚úKbãCFŠ×Y%.iå÷$ùªG3ê·ëùÙ©n^%IÕqAå<£¬ÞéõîJÖ]­&¢Tؕ䚞9«ú:®ô½çî—¶©=æUÙU¼ÎÎ4_¿m¶TâhLž‚¬)þlUñ¾¶÷Ú*ÙaìÅðÎHAñšO<Æ6üp'ë?ðûSÏ;z€q5îíÖˆZ{ˆ”篌$›sìäwcMj±’ÖõS’\¿e¤ˆ¼åšËçp’œ—r»‚ÉÞ¼X .ñµ£JÈV iÆ‘[Å‹ ª•˜rÔ˜0¯óeSñ:«Ä%å\’|Ï£µKëõÁTî3êp’<Ú»WÔ_IºæãµX?†W« àŸm7&ÑÕÄ î)K€ß.²twͪâåE¦YÇûŒ$gQñÈå6 ¾×šâ5µ÷ZÔòˆÚçRP<«‹€¿)ÅcÃŽ±P{?S*O·o}-c¹e•—ï ×°‡®ÿ&yd4v|IøIζ[DòÎØÍÿ ’¬ï’j±:˜$y^\ê–tµ:§S%d+4ãȭ⹂j=¦µùÏ,÷¥P‡å‰KÊ$ù_fT®/ ržQc’5ú{芲ºÌ?Ÿ<7îœW« û=íÑ9ªú:´ô½O’Ιˆ0Òº¥ÍªâåC¦Yìïê×~Å»9äúøîpG£c‹ŠÇ“ì½îNrÄÅ+ÛV¼úa€“˜Šâ5ƒ×¿7íöЙçO}m}âi²ÒìäÃv]ò’â]D’ËmáøÉiQûN(iþ×Iò»½}€ï/ëÙ|­ãíýhç[ì¶±Ö$¹-.ÕjÓ-kO•­@ÒŒ#·Šç ª­˜rÔÐùîÙ,³ xUâ’2ˆ$ÿíьʵâ¥T®3jw›Žç®¨ $ùE|òçî)ܼZMØ…q}ïÜT}ZúŠI6ø’¼sº «Š—™käš!+Þ=Ö¹>¯ØÁñÝ¢âÙC*KÄÆ°^¨ŽeÛŠ÷’3£ÚP¼µãñû_ZøÅþIãš“m=Ý-’y¯x×;š1ºU‘\[ %±¤ùŽZL’_dà(’ä%È\ñìöQ~òÇž£Š7Ü=‹É†Vj¯5  [3–¾”‚âjm¸Ù™V›\ñ~ַ݇Hó@û߬7Þ3ÀžR¼§#ÕÎ&ÉÐZI+º‹‹?YTÏpͽºtz6’ä’Äþ [¿“t…¨)$¹*®Ò~„$—•´§JÈV 鯑SÅs•BLYªßg|!µ7ô@‰KÆÀس¢—3*ÊÙQ›^P¹Í¨¿‘ü—¯¨I’[Å¥þ@’oPú|óHNÌMÕ×±¥ïw$Y—ø]N5KŠ—/UÆÀZ’ü¢G6¯,.Å^Ô#ډ;­¸wЬ.tWÂßìÔœ‚â¶6œéL«Iªx?ï‚¿¶‰=?2$ªý²¿=:Å3Š÷}¬â)©ut5L^ÒºT4ßðõJò$ù£« ¬x‘û¿Ø.ÅËÃ*ãØõ$y™ÙR<ŒZŸ0ñ“±Ï‘»ýåÍ$çŽÎqR½±õo®t|wÍκ?›´ËÝ! ç~;úÓ'~¶æÛ§.l6ËþžðkÃgÇmç:€“žƒ†:øÌ§—»Ž·Ê½cŸ~öãÆÐo˜÷Šv4AœOÇ<ÞÖ ˆœÑJŽø„õ$ù€âMtYM²ùÍ«þ2aÌÎ[ í—8Ò²èíÈÌ‚÷ݤ¸Ûû?&gvkÙ"^$¹ÒÑRôÉ¥¦Z%ä,tãˆUkH2\œA=×bPmÄ”õ ®j©#ª/mÅË—wZ=W=æÒx&ù^/gT6¯½Ae7£®#ÉŸÿâèW¾ßz®ÙžÏ¨'HòGÂD’k·5 ôùæÒ½ D6cêðÓ&9–äê3T¼¼«2Š®"ɵG¶ôƒÛ£x8tm,aù0'9úÒ}9þöegXŒý£,lcÝAc£j  ß;î3Dž"JD«yt|ã~ááѧF{ ´céáºM^à<àÜaÉvÚró_ñ¸2:G|Ÿ·I^½g÷°§üþì±o¬áÚc¬q+SFýéýs`÷uPÿË‚™“œ/Z‡;Ú0ÃVÆ~±q+¿g£oH>­Ê"¹nÏ”«„Ü’nìRôÛ ê¹ƒj#¦lunK…¬…QÌÿ×3L’MD[ƒ|eä[<žQYU¼ö•ÝŒ²ç”ÿ(ÚnqèZÎßÞϨsèšYdãÅ$O4¡ôíD’C3S¼ü¹1m<Ÿdã¸XÂ&µ­Ì“²âå[¦íø.I¾³ ²ªxØõÃè¤%»82ìÜå0çIv]æ°A»?Þ–Ô­º{€ÝЭ¹¶Oä«#›í~Vïj$oˆ}VcKf+oôû<á<àÂ-÷˜PK(^Ú|{CòJ_ ÷õïVS’lØ@Ñœd'­»ÒÑ¢=jYÜÖ÷´úƒü_’¼ÉþûÕ“kJ½JÈa éÆa?¼ií|~õ\ËAµS–ƒ:¥¥2V×'}ÅË“·‘½Çú¬†â‘/sýYEϨì*^{ƒÊnFEÚ$šïÛWÓ„ŒÚè’¼Èþ C?&Ž1¢ô]KòýTLÇ7¦?’l˜©scSôe xù•inm"ùëä"dYñ€qwÎþ¥îói§÷НsÏ„²Ð±†ÆŸÖº«ÚÍÙ[~î)ãzØè^çwg]töqÀ¨É×<ºÐùý—›ÇÎ!ÉUÑÙ¤·šÙí«@+Ñ.Ç[yN\ŠÁ7Ó Š÷ÀÍ$g=zųH.øƒëEcìÅwÓÔ>ÀvOĆC¬ÓWIOû¶£ê6.¹n¸¸­ys>Kò½½KàÛôŽ&&<«·Z%ä2tãðm0l×s¢â3Ço÷› |íªçZª˜²Ô1-å{¾âåK‰‹ŽÉ_ýßk.¿»šõmÝþR~dT–¯ÝAe5£ÆEköµïÜñälrNi±!Õ÷A’üâÏÃKJ6¿ègrî¾F”>ß÷$ÏÉPñòéÆ4è’üô´Íº wð¾Fò§‘±âåS¦ízÿZ’?_5¸µœ¶âu»ôþësLu[t\‚œ~1¬-Ʉ>(örÙ·×T÷¸†d£âîp·ã‰¤ïý¤}ì'lTç8ͽþ–[i溎8çxG—àÀ}ëHO(Þ½8Ün³ä·Ç­)Xr•ÕY²ñy˵.'I~·K¤Aùܤ³B;»¡ý9:çÌÊ›‡¥pOÿ%Ɇ¯W\uiïtª„œ’f›',Ø®z®• Úˆ)›Aµ<,|—v(^¾”¸î÷9ñÕ”A™Üò#£²¬xí*«uà÷ηRÿ;¦Ø ŒÚÝj£ll"Y{N73‚E’Ã3T¼¼º1ù&̰çõ ɧ´£?GÞfZ߯ŸE2\urÖpºŠ÷Þ§Ö;×·ËO=tÏÆMz$Ùk͆iÇï½Ë‘ç¾Ø”쫞¼øÏãFí{ìyw·gzáæ÷.·Ó˜‰W;¦Dùì¬w:à|÷0ßúþæ­i7žyÈ&ŠðQñ¿öµ 'î?jô¡§>¼y@ª ]ÿïšû*®?ãwIšü“ïxìÆS£š»ïu·ûG·î¦ÑÇ\xçK3gÏ[ê˜öÙ=3öˆÓ®¾¯âÖóöLqÎ+ߎ׼\];oæì›n•Û@ÒŒ#eÚTë1unPž(q›þý¹™ó–}ûÞsSبP3*GAe5£|»\ûâ‡ókç¼ýÔ ãzš–QCÿúÔûó—|þÊ{›ÔÞ×]wÝ…9-|pcÚúô'>œûsÍ'/NÙ·K{‚Ê×LÛ¡‰\ñÁÔƒz·ùƒAiºÝ>Š>?.ÿáëß~é?÷—_yØÆÐ½.¯|Ê­t¹•o;ÞNõ+å Jèœ@ÌJU¨A)£”2ʸ;¯Šg‰ò=-nÿ©lz|o*ž$ùp»²¼?Iò“„…ï¶z‹wvøÕã½@ÌJU¨A)£”2ʸ;¯Šw¶õ«o©‘o[œHo+žUX^j_žf 9¹2à˜R§çÏ4ñö¢¿z¼ˆùA)£ 6(e”‚RF™vç5PñN³b;$ùBdÏ Å¶+=®x[“$?n_žÿ>²¨ÕÜ»CgüqÜáÇŸ{wU¹|¢¯ã¯ïb~Pʨ‚ J¥ ”Q¦Ýy T¼Èë[¾œ°{ãsA`ÇZz\ñ®°‚ùmû2ýÉÜ÷)g\=Þ Äü ”Q…”2JA)£ »ó¨xk~ oË+ªÖǦRùîá‡Ø9½­x½/\kíöù^ÅíÊõ­^ÿÝ5²3®/b~PʨJ¥ ”QfÝyMœ4åUÇò½ÅÜxò1‡î·¥µ4\¿û›éiÅ»ø[Gu5ŸÓž|ßåß‹¢ÇøáÑ“ûwÆÕãÍ@ÌJUÐA)£”2ʤ;oVoÞ?ÿrÀÈa£>ÿƒÎõ¨z·ðዽਭeÙ!ç¸Ù½}9ïvð™S®úÇqû÷uÒÕãÍ@ÌJUàA)£”2Êœ;oö/|ÏÞ±o_§JÞ‚ý“EÛsòlo4CÂ8LŒÉР”Q J1)(e”!AeMñ¾ßØuʵGE^RŸÐÔ©–ôÞa]Ü¡–üîæ^yÓ¬«GAIñ”Q2¥˜T~(Þc½€ÛšIÎdûÎNö¤•ÿ™¼‡õ¶÷ð}.zuµ‡:êêQPRö×6™™xØïöÐåûßúþËWJ-;b•½m´®xmI’_\b·òM#›&Gs¼OU‚O¥ “2JAyMñË`ßyöǵ7wôy9ºGÝìÙ³gÏž}¡u¬£gG˜×н¬Ý1rîzºÀ÷„ÅÀá­$ÉÆé{YßÛ`zü+zøó2ûÓuÖnÛÙýæΞ=û+iDäÖ¥$ç„¶þ¿¦±9fx)ÅS• ÅSF)(ŤŒRPS¼ÕãदXÊ[ÖkÖ¢©Íî=Ë­c‘š¾TEÎðš³¹ÐÃÕMîånÀ‘ÑAᓬ/v×½øì™zTÆ>ÛF¶O}4i¶m}í ò^çÿkËÛŸFHñT%Hñ”Q J1)£”·/<Æ»†~ÞÃúÖ™(³Ïýl‚o8Ó™0£0Ö1Ƶ>`}ssg¼æÉº½îH¹Ö>ÁÝm*^*A~tWåÌ{úGïã®8ÙòXŸ‘â©Jâ)£”bRF)(o)ÞE°Á"wâÖ·º¼—‰âý`½ ʼnI¦Pö}ïø¼dc çbç/Ù?ûBGÚ n›#ù½×6m*^ÊA¾jòÞá%$ùü@EïJñT%Hñ”Q J1)£”§ïEÀUñÉGY_R›âñÏÖþ}ãÇÆîü|"€³Ýïd7³¿º*šô~€½›\» ·£«kCñR²Ñt;O[ ‹Ï}Ä Mš¢*AЧŒRPŠI¥ <¥x ›@qÂ,tßYßû{&Š÷¥}òÝÉ+ºo9>[ à[÷>§Ø_½-úšv4|ìÞkн×ÒÖ/ #s²LÔêºz¤xÊ(¥˜”Q ʳŠw?àÀÄ ÇZßëöcŠÇC¬/ìéNý7°UsÜ©âÛÝì—¨8 ’ðì·×Â^œ^“+^:AŽ·’,—âéê‘â)£”bRF)(¯*^ã€&ÊÇ'Hô¹´/2¨ö Wê.ÀMŽs|þßÀf³Wƒ=^b[ aøùD1€³ZW¼´‚ü“•r¦Ö¨ÕÕ#ÅSF)(ŤŒRPžU<{XÃô$s³6õiÈ@ñø;ës¦U]—9>‡àÞøÓohÿp{R½ÿ—›yÒ¡—ÿÌÖ/­ íþyJñtõHñ”Q J1)£”gïoÖÖ%IìÞ/32Q¼G­oô_ëHû;PêÜg/9&ŽÞp¹Öɰas[çKªxii+Þ4)ž®)ž2JA)&e”‚ò¬âÙ“×'±È¢ee¢xë7²¾òˆcλîÁ+‹[ýáXÍmCàÿØ.ÅK+H)^†-¬*AЧŒRPÊ(Ť :[ñ~´6öLf+íoâYÒ{ÇRž¶t¶Æ=è¿a X#[?Û.ÅK/ÈŽU< jõOB!:”Å{Åò™ÞI÷ÞÄÚèÏHñØó’|å²zc¢.lý0ÿ8ÅK/H)^fÔþ*ý„BˆNU¼G,Ÿñ5%ÛÛž>¤¸)Åãë;ÿˆ|þ±]]Í<“Õ­å¯ñïŒÓP¼ô‚”âeêx~ ÜàøÂ¥@0¬|E`\Ãx8àî-òŒÚr?Ȩ’WUÁJs«ïÅ»É~O¹2ÙÞ·7Öf¤xoXßÙpýùZàh×ÖÜyï´~”?ˆ˜›²â¥¤/Sê‚@©Éô2ZH‡2¡Pc^gPjX•š'­m*Þ¶àülïËíßd¤xÍ[[_zÌþ¸ð?×ÖrO·~”1€ãÛ¥xé)ÅËü–dºUþjå³è¸ûm¥qA•¯¶ðü­ÃýŠ:óêíRÓ+ï,[p>O¶÷-YiÅ㿬/ýÞúô°…{ê“Í×·~ËÇ·KñÒ RŠ—* ïWë*”Í¢ƒ(3P‡êÌîÏáù*ÎDo­3>Ûï:[pžL¶÷}Ö¶¢ÆÌï×^Îv²?7¸·ÿ0©õƒ lÙ.ÅK/H)^60½C^]©BtPqó#d\P@²Vˆ\*Þý¶ý\šlïÇ­mƒ™™âñTë[çäê^è÷|0°cëÇXÇø¹=Š—^R¼¬9ÉòÂet!:R‡ÌkVш !r¬x/ÚösD²½ïµ¶í©âÙÚ5p½¥[GÅmÞÛÚ¼´ÕcŒµvz±=Š—^R¼,9éò*¿]ˆŽ!h`ßÏšÒ:elÞÔ×&>¯^ùJP¼¯mûÙ*ÙÞ7']ûÏ+'Iî¼·ÕQ‹‡[=„5¢§µGñÒ RŠ—-* w *¿ !¼Ouȼª,\,¼= Š×<ÔÖŸEIö¶›>—±âýÇúÚ~ä7ÀæñÓÓnmÓ—¿ºeÉóíé‹W·CñÒ RŠ—½§tÐèøjýP3žÂÓÔUÓ^šW‡€|O\£ö$Û~îN²÷ Ö¤À+3V¼†­ï}Ç ’ }Èþ ³“~w] ëO$+íîJºÓô½µ¢xé)ÅËbÝaøCTX3§!¼LU)«Lª«-iE¨¦àr3QñìÑ84ÉÞ;Æ2cÅ‹4•Þ%Kâ7~kÿ„qÍɾz:N"É_íuІ&›¿øÇA›¬mMñÒ RŠ'„Â|jC~þr³Ö°0PZÛ¯xuZFÓkMbEwÀKYP¼Å%€AÏLØØ<-÷Æ{ø‚$¹«½Óé‰û4ŒŽÍ‡b+Þðö)ÅBa8áÊ ”õ6ÓHiÍDñxu‹o@­•Çvj΂âE¼i0ðFâÆÉöO8¨öËþ‘.wÚ;á™Eü+öŽþȯ¬úµ?H)žB³©ÃÖ°¨ @Yáv I¢x¿ö³”fëøQ<-qa1{)ˆÉiŸxFDÐF4%nœÙ¸O|+Û×~ôþÑúsÍ{§./Ä=Šœ ß§ÑO ì½Û¤O!„éŠç7¯³šÊBžŠ'‰âñ&[Š^Š÷áà W¹Ë¬]NûÄÍ#í³\—lëÇãn]}¥?psäÃE‘ºNuú[ÝÀɱËíæµ;H[ñW „ÂLêLì¬V]SØ™šLñš²œf§wú50Ô½œD™µëþéŸÙ~Ïš8Ø‚$_Ø~3-f[K'û€¢:·rÓè^»ÏˆìÕüâVÀ ÇA›íöº[ìÏë¾L7ȃ­=§ª B!„‡¿Øît™+uQ?Åq1­F°[úg^Ýpdò­“£ö†‘7~ZO6ÏŸvL[¬ˆíô–/¶×°óþßg•·Ÿ½9€âéÎCíiÿDKíóKšA޶vù©«R¨ ;«L“V!Å"ë˜üžÃìžEV(‡y¦P ¸vL´ØZ=½ )ž…L•&SmDÀ8Y¨*”³BHñ„0™¸­Sk¢…àW/5!¤xBQÈ”ÆéPØoì0*!¤xB! Q‡ÂU¥VFõ²­+÷ëÕ³â !„H£t¨&äPÃRƒº†«‚*©BŠ'„"ŒÑ¡ºØtÀæt1´¤5P®Î…BŠ'„"- Ñ¡ê2k:`KWÍèbX§5,„Oˆ,ÝëT“ФÜû“ëÔ•ûøCÑ8Lèbè–V!¤xB´ŸpÐØ%k…h¥à<®CuA(u­aáù.†•qÒ*„OˆŒ¥zbG5¼½_ØŸ¤³Z)üž¾˜«â¥U)žÜ)Œ^ò«Ö¯‰Er<¯CIìêàíFùp¥FX)žY¤0V„B@H9,’áuJJ…ÖïR¯yùýÿ¯a‘ôøáÒ±€$…™D< §Ø; /°n0¼þp<Å¡h@’Iøº¨>…=ýKR ÆI â½ÆÞyédäQOŸÜŽ·8î†Áj1ÉKMù?´D< u]†¥òl~œ-ˆC‰h7\ë *àe÷(OX€ˆô¬„#õYYY\rÔË‚ Xp¶þwˆx@Ó¥‚¶Î–eozEM’6¶D#ë@ÄPÏvSzU„U½+*Y‡ÒŽB´,ˆxz=½†y袧ۿJJÇ»ô _\vüA*š ÕAÄТrxæ¢w ‡§k¯ð…†b˜Žåí!b€ˆ ‹;y¦@îeãP*;p—F¼ÊC GR J›ƒˆ°^…8ÔŸ¸{oàb˜b ñ½$ª’ÆSQG’éê»÷åC »?´D<€·8(Ä¡t<(I¡®Ÿ¸xˆ¡¡ â¼é—rÃ1“’Q¦Q ÉÉÅÔˆ¡ â­—àj¬*¬Ð´e°ZJÁ\RMú˜ D<#K: Â&i¿ˆˆÀsÄ“˜w@Äì’ J!Föˆx€UÒ)`é} d€‰ÃzIÂÂ{´iNañÔ+&Y: /Xq]X)ÕPĺÐ)ç âðÔØ: /”"Ü…îñ°¤Ò‚­­*ÌñÔ…¬—KA2ží’6NIR)ð@ÄÐP²v@^Tr¸Óeõ¹ JRت‹]Ɉ#ɉrꂈ QÖÈ‹;Üê²XD’ìšØÊÐ ñ€ŽI8’•Óæ3õŸÅ"’IXUSھРñ€ŽJ- Bé fD“•úm¬²-´D<-Êx)Ȩ&+ÙØ¬)~j“œ?†cãX6fBŠõ;L‚ÜkB6y¿ã0FD<(’ä‰ +x¸‡Q·Ä¡Ú?fÚQ˜“D<€]ù.Rí¯KâP*ð°îZLJp"€ˆ°‡;p̪8”v×°ðð7‚ p*€ˆ°D:¤°§§ ‚ øúáÔÜ^î''ĨRñvèKR æql]ÒÏq¨®Ðjºiˆ!@ÄüÙ…™H¾Š$9õLõíŠ-u†VcLÚQˆ“D<u Yºd-ºíw¤z×°ðkêwCk}¿EÅ%&Ç@ݬì _(­ûRœOãPB Õ¿ðZÈçC "ào© ¢'AÇãPCͧq¨¯‘ûÇIž¸@#Ò)`逼 ({„•qÈ¿C "Ðb²t@^L s²7D%ë~Oበñ4ÈÖyéˆä‰ážX‡xâD< ²v@^Lr´¯­ˆ4ùÆjçKIǃM¾± ñ@ÄÐXçd뀼~GbVßýJ HN³ÚñÐÚü‹áiÆ€ˆ Q¶ÈK¤Íë'ñ°$c6Õ”ê HR=ó6D<­•pìŒBéäbˆo"wÔi`:`Ÿ‡VN3€ˆøQ*bieñ¬0Ý·ãAI Yu¹ØÊÐ ñtƒ¸ä0l½óY("I>«æz³1´D<Ý"á°P€/’¶"¶Eíu¡ âè"© ‚ü:‡,¬Öáú0@ÄÐ9éCဈ"ˆx`¹”óÄõóì,@ĺc×ЈxHŠYVSª/ ñ â]-ÂlÁ¨[2âHr:ñ-:sã!I vd†”tŒ“ D<M‘²tÉZ´#Ž%)Ü¡ó§OÖ…Öh‡~ñ 0¯#ˆxš$áHQ~ ðª¿ÓkX$š?Íu.´vn ¼ˆæW@s¤‚RˆqGðtÒD’œH¢“¢Ùq¨?,IX'3VÚQ˜Ó D<MêU"’¥òbA²kK$ãþá69%:Z11)Áù"€æu+VÈKZ›];~Æ8Ñ”/ÎÛfÆ¡@ÇC«1Æ[2Ä â=ÊÖyqÉaiQ{°î2m †D< ‡Ù: /áÐaZ,icëòĈxšÉÖy© a@ž­¢’uq(í(DË‚ˆ ybvÞÔL‡%ºhø§èÓôßPJùô´ˆKŒ.@%ÇÊ(•ºhè̈8~} î8”Ž}{“7dáCñtRÊÒ w$Vð¨û¬ˆ$ɯçF}q(q$ßÞM2€D<¨©Ëdzå¦öíU¥:âP6´†ý{7ÔÆ!† â@ ¤ƒR˜{_^¥¢Ž$'âë«»QW}Z O\€ˆ5÷™ÌDáùGJR(îóhœ„joÙ®­Æ˜¸ÃZñ &1Fãy’t$¢]‹=DÐHW„VcŒá÷ñ- NŽŸ«Õ)®@á â@ë·pìbšùæ"ˆx€uúy@ÄlKxV.Y â=-.9<„Ú3R}ûZ;q¸ ñ”÷, Ñ;y>,)h]h ×D<úÈ â*ˆõ’]2°÷Ъ`œó â"‘¥ò’Œ3̵q,(Ia«®v%#Ž$'J#D<•Å,äž1Ƙþˆ$˦¶2´D<Mf逼$ã 1±€$E6Õ”²0´D<­è2í—Já^§ÕgÙ`µTŸ1&`Yh' @ÄÐ’0d瀼tD öøX­”]ƒÕ’R˜˜]OXDl{ÐD<>“v–å$h]‹-IŠX¶†…c]hˆxº* Y9=^*(EºætÊÆÁjQëB+@Ä€ŽK‡»é¡ «¥y â@óE%‡qþ@Ä`—¸#&Ü"Ë$¿Î"låC-)*±ð~o¢ÿœ â@#ù ìË|)a]¾ë ZqÜ€ÂÖ–ý²¯ýAÄ€^—Œ8êâ%Ö† ­’Z1ò1fc X¶FˆxÐëÒ± $…íZÃ"âHr¢­y¶%¨€u§A¢«fm€„AëôÛ¸†E6´¶l <+ãPDsBƒˆ ­‚!B^k¤¢IN$aUh KR ÖʼbcJ;1€ŸÅ­\²ÖB’Œ[ m ­VÆ¡O\€ˆ í]Ã\r-‘µíbTº=¡ÕÊ8dãCñøZ‘¢üš.iãz­ñö„Ö Ö à‰ ñ´[*(1 ~ŠÇˆ×¨7Ž_N²'⥎“´÷=Ÿc^ÿÙ:’‚ÉÖÕ7Fû,1Æ\람÷pòyò\yÂ[c1D< —ý³¸S â5æ­o—,Šx‹v’–Ëß2ýpOIÿ´ª¨§—רùÆcŽ—$ÇÉçÉaåoŽ¿4ˆ ^éþûе‰xMðÎIËgŽ–D¼ÁÝ$]_xýÉ6’öÉ´¦¨¥›H_Èþ;ÛIÒíœ|^¼1®,á~•ˆ"€ˆx K~gÅíN8em›"^TÒnʼnîqIº£5EýJR8ûçùëJ-ãäóâ;åñµ (ñ4*EÄkØy‘׌1OXñR«”^Ä3ÆL—´GKŠÊl(é‚Ü«…wõsòy²pMy±ø‰Ú7—ñ@Ä`2Ëñšô“œhOÄ»I’Jg¨;EÒ˜ZQÔß%érN¾z]4ì´D<ñ€ž¶¯I6·'â}]’Þ-Ùt©$ý«E%IWpòÕi`=M âˆ`ˆµ‰xM²•=oWIš[²éjIz¸EíCÄkĵ~xD<ñ"½lOG¼Í$麒MçJÒ3­(jS"^2Ó4i D<D<"^ v•¤™%s¤"iô¢•OÄk@L:+céÿ(ññˆxÍ’$ÝZ´%½¾¤ZQÔ€ˆx ø¼¤5ö8ûïYø? D<D<"^s#IÚ°è¢Ýo%é†Võ>¯OäæÂµËOD<ñt}Ä{ïŠßcŒ¸ó¢?ç†+E‡{~ð¿‡uúÏû+߯zñÒS9î¼ÞöŸxð‡ßþÆ·~øç¥íŠx×4bQµÖô¼{R윟$eé¶’vlErxc˜ˆ·øÆ+1ÆüõÂ#N{¬ö"<7[KªmgßÅSïxw†ˆ"€.Žx©÷«#1 ¾3ÑýôËýÒ]2·ï5F3V’&_6$š öm–ë·:ûùJý⋇®”ÝaµcßmCÄk¼¦‘‹òPÓÁî~»¦²¯—´þkÍN³5Äìl-÷¶’fcÞÜ_’tQEÔÑlÍo¨6ž}/.ýñÍxŒˆ"€.xKÿtðŠ’t¤1M*ôm«ÞT¼ÓsÛK«ýâÒ¥Ï}o9IÛ/(=ÆkŸ•Ösë=?ùŒ$iÊ!ßýþ…^xaa–ÅÇŒÖèÍvÙn ÷Ø×´8â5£¦‹òTÓ{e¯ Í7Æs¥¤©óšž†‹xÏÍ™$I³Œy=û9Æ%k)¢®fkzCµóì;±üç7æç"ˆx@X8÷þ[ïê£j»¼óä½·ÞóÌ[ƒu©<â-yå¡Û™—áhÞöÈ+w â½rܧ²}Ù‘æ²Q%[QOøç•¤µ²]æ3ã%Í,™VâÉOI;~hŒ1ƒß.>Âïr;ÌÝB«þø}c̲‡¦—]DjAÄkJM#屦¹«¹}õÛœ#i¯ÍOïΛ7oÞƒ’¤“³‹o½kÞýéôìGŸeÞÞ(WÆ}5Q_³5»¡Úzöýéøývš\ò$âˆø[扳¶q¿³Goyâß+F®¯Þ7w)aâa· Ôw¤’ˆ·è—Û¹/'?¸Ã=qæ¶Ù »_öv»#Þ3vZ'ÛË^+­¼ß9Wû…Üxó»s;=¿²¤ïç^Í‘¤ãõu¤Ñ¹Õ¹®ÐÉÎ_Wë<ŸýsÊý\׈׌šF,ÊsM÷Íà¸W¿(­ssË.%$Iæ_Ÿ>i‡õ³op7m|ÐIÒ£#Qg³5¹¡:pö½yãìMŠt"D<À×ïžK~5ßúî!»¼qÔxI¦í=Ó´ù£u©(â ž¿Ra¿ ?«x¸‡w/>Øø³·7âcÒGI’6½ÊùïcŒyn+÷Ãl»Hù%Iº#·ÿK’4¹P2_ôµÜ«÷'HÒ*ó‰D";ðìÃiÒƒùÝŸu‡D½Õºˆ×„šF,ªŽšþ¸|¾™GkaEÕñŒ1Ëq#Þ9£.͘¹[JrR#Qo³5¹¡:söež8vÕ|sýœˆ"à_óv‘4îËW?ôò¿î¹·„f•N;›¹xIÓoIc–=ü9IÒõ©ñÞý¯Ò;>ç=ÚûûI³Õ÷Þi•ìN“ÿÚîˆgú³ÿr~ÁÔÅÙ~ºûò=IÒ%¹w—Ž)]+&¯Ûþ¢û€ùK+_*zéfÚS[ñ­iÄ¢ê©éÑ|hØsI]EÕñ²ÉfƒQ7cÌÇgnóÅŒ\D½ÍÖ܆êØÙg>8;÷rôSD<ñ¿ê[YÒá¹áq?p¿¸·Hí1’¤}sS*,Ù[’t}GÊG¼g×-ºý§ò£ýmŠ4õ7 1fðÁÝVz°ÝïIÒŠ¯¶,˜*IZócŒ1‘$”wIŠå_î%I÷æ_Þ'Iúfá`Ï*ô÷Gî…”‘Wzo$â5XÓHEÕWÓ³NîLØéíöFzxûêHIXÚÉæÇ'=&Iz¦ðwO¤Õó/_“´yñ±?vŸ;8·¥¯±šF*ªÎš2—-Œº¼¾­oUIÚºdSõ"êo¶&7T§Î>×ÕîXŒoñ@Äü袲 f̼l7»Y¾SÙ6;©DÁ5’¤•—zâMךÿ—ßöþzîÆ/ÿÝwÖ‘&”\WùSöh§u<â÷òÅ÷Ų_vAÑ’•ô‘·J’.ÿÉå/NþJÒÔÙÅV–$í×îˆç¡¦‘Šª¯¦%ß”v9wL.äUYì¾EoVɦêEÔßlMn¨N}Ys$Ik-#âˆøÏ_FKÚ¥ô+zj¶—M•ôŠÚ¶h—GÝMqGÊGâM~¥x·ǹ[Ÿ+lziŒ¤—Jvtöh—v<â™É•{¶³SÆÌ)ÍÂE(þ[’>•­kHš1g¨3Ûñj¯i„¢êªiîzÒ^ƘûærÆ:ñªÑ@³µ®¡ÚzöåÜPm0D< ƒþ IÛ•mœ¿¢$iËÜëì쵋vqçjЙ”xÏ•îç΢¾Â–ƒ%mQv´kr“jt>â¹?Pºñ­sM»|RI'ë^rÙ¾°Ó›’ôùÜ«ÿHR¸®ÛôˆWsM#UOM¯N’÷IìÌO²7kGµd¯z 4[«ªÍg_>VN¬2Y2D< sÒ›KÒå›o#iü(íÅî$ûÇ xG{<ÒpkÔ~ÕÝzqá¢Î(I'”ìÅlÄ[qiÇ#ÞI’¤ßmÉ<öµ±cx(㎆Êw²ËÜ%à ‹wúªJ6],­õšç¢"Ðr»»ßïi¿%Éy× ñj=Ro÷õ»þ‰xc‹î#¾·AÑb w»u—÷1&}ët-—K§ƒ¯c–®$iÔîgüòއž|áõ†N3»l×l/;îˆû_\òÒÝ‡Ž•¶_2òõ“ ’4v°5X”皤óŠ·Ü i¥·Zbe+ïcÌRIÒ^Ѝ·ÙšÛPí=ûæHÒš¿,zlã¾ñšð¸÷¢"Ðrîs°:v„Ý2‡I’n4ÃG¼äí‰Z]矈§Uò3ø/ÜUÒ¹áK¯á¾¿ÑÅ÷_{ò­Ð·¿$éü§nÙá"“_d¹5&oÐU ŠÿÊj…wÇŽrÃÆ[#Ú¿¨–¹[RÓÈEy­é­ $¹«°áµ‰UÖ©k(9\(IÚ¶dÛó’¤ ew4«Qg³5·¡Ú{öMswÛ.Yð÷+hƒ§ë( â-7ÇýÎ^ù£ê»Ý Ir–U‰x5©Öˆwœûzæ0yþ§‹ÚñÆiÌ!ÏcLæ‘OK:³0@ýGÅ}èFÏºÏ HÒ¸Œ1Ë6­´ÛŠgÝ•|êSeïî¸`ä»${Ýô¨©†¢¼Öôòº’Æ]™;ÅžZ˜ù­¹ÉaÁúC¯7¼Ÿ»mNÙL!Õ‹¨«ÙšÝPm=ûrWüF³Àcþyôù÷ê) â-Ë~gÿ¢â»îìИR>khÄ«íH5G¼ßd÷ÏŠ‡û$°ÜÛíŽx‡}[ÒVŸ5{+I“ÿ\ôöà¾ùÞqô·óRv}Ýq0ÆóÂf—ÙݵèI‚ç·/~gÜ÷FX44óQ≋ò}÷W¯{î2í­©†¢<ÖdÞÝG’¶¹üßKÍ¢{#9±¦'‡%óîûáäì'sôO¾1`ŒYxï9“r3pÕ_ß*þE¦z^KlÅÉ×Ö³ï®Ñ¹½VØù˜ÿ™&mzã`]ED< å>Ì~gOZXáÍ7ךìvrw:¯ZÄ«íH5G¼—²]ɳËqÃÃoaÄ;ÒÜ6%û¡6ù^é5ÄôãÝÔð%÷¦Õ9’¤ÿž»Fô£U+õ²Å%,»:ۼʷ#}ÎyCvo›kª¡(o5“¹#;syIZ÷üÍO–ÜYƘYeÛn257Œ×[qòµõì‹mT|Ów·¾Á:‹ˆx@ë}6ûu]a-Ð¥3t‹û§cÜ}J/`VÛ‘jŽx™ÜÝ£J£ñnvÙó–ö²fàžÓŽ8èÔKÿ24v¾}å1¡“¯È_Y|`ÎAáÛ C®_þ¤xß÷gï¹ý´ ךPè"KkxùòÓ8(üÃǺ£¦šŠòZÓÜËüìÔ5§Lßûü–ú&9T/¢iÍVCµóìËüý»{vƒ‰›î:ë”»×_@ÄZïÊÜ7þíC¾ÌOÐ.Ùîd†»Ë‰Åo'Ê#^MGª9â™ÙÙ£­1ô¡Ú­^4X;ãP}–†µkѬcËRï¼üć®&EÚò­Ö’š|ZTWw?4@Äšiñ¹!8eë¤Ò¨§²ÞÐÝ%Xüþ£î¶¯x;’1fbÅéP~Vþ@î3ùQCå ^p´ò›ÝÓ˦vÓZ>î‚ÏéÀîM>-ŠˆGCD< çôüÈšKŠÖ¤ö’ŽÊ½ÈÀ™P<— î¶/x;’ÉN2+½\ú1Îw·Vزcîp3“%{Þ½z•G-ý×Ë.þ¼tO¥7îÓ>]›üZ†ˆx@Πò£s¶$w;5óÇÏHk½“Ûg§ì…9ï—ý8»ikoG2fYv—²IOv·PØroþhëý®p›÷ÝÙ£¤=[ô Ðü^6ó%i‡Šï<[Â>M¾-ŠˆGCD< ï¡Q…!ØSæ\÷ÀÓ±ËNÞXÒ˜󻜔›”á2÷õÀõÛh…즌1æµɘ²;ýZõ²©Q’V~gèËkÏe”V$ÿEÄ£¡"PœàÎ’£&ÞU²Ç?W,yw\dИ‹ŠîÊ&j=RæŽro|û“Âæ—§å¶n>·hïù› Mxû&ÛÞ!e$IߨçˆïK’¦Yó󅙚ÝÑäÐ@Mþ-ÊÆˆGCD< oUš£ö)ŸgᮕŠÞÝ}®1Æ,š˜{ý™Wk;Ò‚+ڼ譵þ炇17œ²û˜ÂÖÑ;œVX×|á)cK·öM™öwHnO¹g]‡ÜÚ½É|æ³EŸ{qì«£uܲŽv²ÔäÛ¢lŒx4@Ä’¾r‡BÈÚûoCwøçÌÜۻܒí/nÎÞ\=2Uã‘rIn¦É¯„Väø¢¿3÷«Þüú“NtHs%IÛÖuÈséuê7Ͼô·wÝvݾ9cœ´úµ™6µl jòmQ6F< âzõ’ã÷ýÜö_9öæ÷+¾yü´=¦Ï<ô‚y…MOŸ´×ô=ç¼àõH-½ç´Cÿ{›_9æºùêÎr—­ïŸÿíòÆÎz»ÓÉ¡¡šüZ”†ˆxZÓ!-ú~vø`àѺ¦ã{!XÖÃN8º¿ÛkòeQœ|4@ÄPk‡ô½MŠ&û[qÊô¾:û÷c×ËbÒÁ¿z߆š|W' ñÔÞ!ýá¢R­ëÀ™Äÿýüü3~rýݯd¬©ÉgEqòÑP@o'Š¢&Šš(Šš( D<tHÔDQÔDÄ5Q ñÐ!‘¨‰¢"’EQEˆ€ˆGMEM":$j¢(j"∀‰š(Іˆx訉†¢(€ˆ€‰ä@Mñ(Šš( D<D?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþëÙ5ÄIDATxÚíÝy€OåÇñ3û0¦1ö¬©$Ù¢Ðr¢…Ûb‰´É­HuÉr•¤n²”®´"*¥I]!k*‘5”}ÍØ†1snë5ùÍÌù>ÏyÎ9¿™ßûõ÷üžç<çùÌ9¿ßù=¿ïcYî’ÎiÜáÑA#ßšþù¯–­\ñõ‚YÓ'½3zð#·6­]:†Óï”mÙë»ìÌjæ]9*ó£/Óšy=Dû4¯‡ê@„ïÛÄn)KqªÛ|œÙ%… þªršŸSûÄ_Íñuj3/%€È®ƒÏs»¶ÄI);üžÜ¡'=íûäfV#€øS©ÃþÏîDˆ?)/Ìúñ«éc^ø÷ãõ8xÔ¤3Õg7«.ÄïRU.€G>tCõÄS¿E©ÙvØÇÕ¦w:Äïzˆ§dã°+ãrÿ$ÓîÝt•ùm@ñ«è •Tï]åTr¨d/…߲噦3TÍvîn–r£uɆ/Ë>¶Ž=SÒXáÞâ¯TެãVÖ¹»’Ìt˜%IË5¥ÍUü\šÀ®–+XñÂïÊ£Ÿp„e]&XÁ×\­É»…ej@X§no=Õ6°„µÈqênRot¸(€3 âŸ×iu‰$€‡`Ä«í4qÇ´f®ªè™tcñnuš¸ôÚ-°y‚F¼'&®¥^»§íp2Œx£&.U³áÞ‚n €o¦Ó݆S Öd%ÀH÷­Ã¼­Ñny¤àXŸFº5ó¶B»å†‚v"€‘Ωùí–£µfúÀH縀OÑ”à<œFºµN×\»éS1žF:ÇïÌÞÔn:ÕyQÌ ékr¤Ñn{•c*¾&€‘nªãÌ=¯Ýö¸\žþÚ¹aù¢™SÆ¿<´;ŒtcœŸÿM·ínGvoúnñ¬©o¿:ü©ÞÞÙîú+Ô¨\ª°»©À|«óÔmªö£ €ùÖÍ’¯l+@x¤–dÙÔúÞ(”%IàÑD@xb¥ð7”M ¼ ûýÐ/f_MaÞµòj.Ë;&@VD¥¬ZÚ¨úf)È_;°6„A• ç­}úÒhC’tö¨Ù3¶uQ#^Ö+ zbÑã—Æ@¸VO¿ŠíÉÝêD@øú1äû?ìqa „¾K\×óNû¸×%ñš¦Úù¬W½h µ²l3ö½ßå,e£lsÖ kš@¡$y£Ñ-^O¾½„‚FY¶Y3»žN!6Ü6.ó³;S d }íÅvkÇ&_C!Qn«í‰-*@\xÄ›Ú'>¸ŒÂYë,Û+KÛD@8¹Ç»Ú+= ,:gz—@ûÛ¦n÷2öŒs òÖ.Ý˦÷Ž#€ÈSý-^&Ð^vDžÊÌ÷4‡;@ä)¶Ï1O#øZ<Džj|íig§@ä}|hŸ— \]ž"oņzy^WžÂÁYãOx˜À²N*8âÝã˜dG%ìò*ŸÆ@8‹o;Ç£Ž €9w¨7—Áö§2ÍFðà;‘jR Ÿf: £ Þ6yÞ쯇ínjjôšmðõ¡rª 7{öSK÷ß$€ÐQ¢õÈïM0«„¦ÓÛ½¼Îug@¸P¾Ãk.?—Ô'€p笻ßÝ£ÀI®EÕyd†æº…Ìò&$6¼\'= Œ= ¹cÒaÕ~OaP¡Vc©%°„Q§uYªÀÇ Lk0MÀæ]"^ÇšY”Âmö ؔ >—°/„'bƉ8"Èn"€ð*s%k² ²‰¯Ø U—'^™¶ä÷mU”|©Aa%ݰõƒOùtÅžÿ/w~Æ@³÷ Ø’F¬Ôó®ìðÈ·ç¬Éé'o³ t°Ã9wÀˆ4aáÆ£yNÙ(½ôvŽFo‘Öû²N †s4†Àˆ4ÝqÎz˜èf§c7c`Dæ8gŸ˜èf²c7oÀˆÔÅqÎŽxBg½ìØÍû0"]év_ þûeYÓ-`${@XÂêhgņ O”5¼7ŽF²qAÝÑ)*íÖ\%lv”E#ÚKâR¦;:È?þö:*mµ.ŒlÕ3åÕtÊޯŴ•oŸô…E#ÜX•ŠâKo-ìÔ^чTvïºFº3Ô6N{µI¡Ü+ÑqšRs›ã `Ä®º·Lú¬~×W iæ´_]­ÚÔÝŒx¥Úö3uÔãÿ|àîN÷=ôØÀ—'/Ø¢ÓÈÆ8«Ÿ”[-+~y@ù[Eñ‹:ä/«žEñ«þp”Eñ›¸¥äo}ˆ?Ô8ä{þN\d@ü©U–ßìc@œÔÓçüMŽ"€ÈnŒ¯ù[•l@d—0ÏÇüí¬bÀ Ð:Ÿÿ( Lžë[þöÕ² E £ŒB!÷·ó¦ ¸QcŽî1zOªÀyXp¼¯>GûM> ¬zL¹ÿÃgÀ‚£Ì>o>H}¦Þ}‹ 5f©™±ÞÛ©wþu ,Pþ«>K ê;EýCPÆù,P*k<d¨ï<ìšæÝÔ§éx #=×=¡ÜóÚDXÐDklè¾ÀÄÃÀèÅÊýf5²`S]ýQˆ}·~ïUïö‹@ý4–vÝk)õ'@Û‹À‚(n…úL½åº×1êÞhÀ©¾ú§»©Ë>/Sïr’E ¨çÔ§j}¢»«®ú/”%€Uá ês5ÐUÿôzÌW®Ðxxž‹þ*Vîon,À^UŸ¬y.NVî-½ªE °¢ÛÔgëNíÞ®Uï¬E ´Vê³µ¯”f_…6*÷µ<ŽpïªO×8Í®ªÿ½¾E ¸Ò{Õçë*­žÎUÿîo˜E ¼ÛÕçkÖÃÀYÊýlJ"€`†ú„ Ð覽z7W[0T:¨UhúÃÀs¥7øUÊm?iÀÓ(KyÚæ›î©ÜòêqF©Ï[GQÕ”—Ag5´`ÄIÙ¢#Ãù‡êŸ¦ÛZPWÜréÃ@­ Ë1Ë„ÍÏ22œäŸ”§ðÔÏÞgQmašEõÕ“þ¬Q«ÂrwéÏ„ªÎ5ÊS˜uÙ_[øPµƒ ƒ=\ìQNºD Ÿ±áŒWžÃïÿò0°ePˆØ^/«F…eiÝïâ §ÄnåIì›ýllV}õÂhèŽxË åªwÍ„ g^dp8í”'1û:åG‰Çª[Ð%é“Õ Ë ÒŸŒ?ot8S•gqæÉÇRÊË û[Э”­ÂÑ*VXî/lvs£Ã)@yÿ_ÐoŽê+WÅ@÷Ä[n(UX>[º>ª…áá¨o,½ë'É·)„¾Ä"€H?,(=-ùÄûï™sõ¹ò<¾úÛ ‹îT}Ý‹š ÞrC¡ÂòÍÂ&÷”4>œ*G•¯d¿•Ô©ú²Ÿ’  â{¸Âr²tôm G}oÕ_ßËÕS^}E ‘n¹!®°en”/£‰šíAþ¦XÐ;¥¤Å„îÈñ基¼€švÖãù;PŽzIZ~OqŸ;{û6œ‡°‹E=%Ýr#‡ ËÉÂß×-óm41_Îß¼(è­ŠÒµ©B^:LöÂÌú>§Öq£ùK?×"€“VH©°|¾ðË䡾g€Ñöµ ç¥[nœRa9JXouS’¯ÃIXe0+â ÷¤[nœRaYúÐíjŸ‡sq¦±üe6° ÿ/–K\ü¦ïÃn,€Ã-èñ–ÙÔûºì%»Kø>œ¤M†òg¸Š ÌU]áljl–ÿ&\IÓ>€á45Àæô‰´NÞÿ+,Ç®½àã@†3ÚHþÆ[Ð/‰k…gàrµoU d8ÅvÈŸUl`®¤[nüQa¹‚pׯn ç&¼Õ"€>’–ŠúýÉìû²?^Ôp>p¿O,è§Ó„[n¤ÿZa¹…ìo× l8§ïw™¿Cg@]+<3yǸAö§O8œÎ.ØÝ"€>{KüÞHxòV'9œ™®ò÷e ô[Iá–».–myø{ÒÀœyØEþ2jYÐw·H—(Éþ쥀‡ÓÝEZ0ÓL.dÚšðh¢kûšD„ iØ*ðáÔÐ]›šu¹EÑÅ\þ&†Ápúkû(‹#j®©üí/ùZïØ·¥À œsÔPï ƒÁ$ý wì7[00=ÍäovTŒåy̓†'öù˾#j`.Õ]š¢> N ìI\£}ø+`p¹Ïß²Ø0Ç3.0ˆæË+ÇŸ·° Ã`â Ø9Ž  NC·[n ƒAÄ-w5„ïâ `p^t—¿ çãgо}Ls—ü£«¹kC¨é¶BLÆ08-ÜLݘ0€Y+â `pÆéOÜ®âapüÿ´Ý{’§Ä.íyk‡_ÕÄŠu `pÚêNÛGapðÑó|¸<žgªÞ¤¬Ç~¿mÆ08åh ûÃàÐ+2@ó;@¹{tÎÏ¢è08ò™¶)Ëâ``¢æ¨ŸžcÕÃàÀï4ø³‚þ08UÔ·Üx<Þ;˜Ü®ëxmœGUÏΪø08j£¿ì³—R#:81_+þ–ìÒ08èö¶Yý`pj«­M}1 Y¼ó˜ø&\‹g Ê¹ù)9 Žø=Û´ob ``T ^|ƒm^_•ßÕNƒã5R™7äÙRM¥uáP a¬í…%±0#•ÎMëÀ·…íÞ0Ô~²»dÀÇ+-2¬~®APx½âÉy/àe{å«è¿!Êgçæ@·q–g´{@ßiì9¹«Dì ÞåÏN¯Ný~¨³ënb†Ú^2_³œæí)­ósc~º`+éI}UW¯DÑΠn ß{›?;½ôQÜ2Íyz;_]°U,Ž!€þé«=O7ä§ ¶’G  oªÓž¦ÅóÓ[ÅÑs  Ob»˜§·òÕ;¸_\ÀÜ=ìjžZå‹ ¶ÎSë‡  /4~”Ýöbùá‚=ô}›pU胨Ï]ÞªÆåƒ öÆ$ …F@ïuuýf©eø_°›éý~¸ô\¥ƒ®¸=ÕÏ ¶ÎæNc}álõ×9‡zíŸß ó öï5 «hq›M½ÕÉÈ‹ëÂû‚ýG C]¡º@O•Ýo$€Û|» ªqtÓþxmìR›pè¥)†ÚŽ ã vZ…?_}Æ^"ó¢  wÚûÚàšð½`w=ùúÁ/z¦äncÜZ4\/Ø ²m䩳ŒúðÙÐ+ïüæô0½`û˾«4Z˜E½ÑÊèw÷-Âó‚}J¥74š¸Ÿz"u›ÑnI Ç öŠS þÛ©q>“zá Û¬×Ãð‚ÙàÔVÚh´2'Šš×Ìøº«Ãï‚=,´©Á䀞"y³ô$ˆ7Bý)%Ü.Ø›’BÛ)Ÿ¦ÞΡÊдÿHÏÁòâ©5Ü.ØÍrjIçÛäÙQЬËÅK„»(”ákìSÁ„ŠZ ÑÔ}Ð(y%¢y¿üï·–þñ§~ÁYªšÆ’þƒg@“ž“žôß–¥(ýóW¿`‡.‚ ÕO£±Ï¢ 9‰ [ôV|ãÞ4à v‹`BŧÑܽÐy%¢å<ǽOú‚ÍÉ^°s\ãæ0ÛM¸4åIéøOÔûó»xKÞQ^°¥s÷¼Fƒ3  !uÄ…-†œ|ãž.}ÍUæ/Ø+uÁ乎¯ˆÎ‡ê{ ò…Á?>ù*q5‚MÉ]°s]J§ÀyZEhBñð›d{UÜ é«F>Þóu*9îùö–F£Ÿ@Î?­÷F,ëÊ€.Øy,‚ URg—¹» kÑ_H¿ó”šÃ¥/ÜXÄä÷6²&Ômí¨às»'†£;÷ô¾ö÷ÿ˜¼`§kÌÛ0IË:¿ˆþÄ熇Áçólqa‹C^Û\úÒ¬Æ\°Á„:ãFÓ  +òJDiåC_=Nüñ9ÉÔ?äáÂÄî:7áòÐ ñ7v—^]Bü£Œ¾_°³‘V ‰ùR£ñ   Å…-æåøÝ{ñM¸‘™ öƒ‹`BÕ:®Ñ|'¨Oü¾;=—É3¤ l0rî¢sºn‘·?P£ùŸË@]Å]öÉ¥…Jâ7î/˜¸`k¬ž·?Rè qÇÀìäEB—çú·›ø&|™ûž¡q²*}_v™Î:Ã; žÉâç¸õs,".м¾°Ûã½Ã‡ï^ÒèbY¨£­¸Ã!y´RSüÆ}¸Û ö>sµ@±˜UÊVN¦@ òg(“ŒœÉ¬†îx’Æ©:vžj/ZõIn#€êÞ÷—÷²ú„ÕÒvÖ¹º ë”0p^j¢F7ûN'€ªZŠ»íÐRCñ÷¡n.Ø»4ÎÔwñ^~4ËæC¨¨¨¸°ÅNÇgFŠ?ÌüMÿ€uÖëe^¤Ó“Îþ v¨æuqomÛ:m‹´­µ…t÷z¥÷±Ggÿ{_¨¢©¸³©FoçCt/Ø:N7i®CÔÙ¿ÁžBÈ—ò¥‰V{¼+¾)^¢wÀ¯éœ'íê\:û7Øí  Ü‹â¾d5PʈѭѺ 7Ñ9MúÛåh­úß[†šÿÂiž°E'O¾È&³´»„þ ÒÙ¿ÁžD… ­“ö”.Þ%ü3ñMøbõ¡s–nqsŠžÕé±”‘ïŽÑGÜæYâ•¢«U·¡Î \"ýì=¥  Dqa‹åqòVþ³ªìµçè`Ew'Igÿû}(/.l‘Ç"˜P1ßxõxXëvèz+…7tzmCMŽ\훳:â7îß+Ý„ëë| p¿·¹Îþ öîRЉ¼°ÅFÅUôψðŒÊ[§vŸú"C‹&@§G\â;¥rmÉB⪑'îíO蜡™X®ñ¡NÏ­ `Þ÷¢¾ãêâ¶W%HÛ¬­óC5E0¡töo°w•$€y‘—õÛY\½uùfƒ¤ì%çGoL¨ût&ç]˜‡èEâNÚj4Ÿº]|®'k±—Îùn& e`³»‰æN^ØbªVû7‰Û_)º Ÿ«S‰h³±b\:û7Ø»JÀ\¿®8,í"M³ä‰ø‡vöS’ öBÓÓÜ2Fgÿû˜Û-E^ØBw# rÄ7á [ë¦svƙ˟Þ3 û˜3ya‹ùÚ»°Ü+îc…ãGÕ3kœ7‹`BiÕã}~‹ÀÊ [ÈÁ„^e犇ñ¤SS³tNN{Ë(ýì· `Nä…-úº˜±ªâÏ äÝÒ=:çfºÙüéíß`ÿ†’¶PYJ^tyž7á 4NÛE0¡töo°w#€§’¶PZ*N¼¡µ= ¯v¦ëœšû-ãÆëÇxxªIâæ‡ºœ±úâ7îuroE§h½E0¡´öo°[@ݵ]—’*îëÛ\oö¥÷jœ‹`Bݪ3GÛ‹Àì [¸ß`5i£¸³Çskã}Óßò„Îþ Ž¿É‹°Ê [Œ10cÍĽ??çnÖ9/+ã½  Öþ öuð$ya‹]ÅMLÙ›âþ–åx.®³Yç÷v2:û7ØÛR àŸ [´52còÚƒ9¯§sZž÷*V´Îþ ÷’ˆ  |Þ4CSv‹ü&\;ôÕ×꜕ÍE<  Þþ öµðwòJDiLM™ü)ÞÒ›pʳÒÜòÎÅöÖ¢ð·o“ä…-º›1ù8¡UL_Ñ9)ã¼ÌŸBØìF[°T [è/‚ õ€ü&\믯¼Rg®Í.‚ ¥µƒÝ‚ô©œºôj&߸ËWÿ«ù1›ö^ŸFýì-)äÜùßQ·½ì’eIEND®B`‚stxxl-1.4.1/doc/images/install_windows_boost_msvc10.png000644 001411 000144 00000107405 12350112610 023032 0ustar00tbusers000000 000000 ‰PNG  IHDR¹mŒ{åGŽÌIDATxÚì½ XWºÿß×gþÏofîuî83wî5c’“ɨ3‰{2q×·¸bpAF“¸ƒ»â¾°ˆÈʲ/²ï ‹Šl²ˆ€€€ˆ ‚"ký?Õ§ººººªz¡Áßó|xûô©sªª«êýÔ{NÕæÝ@ bÕœ9sèŸ$H A‚I.™˜˜HY¸ @ CÀ Td…ß~¼£úÏ¿#ý„õ_Ÿ`ýŒ4üX¿ óËï(Ýùß”Æíüý¸]¤þ¹kÄ?wú¡?Úó‡O ý‘ÐÞ?~FèOHã‘ö!ýÒ¬ýFšˆeô¿H“HýßdcJ#§ ôÞT¤ƒ„>?øR‡þòÅ¡Q¤ú÷á÷)}yä¬iHG?ÄšNè¯ÓšqìoX3#Æšué#¬ÙH&cÍA:ùw¤¹¤>™{ê“y¤þôÕi¬1ó‘Î`ýë,Ò¸Xçþ¹P¢EçÿEiñùO_ µäÂgK.ú†ÐxB—Æ/%4é„e„&b-7Cš„´Ë|2ÒJ,‹)H«HMÕ¹LéóÕ–X_ }‹téßHºXV_®¡tuÚZ©¦¯³&µÞzÆzRz63 ÙÚ`;‹”ݬv³±6Ù#ÍÁúÉa.ÖfBó6;ÚBè«-N_}Oj>ÒVg¬¯‘ô¯a-0ÀºŽ´p%—E†”\o—è×%?¸‘úÑí›ÝIíp_JÈcéOË(ý친Ò/^+(íôZ¹Ó›Ð.B«vùÚMH‡Ð =„V#íEòEúi’’.Ò~,ÿ5HFXk¥Zw ÒúƒHA„é &t8x© GB6’ Ýx4tÖ1¤°ï°ŽÚ|ü&¡„¶œßbBê{¤“X[‘NEbé#FŠ2@:CjÛ™èmgIž1HêÎAÇ;‡$:ìt—Òç,RײŽºGèú½c„² ¹dÇrÍA:冔k‚厔w˃Ð)üSž¤N{Þ?íEê ’wÖY¬B¤s7°ŠÎûR*¾à'‘ÿƒ‹”\ (!XbJè¡i!3B¥fÁ„̱BÊ,°BË‘.#…² {Dè&¡+„*®„²BŠ¨ÄºŠ‰T…d…ôÉ)«Ú6F*»ØJöqµ¤âk=!”ðÄ‘P¡Ä:'RO’ž:c%#Õ_ÃJAzv+•KjƒË-R®·]ÓH¹!¥?ÇrGÊ@z䑉õÉó6¥&¯;¤¼ï6k¹€€€€€€€€€€€€€€€´›Tfˆ|¶ªåÕkXXXXXXXA‹XA¥qª–W¯•A ‡ó ¶Ir,¬ðgƱ Oëf…‹%Dáše…câiS,€€€€€´‘Üi±ÂðO\¢ “÷Sÿ±Âº¸r±“~äbF²Â±qÆ3›õû¿vyF|åjÑϬàšyœ¼”ˆ+ø¶¨ÈH†üˆÌbc1+|<ÛÆé1úؘÖHà…·½æYáÒCz@©Ê÷Ú§‹C“ óáQYA/àQC€+°°°°°Â`zùÕXá¯_ŸZ~9Fß5sÉa¯ÿú‰ÊÿìÓÃ맘øÒáà¿õ&˜þïG†ZÁ UÏUÅM°‚™ a“¬0@q…ÍɨÁ[¾åd…ãFi8é‹YÁ …(ŸØ_q…KâœÌPÙ¸‚š¬q````ídÖbÊÄèpÀ@XaÌêKÓyαHül‹ï§ßºÍÿÙï£Y»þðíëÿ}åçñÖ‹þëäÊß™éýÃÉpšÇÆyŽÿõ×(ü˜ ½—-s:÷S"ýæ6/Zü±üÚyD s¯Õ*c®’™+Ì#3cç©Ì ñ6É¢˜cûþg]ü£äx +1†o‚ŽH×åq¢ ‰#qщB´õ{¦\äÖ@}ÿÈý f…½)Ò…*ܯ VXâ!-Váq• *©¡â1}“‹Œ¨>ˆ3ÅDy/‚ÎvÂYl h@¬p MºX¥Ã'çVz¢„ƒé„™xáô˜íi•´Ú+o8±²‚îç8–@g…#™ô{xÔLx¸†XcA²9Á ÇdßáI°‚E©¸d8bc wa€;°°°°°Â`dFXBÍ>ˆ©{\Ç|°ìØÑùýøÍȘaà±p“ϲOmfüéÂò1×W.´þdü¶U;ëÍrÿë¿~ÄKÍrz*F„³ŸwmÖÛù ÝÿþùN”ÿ_ VسÔkÂg{é¬@8ú Î[‘·Æù?Iú ~?æÂUIæÏ}騯ï’*ĬЇ>ˆ£gáÇ$ VÀc R+é¬P}K‡Æ ‡Òñ0G}tVÀDpÅj2ÖÑXa¿ø‘èƒVVVVxVàz¢A³¬ Éç †ÿs;Â…¿®<3s÷Í/üÿ0aóûÓ÷ÿñ³ý½uÆl—9 ý- œøùáÿú@÷·ÿ(ß8`¶øý ³©ÌúrqGCô¡_hcócÄ™1‡vöql£VàÛ(yB~l#½Bô8i‰xÈ‚ÜØF+[êÙÀ2¶Qü~…•^ø!I›dYÞø`ñ„ƒsµÌ0CqD°d´èóJnV8œ!b¯païs騯o®]¯‘)&ÛèâR c€€€+hÇû>2üõz¿y_÷7ï¯ûÍhIæ¿ùëæß¼¿†Èÿ›¼·ÞÛ¬¬¬¬¬ïm„w<++++++ô+˜(0+¨T^½V€€€€€€€´…æôsR¯```````˜“æ¤VVVVVVVè'V@ bŒv”a…»Õ @œ¬p;¿ @ ˆ“2ó*A 8Y!=·@ ˆ“Òr@ q²Bê½2%•f9c‘å¬RåQIý]?ˆk?÷ÓžGÕ΄Tk~å!¼¥o÷0ƒã4”ÄÉ )èX—U2ºÊŒ™ù—±³°ÞsÀ<ì2ºîøˆ¯>>Y¥ò‹()¹šõö‡•Ê|Kk%Yõæè‹$g¥ì_¤·Ïùr×™¯­q3©]d¡DÊo‘šÛNÛ·z¥œÕÊU®jsÊ´¥ÞV¨°½Y©F‹gSë€5ãDJ´¥Þækdˆã™jlf_šÈFeÎYâœ"¯ ÉY¾Æ0U|þößaƨ™«¡~=ÎA '+$ß}ÈPq~^ö¾S¢d¾òâ¯ñ­Í%ÝIF|°?´D#k«ü¶$9¥Ñ¶ÔÙvúú¨²ŸÕhŽÙÖ˜™zö%ÙŠÙWšüé¹7_³ëÖ—ßH›MºscÃ8c ñ">'6ÌX¤‡w¦òUõß äià@¨ÿÄÉ Iw2”ryú¢Ë^·K˜9’|â£ø¾ê½1zûBJäkà’|Íd¦ä.í=i+Éû$w6Ó'+ß„×q=\>ÑΉë,›olŽ ¢EÂP~[fH¶%ñö ½qÆfÁ(gß¶Ùïá Qq¸EñGû£ÆÌ|OÒ¨ä+rŠk ·w½"žÙªn;}ߢmoÛ ™ú/(·OÈ=¯Ê¶ÓGúΤo—x¯o -GºJ3d—Ru5äò£ì¾õ>±aÔöµ¢võz;õcê#:ö¨€Ñì1ÌøV¥mdotŒôè’ÏaY+µö-G£ä&Ð+÷ÌLRض×f±ÍØÜîÀ |þÚùãŠÿ¢3Kr‚”ÐhjÓ˜gœÌFžãûÕ=ÇYX™£šv\©zD@Ú#NVHÈ|ÀP|Åt™ž£KæÓZx |ô7Ã{ý#ÓŒb²$‘S,_ «˜5/´pOGµÍ\oK«MR§ª•“Mؽgà S¢ÚxÎu&s<Žé­70"J¢·e†¤¼çq½Qh[ÍÑ„ÜT›Ä&¾¯’dô•teÄ9è";–¯\}‹.a{ƒdv Ì¾•îÙ=¯ÒïH+/Þ™—Ò½˜Û…þŽ#Ö„ÚE´í¢í éjÌPã§×ɲoi{ÕÎýñ’µ">Ž‘}Þ|SzC’ƒuÇRߪº™Óé›)ùíÐŒjcÉ¡~kɺ™Ȭ¤ZÊœDè€gT®Þ9‹ŽéÇãÑY°M|<,"j0Û6Ÿ¿’Ir?–üaÃ<㨃}”žã‹T<ÇÇÉôň/YlǹìÑ.>´TÞÛ 6ˆ“â30(>áÓ‹™9Ô_Y’0¥•ägÍâ™VÒ‹å +ÕDº÷:b•÷.$V,Ž{M ôö×8ô­wœø¯ ¡òÔmÜ 3íq²+3Nvexw‹ûÛîæìûVv?«Ñ£<Þç—PsÜÛEîîŸ[½­fÛÕÌšñKQ»%NÝ#uóɆÆð²ßöi3eª•Û±Ò´n3U:IùeÜB¤1+Wÿ”œ€qéI{¡ ñßb΃DaÎ8 ãôš:­Xsú  [ qDÓjìsè튓bÓŠŠ 0Ÿ¶ÐÜíV!3‡þ—ö­òâ¬Yœ#[Œº ]Ô_¿ÛÚ|š¾—|ôbnGÖ¯µöZKäÇï^htÑÚh­µ mÅø#—#»-´¦=Îè"Ú"œÀ· WŽJû6æ–×Ú1FüÍØ÷­Ü>QuW3Êã®þf vÚ r»ÈÆhÛNSï‡`ß“ãÖï~K­'UXÕv¹6ÚICrÇ0aÈ}«ö¹Ã´Ç;–/‡8§~QCÊ”¡U®æ¦ÝòZ7Nzº5ÚൎþÓÈ6 sÄgœûQ=tŽ‹«JØÓçsœqü0‹ÑW€Ø"òç—8Y!æVCÑþæÓ˜»¦2sÈ|âÒ°öj¡ü‚ ÅR3QÛúÝþDŽë‘õïI[)$ £ PªÊ E_5B¼×0š{ÑWk¬Ÿv$^Ü´ÑÚ­Fx5”ß–ér{‰ÊAÛµnÙ¢Û½Q[½h¤_IvºÍ–ÉÁ»ÔBõö-ÚhOºø™Q;PfßJ÷‰ìžWº9™¶$û6Z~»h;ïúv9äÞ WC~—ªúC(^ñn™.9ÀFŸ6Ÿ–IíOêfýVís'šv<µ¡+Ÿ#ù­×ŠðèÔøÝ Ö«t„ó4*=ƒd+Wûœ½¨¯7M²zÑÖFÓ%'&ùÓÐbéID–§H,gܺ…dUn}>ÇñG—OöcŒ¾’ÖF£Tÿ‰A m'+D¥0‰|̳ë)÷™9’|â#M•-É/ú‚âðÝú]~÷#­öãÓ¶î§·‚ÊŸßJžv8NÉúÉVR<׌Ù^\C$÷:‹‹+@³Ú¯Ò†ðí%Iñ‘Ŷ2ãdW†–³ÆŠ(|A¶JÛ.³’¯^ϲo©Õ`ìyµ~Ç÷$û¾ÉävÉm&*ã‚®ãâe©F#¯RëpâßíTmxOR-^ó5•þ ²F7®ŒýIìßöi3ÇÈ]´IytKÚUñ<âiïÛkÉò•«wÎG…¤!ü â³€qå‘9‰$‡ô@’?ãR<ÖŽ“œã¨|ßÎqÎó—ZÉqÌtâd…È”ÐÀ(ЏZ]K¾»‚sÿlõ„ý”G¯/A pvƒ»8Y!<)Ôߺ™è¾fÜLtú‹Oì †Îm-¹Ûw6öhpžã>¦Ó˜:à äâd…›‰ù @œ¬Ÿ @ ÄÉ !q¹ @œ¬“ @ ÄÉ Î~) @ŠYÁ'üNâ‡y•wî?@ 4øTP}¯¨æ~I]Qy=—nçUúGÝS‡(däVF¦Æf<ˆÏ,@ 4è”pçá­{4ä?äÃ…{EÕê°Bâ‡`/ƒ@ ‘oxº©­ÏÙ+îï¸.Ùz{'Ãþ_\³JÉ*Ë*¨æa$uX!#¯" ƒNè°ƒM Òß9§¯^µt¼ãB;ἕ'ìøÅ5~¢Ý¹ÿXó¬€*U²ùÜ(ÃÒ»'ŸWY—ýýE¥YiŽEVÜI¸žŠ‹xÂí‡À -ù-Pµà6°X÷0ìŸwðï'V8m~âºÝWVØ²× ‰«ùÒ<»ªèI¢Wf5©ëD¯,+#Ç'%Øžp{fìTÿ³õÓ­uëÎ?Y~²vÁ‘ÚYÆ5_î­Vc ß.ŽÅÞ=yÅûàE·ó6~×üxJFE¶"Ý k |uÃ祻[ƒ“ãS뫵—-ª.œtÒ¤ôÈáb£ý»wå+Ó®—ç#7·RG‡ë«…f¦y§OÝ;r8sß¾´Ÿw$·1X½‹xXü]ßHlßί,­¨½ìè«pMÂR %"ùFçx„ÝŠÍîË.JHM ¼ž­qÿ{+?*ñNlò„´œø´¼øL †Ç  Qµ¨ò˜Ô¼¸~¼yùßä—釷R­2¿Å´X``v²Â_ÆÎ¢n÷/+ .j± 3[žŠ„·]]~Ý2BhÒuÆ¿ó˜OÇ÷¶=×Zw8´n³i™¸M##ٷκ…d¸ݺâä—äp#ÁÎ;ÎÆ3ÆÚ#úª{”f÷ï–ý6¶^"D¦œºâíš¾y¿5kID ‰ ¢«V"¶ÔÖ!üf]âúu!HÊ´‹@Q[k»ðü¥âýûÝ×®ñUï"ž˜kfë™|ŸØ«UõþañÎޑʰ}CÜC3 Oyy„¤*¿#“îG$`yú…å–ç=ʸWxÅÎÅÕ;ÈÃ7ÔÍ'8"1»ï¬™pQHxlZXtJHd2rê‰iÙÂ…¨*T!ªU~3æVàÍøÈ¤ì~e…䌜G5\:xüÜ[©V™ßb‹ý:Uq|!°üâj³‚ñcºÁÏ ¿¼(o÷oÄC¿Ñ“V¿©î5·5†#»:æóvDDa͹'KŽÕ̇°Æo½­ÆÕMGDÍ."Ž4u±¾:wºqIž—銉 /æ’þíOæ+~´d­$0 y÷K›XA¡áeÛlèÉ“·¯ÑõS#¢`d”¾gWê6ý°ýFakto¨}¿êìç—|·´úIãöÝÇ|‚.œXHmHwO¯­w\lzᆽ—•Ç„÷Kª >FoEæW8¸ø&¦Ý³vt¿ls-)=;:1ÃÂÚY=ÿ•ŸQ,…;±·|#m=PµæW]nD'f†¾%NËAU9ºú¢jѪZ;zøFù‡ÆE&‰+Ï(FÛÒO¬`|ô ]8§ï¬ vµJ²Â—ÆãUÂU}asÞ¥I“.æ¼nV½;¬p3*öšÍgëÓXÈF™¶î V@9<¬@ÁÃî_V ù««¢Ž$„ Èv³K/r*ݽ]½­í=¯Þ_¼Ö¿ìªièTÏ‘ËãHmÜç]ݽ]=oÚ»›ßtãúŸ4v–V¿T¯ ¥÷]Û›ý‘:_z ]üÜÌ##ÅVï°XñÕ¥Û¯²Vâãý±ÂI“:VP¨xÒûŠ­‹í5»ë>fVŽnÞÁñ©÷4à¼SL-ì]|QåˆÌ­œßõ މLÊF+`i{â ”ô¬uBJ=TÂÊ0L@¥ÍíÀ ʰBsàV¼Û&ž+¤çl&¢;êdwÇ‹ÜEÁ½Á¢6¿†jûÍßm8söLDDÄŒ™3ѯþ"ùuy\è÷±ü}…Ùö•Q{ž«ËØ$j¾Z9¾ð®yPRa§°§µ£çUk÷‹×Âg/»j:#SòÕsäò8ÒÛäâè Únx¯¹•Ñ=ÈaÊãúµYÁßÝ¢í¥ =õöÂÂ=>×/z8œs±>åhyÜÎìðÕ ø+q¹þ ùøÆå¬ p¿¬aÌÂÀ½{¬\á¢ê…?'}¿9|󖈾߻b¹³Úqß°Skw÷àƒ'-t7*³s<Â2ñ¶tvu7¿î¨khA¬@߯³>S·¥;‡ð¸sT@X`wÍ ¹ó«nWì\ÐÝ?B[gOÄ g.Y©çŸnE"!ìðð½‰îø-®:#< bE)ùfVÎ.7ÂbnõÝs£JPU—,Qµè#jÂÌÊ霩5j5V@½ÑÊ8uÿÐJÔǾ³‚ÚÕ*s˜­¶üÆ8é'•pA†$¾ s+.+ÈÂ÷þr{ÂÄÍßOV*¬àjwAÔî'|$|uCÔäV™gjll¼|ùrD ÆŒ‰¿’íb…»Q»[ÊvöÔ~šüee‰²ëî~÷ùƒöΞ׭Ý/(4wüÈ‘³²ÂÖ§þ2v%ô‘m¼GÚž¸"P@B ÒÐ$D €ôèI{~YƒzMÜp5mkpÅ Ð[o/*=,*ÜÎBV°·{‚Xaç/¬ ‘ÿdÔ\ŸíÛï-[æ¨|Dáõ›Î-úwæÎ=¡ûí_v§-_æ´fcØ’ÅÖj_ăcî™Xì:¡ûÝŽûO(swöOAkâ’ÞeÝ|2èÉ>· ï„çtVØëØv§ùŸkCrÙÃ6a±òŠ+îÝ/M»{¡RàÍ8äe}#( 9yQ=ÿä~#!ú‹j³¹v#8†ìçŠËx`eçêèꓦ VHCUYظRCQC¨9Ô(^sfÖo=®àäâcBKèãÛŠ+,½ôb$=÷•Jâ++Ð?Òâ “ÏæŠ?Nú~óäa²94È`.%,·×ü¬Ðü:tË02x Þ:bÛ¶LAõ䞟‚Š a¨zYÁÙúLÏ+÷îW7z_ºŠž;T¦þŒÈ ®®‘ÁÌD\ý¥r´ë™IߘÜö—^ÔPylP›„uÏ P(ÒÎW |9«GÊŽÙ“x@ôܲ>y:#•Wžd› iƒBÝ‹ÎêgêÚÖ´gäשׄ—ó…–:G 1¦HQ~—nz_ö8ççrÚÛñ¤›Í‰kWŽ:X¶1=Ä VWª‘§Gà w˜î¶qSê’%œNÅÉ¡$&Zxîl# hñ•R6nò^µÒe«aü7Kl—­ö_ðµi_nßqÍ–=ºÛÏÛzÄ)SÞÎ;®»§·µMØØÔVU÷ª¸â¹sÄS´z—‚º‘î—½ÚbñtÜÚ›c¿ øÇJöîäP1(dfݺ“•ŽèÝ‹#¿åæ|ÍÃßþº7?Ϧ¡ÅѲNn¾¨Bä¶£SÉÇLâÒ‹¯Ø¹8¹ù…ÇeðÔ¬Œ%†…Æe8»û_ºâ„ª•Ô_Ÿ†EM£8uÁ’7*Ù9H39×78 ;uô‹>Ȁ˩S­pÕO±WµèÇŠN½ßGV˜{úß?„lB 0óôøíûëÿþ§Ol‚š¬@¸FtÇŒÿÒ8 —t“Ø)Ã9LV Ü*Kh¡Ë*W#ÚEç&‹!I¼™Œ^ C;3è¤ê/.ŸéxåT× 'Q“›è…ƒóY'«ãˆ âââV­Z5cÆLû+gfˆYçXž; E¬à–Ùúø<"awoO¨³»·­³§EìÈ?_bðôyéÈ«Ûücò¸: çòâä-»OX×ÙÕÛÖÑÓÒÖÓÔ"llîzú‚QQ×^ZÓVTÕ›Y¥^Žç^?¶ê©·¯H3 ñ±è©8)ª:#UåIÑ£#¢’ý¢âŸò}טgbÓôRe|œ(*R´EÿΚïÒ¾Y—8['zʲ›cŽšë#…”o×Ä.\x™k5¬­ ÃÂÚ÷ï{H‚BmsVáÓ/WDü¼3uù2§µ›Â-¼<ÿïysO«w÷/7vz²ælÕ.Ëœå³uŽ[ç9p¤Ã!Ý£Ú;„8¢°Ó¹ô»"K¿**¨ÐüFXVÛž^ðÚ1èÞß—]ãê&@ q¯0õv^Â-b`ÊÁw±¥UõÅ嵈$,m¯›Y9ªáŸp§ZÜôŠÃÅËv”ÏŽHÎC™×=Âân+éÅyü7ªUué²}LZùcI=gf=qÖ\áÉ¢LC(\upS/€*ç©_a\ý4h—F$åö…¾8ú ÿ½åW AA1+äÊ bÄ92]r9ìôЮ΃rJöAx$šÐÌ)#dö YŒ¿8‹+´<ÙYUôÒ±"õØu‡K•·ìرCGGçÒ‰u•w]:¡Kåx^·Ñ¢>gÿ”–ü;1¹ö95üdÓ°Õ²AïRCQéÄ ÕÈ‘?EwümÅUmá¹ê &@²ñŒAkè-8ˆ(4·t‹n\MÔ6tT>mßž5Yàðùå­ eê5ábsúUùEQµYè óÌX3 ¨Þ‡¼Í¢ìo»Ó¿i›[æþÙnƒÁ‘)¬•œ9]~S(òôè¾lñæü¹ç'MêªÜ»çÁÏ?åcPXº2lþ|Î`»™ižŸó>£ûˆ6Vê¥ü{EÄß¾ò·(àûm±ß,±]þmàü¯.Ìœï:sÆa5.â¾qeßY¼Xsª|Î×…[íW%¯8RôõO1ûL£²BsKǃʆ܇u™÷'e=:åBõntbFPx<ÊAnéfL RHdâݼ IéÙ\‘|55ˆLC>5¤Ì3“üþ?3‰ªB¢jûÒ³ °! èÎ>.åÎe›kÞáî7B\¼‘šà ’FÆ6¢&òTâž ßP$ÄCž~aΛÛpá‚2¿ÅgF© Šû r™ž^nX+n¦Ý¬€Ým(É x$# +H‚ ïÂÀŽÁ‹ Jþâ,®ÐôxWíEÑ3sWÇ‹ÉQ—+“w0ÖÓÓ[µJ'ÍCG”»:Íy>•“¨E¬`çל2Õ#,³ùM÷óf¡øFŸ$ä=FnBYM;…û­}aä®Ð"P@B7²Ï_ ( UÖ·£›Z H9¥oÜn«×„ó“¦'«Ò…x›õ>2ü@PBþVQöºîÌåí‰ ^GN0ñä?sìG®JŽyàç+ruZ_mýfÃJ:[7ì8F€Âаù‹ýærDÄ´qÏͽÁÞ¡ÆürÙ©³…dý¼'mëöø5ÃÄŸ¹sNNûr?’q#§ê5çžÌ5 ùR×r£‘çr㻡I%w¿ÐµT¸óÑ^¥kŸU>ú9„=½ M‚ŠÖÄœæà[/2?ZhÅ5¤ íî}䙟Cpx3ÎÍ'˜Š( P@ß^¼lwÖôêÉ󗞺¤V°½æƒ\lPTš¦ ªP…¨Úþ{§Bdrò܈¨"ân!G$àÎWï S,ûé9T­,üë„E'#tCëpÍß ”ù->ÞõžJ À9¶QÒ‰ÐLëM/FÜdo‘ÜRÓz%ˆ¥HÇ9ˆ\¦â>i¿ ¹uÄFé™AŽÀ Ci¼‚ÕÅãU&5G].ŠjN¿É2¨Œû¾2fceÄêçqË:’ÖN+vžð㺿Z^0ây¿CÑÉóÐ?:û§44uÑã ·s!§‚ùƒÇmZsÊÞôÈ'$ÅVëºG€³W`dºf}yLZ¡«wˆ‹Wj¢?X!4ö.rÏhwÑAÁÂÚùâe;eÆC¨'¡QI¨EÜßáäækëì‰A­z¿ÅŸô£( À»†·º9ÞÅÔ¬`qöPYº‰»“©¨þ¼¨Ø@t_OtwUwÊüöè/[‚Ç7y}\qùw‡ô?Ô7ØúæŽâïƒÀjô·¬¶Wˆ»UÈ\c©Í ¯[»r>»SXŸš[çqÄ­ÊÀ„2輻.¡EöþVÞyç¯ß;íxW½&¬Îðw»X¶#û²+ÓìW‰~~ê¿}þË¿Øîýpã7£mœø®’?ÿ”{Ù¢ÂÇóýþ6i¡ø­›qñ)wÓnç,Z¶fò¬ëŸÏtžø¥ý—_îåªá§Ÿ’OŸ+Xº9nÆBt×[X5sÞbñ…C}œ4AÿBÁì­þ“W^ú÷‚m±éE–^w¦Ìÿaâœ-glU~+ö±c¿õCúÇJ÷¿/uúh‘íè—ÿöÕ¥¿Îe¿Ó O¸‡nU‘Ï»`a‹|¢ä(à@Cš`…»¾¡‰Ñ©ýä\Cco£&ú­ò»g.Y!o}ÎÌúôÅ+§.Xšœ³@:vÚ´ŸZD? ª5„„ZDí^±sAëÀ Jþª‚BÿùÂé,¯V .V¸|ö ³µIEÌ/f‚ûæ‚ä­Ìi—±gtò^ÇþríЇ«¿—ÈÿÞFz„ÆÞñÌÏ ”//¨h½“÷(í^iRæƒØ[…Iùj“2]æ¾ãBnÃÅ/öüâšUrVÙÝ‚~`ÿ¨¬[Ù¢R‹à@ Ðà…Û÷ç•<á…ܵª±Â5ÿT¬ÀØì´ìGwî?@ 4Hu·@1(„ÄçRÞŸ.NVp J@ âdϰ; @œ¬ðéìHSïÀØ`ƒ 6Ø`ƒýnÚœ¬0öKÝOgoÂl°Ál°ßY›“Ä4A–l°Ál°ßY›“>›½é31M`l°Ál°Á~7m¬€h‚Zl°Ál°Á~m¾ñ @R`ƒ 6Ø`ƒ 6ßxYL]`0íe[N@}ׂõ‡§¯Ü´6Ø`ƒ­ý¶ ã¾úÖhéæ“ ¦´Pï(Ð:Ø`ƒ ö`¯0ç;$‚,ÄÒ7ß4+Æ16Ø`ƒ ¶Ú¼ã$¥±½d“ ¤Y1Ž1°Ál°µÐæ¯ K‹7½ãÊ)|DWß+Zl°ÁÖ~[A,m<¡ŒN«©K۱ḒåûUZµ2C@ˆ(ƒ²åeí޾=uå=}D™è+z&Ð:Ø`ƒ ¶öÛ*ŒWX¸á8Ò‚Ci5=="qª ·Á™têÒ~Ô;ÆóO.ÑÛE©§§ÑÿÐ1¥–R±!³§ ÊfÕU—›.XÞÀ‘>¢LF1 u°Áì!5^p½z™'MØÙ걂^J½eºs†äݹz¬@Ç.PÀ¬ä6Ø`ƒ=¨Ç+lÆÂö½c_¯Ìèy`ºþ(²)}}è¾ãïE|¬»õ#þ+.I欷ñ¯“$î=Àß².ë,–awT¦I…d±2=5á6’uhô·CedÔI§–âjHú‘ü‘ó[úÆ11p}ä/ÊPe+S?WÍŒc l°Ál-´9YaüœÍHˆ&°ôõzäž];òž{øãüu„Ãΰ=Šìn6¢üùoU?¹õþ»ŽÈŸ/±å ¶e%9ómôHj +¡÷APõô<¸´ÎÆïI£ßAü‘0ÈÅÑWÊ4D|‹*¡­*íÛ!/«ë¤;G†Â¨eP6N^öÁ%‘ÁZ€qŒ 6Ø`ƒ­…¶V@4A-3ùf‰¾²-CÀWçº}í"Ù=Åq#_l3 ®ee—"[”ýHiûÍ´&Õ7­e¾ZÑÓàg«\C2#!d¾}du= Ia1ù $žò&>b!*Gú(_†qŒ 6Ø`ƒ­…6ßx…ñs7caû+äãiš·Æ?¹UãÔê'©†k9Ȧrùb›ip-+»ÙœìGéjØÔRv•QF¼z ¾6*7ÄÓ 9~…eL,¼Q±+×ÂðGdˆqÁ›QŒqŒ 6Ø`ƒ­…6ßxª¶I'šå/ëP‘KîI·!ܪaXCO–¿”K^#ɧAœ©`Y¥YäŒƇe·)î‘RoC´o4 PàÁÆ16Ø`ƒ ¶ÚÜ}rd1oÍṺV¾Oz$±ú_£CD¦Qj590°ø‚î!âã“Ômº‡¶…5à’ÕYÅ8¾ÕCÏáZ–¬VbÓ[¡µŽ}¼¸0"T­ £Bž†•#ÂØ&û-ˆ!äò5UÐ:Ø`ƒ ¶öÛ XNs‘OÕnÍ¡È@ëWuP ±‚¦ªZl°ÁÖ~›¼Â,lÏÑ=¤åšm”Rý$ÅàÛƒÚ¿ª ,Æ16Ø`ƒ ¶ÚüãÈrØžýíAH³bc`ƒ 6Ø`k¡ÍÓÁ$‹Y«‚@šÐ:Ø`ƒ ¶öÛ XN3u€@šÐ:Ø`ƒ ¶öÛ¼ãæmÁÂv|f ¤Y1Ž1°Ál°µÐæ¯0aÞ,lƒci\Œc l°Ál-´9Ya¼ï‘M` ¨XAæl°Á[ m¬ & `P?²ýl°Á[ m¾ñ ²è'oqöŠû ª¤qVrl°ÁÖr›¼‚ Y°^냢Ӯ89[\¿~ÅÁ骣«¹³ƒ¥³cxRvú7К0þj–neWì6‹GúåbìŽs‘†§ÂR³ó~[¬ä6Ø`ƒ­µv_Ç+X9:÷äÿ ,0è.ØÞÿcOÁvôÑßkŸè«žáW|ƒâï©êÔEr©»»[%\PÈ ;MãÎz=ºøØ=¶.âvCFa“înWpÞ0^l°Ál W@X ¬ é¨ í¨ î¬ BFד +ÇkEù‰”.¨Wèíííééé§çÏŸÛšŸ +21«/¬`x*Œ`… *‚23Ššmµç ãÀl°ÁVe¼ÂW[±°Íz­·tvD4°ý ¥ 8]³tt°pv¾âähåäjå|=(:]¸B¡PØÙÕ…„à€Èêíì¶ôv6 ë–Í ŒHU›ôŒ¼Ú:zšßt?kêªyÖYZÛ>sí9pÞÍ ²ÇØ`ƒ 6ØZhóŽW”Ã6G\Á‘N \ºêì¤F\¡ QBgg{{{KK Á =½Â×½õ=mUècvf2¿°$õX!énéâ­Wço²˜½îÂôoÏ|±Ê$ñöCpÞo‹ä7°Ál°µÄæîƒ# öñ Î×q\AĈHƒ£ƒq…öŽö¶¶¶7oÞ¼~ý:9:¨Wøª·óiO[E÷ëÂcû·#P@úûßþ¯ÏAL]¬>û-²;Ø`ƒ ¶–Û XNq…kÊÄP15â ±·k¢3ª£Ò«ƒ“ª<"Ë‘**Jº_ß6Ý>ïzÚùÄG=V@|@WòídKWWK"¥Þ+>¬ä6Ø`ƒ­å6ßx…‰_mÅÂ6O„â¸‚Š¬€D¬@¯Ç>ðÁʾùIÂçq]õ!O¼:«Ôfl 2‰NF©ª¾É5$dËîÝ€ &Æ16Ø`ƒ ¶Ú|ã¨rØæz‚ ¸¥;û§8ÜH´öŠ»âeî~Á>錵¿‰¥Ï3çœUŠ+ÜL© ³Â·‚„¬Úù›íroûvÖztTÛ·Wš«Ä ôw*à*$ß&@×ßÜ!ªjYº†»ž/>À¬ ¼ 6Ø`ƒ­%6'+È“ÇsÎÔs޾I”_v÷¶¶÷4µ‘ž¾ìª|ÚñàqëÈž¸‚_ìCô×%¢©äQã!Ûìù[æmº:kYÇcÛö ³öòÓ*±ã º»]-]]«ê›1(47‹ªêE!ÉUûvý^|ÀXÈl°Á[ËmVÐG“…>Ç»˜¤ã®zÆ¢Å'Ïü©µ£ûÀi;ºrËÞ(Ï 8®àœOÁÇ›ö®'ÏZнHÌ,œþí¹ŽŠKíe§ÚR‰ïTX´ÕÊÒž` òJš]Cò¶ý ^| XAæl°Á[ mþñ dils±%ókáăŽÝ" /_ ë_tVÕá$ ™E¯•g²rÏLT¡MУ³×óö[¥þx!év^étS()Ï Œw*Ì\{.¥èä*1($—4‡ä5ï>hâuåxzN)8òaú16Ø`ƒ ¶Ú¼ãæëca[áuÿœ] °›Œ+<}ÙɈ+$å¾R•ÎÚ'‰ßÈÔóúMWí³–’Š9nç”ü{ù1õÞñL§~¡Bê½2KKK×!!Éy®!yV®[b¹%Ä~7àÂ@°‚ì16Ø`ƒ ¶ÚÜ}âYHJ+¼îŸ¸ìîÚ›ZºŸ¾ (àpRRnsôÝ&UYá€i$Z*¢€•™]òùÒCœg’À{Ë-?ìÞòÖ-ëVZîžSå:Gt{‹«ånðåÀ ôc l°Ál-´°,^÷œsf¼·@^*9õO´w +kVÔå—TåWäU<(¯ž²Ø¨Ÿæ¤NÉ*ݽrBžÉ„*û9»X ¾|XÈl°Á[ËmÞñ ²dÑOÞ‚Ë©§d•m2òœ»ñ Ò¬µ—¦¯>óåJ“Ï—žºÄxò½ýÄ ¶¬œ°eÑ„ÄÔ ðåÖä6Ø`ƒ­µ6ÿx,l0+hgµ M³‚Ì16Ø`ƒ ¶Ú<}D 1Y¥Á±úƒèÇØ`ƒ 6ØZhs²Â¤ù“Ä4IÀ  ~ãl°Á[ m¾ñ Tilƒcõ+Ð1°Ál°µÐæ¯0ék,lƒcižd1°Ál°µÐæîƒ— ÈBRZ ’¦ãl°Á[ m¬@' …×}³À:ºÎxÕñªÛç\÷“u¾YÞ™º%HGêæí#4m_xJH@ë`ƒ 6ØÚoóŽWøz¶•a¯t’s!ëBfígÛxµïCrnÿɹ]ßšÐDýð”ÇØ`ƒ 6ØZhóW Ëa[¥¸TDt$á„iû*(MÔOO ‰qŒ 6Ø`ƒ­…6O“,^÷½"ššÚÛÛEHM„Ú 5ª«¢ŽPq]{zqÝ’%KTv,e–Ó¦Y–iÜ_¡j†‘}¯­{UDnß[è÷i(ôÇþUÀ @î`ƒ 6ØZn+`:Y(à ò™í" :4‰êšDH*ØXAìRqbwY<¬@}¥Op-¢RU+È-"Ç‘†Ò2„' ÁJK¢ÕXZZl°ÁÖ~[“ãøY¡³‚8´ÃW »©HCVש +¨wC­)V`©JÆý’8$É@ßá ÕHhCT1 +ãÀl°ÿx…Û°°­&+ý2¬ÎÎ rNJi%ïNÚÒrÓ ¥¹e-yïâiµÈÖ)^Žl“ÈFf$3'’ÃËß©Kן¶É ÿM|D[B[[éúK6‡¾)8<ÁÜp{ ¨Ò2_Ë—í§~NV=ÆÀl°ÁÖB›»b!AbI VŽ]h"%ÆQzq“|éФnœÈ¹íædyƒmYªnÖH;펚 k0’®[‡eÉfe*‘DBÛ 2cä¶NfÃü!* ï+Æ&s0[²‚Ì16Ø`ƒ ¶Úœ¬0y!¢ lLî++´WDT$˜åXëDxI/NçÛˆ‘AÖ/“^MyVàZ–/O5Ìîçñ-8ãæ_@.h–dÆ4ÈÆ-X †>¸’žd7œeä‚,X`c`ƒ 6Ø`k¡Í7^*måY¡®]”Þ$Ê‘<TìUì¥a¶$pß4/ý‰^gt"t–èóŽÈ—óîýÎ rÄÀ(&þFf9zNY¥B]Và$®Ybظãl°Á[ m¾ñ ²P’rÚEóŠÛõ‹Ûä4éyU×µ{yŸY±o^ Ò½ˆ3úGtZbÆô ”‹âéG}.Qªg¸L®eyX¡ÌÒÑe/¼Ç.Tv&=§Lñ Ücéµá˜ôÀÓ!(ëƒì+C¹Q <^hl°Á[ûm}t²P’trštšêÄ]ÎÈv®ÐÓO×׉Ð×KHH¯koY›¥ëMs>¢ãÅùÄ$} üØFZ¶t$#5¼wl£},cñ˜BéXds8},ÛsÌA‰lÙ‚òÆ‚2£d·„–ƒKÓ·K~äs@ë`ƒ 6ØÚok~¼‚ND]1"ñÓ’ÅMóÌŠçÉÑ1+Îg¢dm]¬³$bŸŽó;òjBuܯw÷y5€¿_hl°Áû¯PQQ·/½"°áBÓ™œ¦i^Ó¬+–xUè%45‰Ë4µ‹öYëíËÙ÷“õ;ó"cÕßÛ¨+П˜dïmZl°Áìã¶ca[y Q÷·@BK"êô¼ŠQŽÞ¾ôyÖf uû¼*ôÍ+Àd0+Èc`ƒ 6Ø`k¡Íݱp;AbI%`X¡XlX'Í#‚þ‘d€„D²‚ì16Ø`ƒ ¶Ú X µXAô±n<¥Q+â¿0vØÌÁ¤(ÁØHÁûá‚?… ~&ŠD-‚WuP X~Œ 6Ø`ƒ­…6ïxY²VX|´bþ¡òû˦î|Ø+´ò²ùhäÚÂá« ~µ8W07G0-G0%kêb}`…¡Ä @î`ƒ 6ØZnóŽW%‹‹+Œ\ûëù±½¯O»™ýC0*\0œˆ%ÁXÀ C’€ÜÁl°µÖæd…) ·OÓ6¦ô?+øDÞ#­Ö ½MÇDõ{DÕ†¢Gß]þዯ„ M]u/:«:€†RbcÈÞôóéŒ;Y¢î× `åÞÏßúËqú5<¬ð¢ l q]îM¸“JnÃ#Œh½§Gäeó‘›Ù'½•[mŽ·Ú5±î¨ièŒLÉçdëUøµBºÖ²9ß^f–¡ç°JR™>Û)·¬Ý·d[Ø–äj”Q°É 2Dz3nß _‚@ è­¨²²”~MÆßxª4¶yXA¥|®t=èVO¯¨»[Ô)ìmïì•m¸ºkÊ‹×ÂÆ&!ïàa©Ï¦œqYA\Œb.V`VËßèÍ=ˆ«ì€$¬@?ƈì«@oQôk2¶ùÆ+0ÈBV@nPmVpôMöôv {(¼iï¶Øþ9’|;+`üÙ*ÝÏпOÜÔ+ *Ô+äÝŽ4ú}š`¬@gêxuÖ@ è-J>â« ‚NýÍ ¶^±]$(ô¼jí~ùº»±IøôEWmcŽ1”T·ùÇä±²å€e<±¦ú Ø @mV`|V`Ы¨£@ÊËH&]}¯P>âËÍ ‹~@"ÈBl ñ³‚@6©Á WÝ£(´bPh66 Ÿ¾ì¬m쬪ïÀ1†¢Ê6ð\6V Ýý“–êõAHm`…þ`Ùc Ù¢öG R^H Ê–W˜çiômj¤=}D™è+z&ýšŒ Þñ ’ÒØî︂÷Í4 µ¯›’Ÿe{×îNӱ˻_ÓPþ¤ýau{Qek~ùvV`„ì# Ôd > ¢X~Œ‡YÛC¬²‹Ÿ“üšS.ÄPù õš:é`ik W>–@ -Áƒ²Yæy’À…übP8É(F¿&c›w¼Â¢±°Ý߬ðàQíë¶n—<ï£åº×Ê=ô]B'šyžÎtzXÓ^TÕ–ÿ¨5§”°Û¦¼/vÆò‘õXæÚ/ëJ‡%ÒmÕÇ6Ò…xçYAæ#³Ö"¤² ÿ6ɸôM!²{ߨo&˜r>ÅPï}ã©’’ÊHÕò 4 ó4aô)ˆÝ¹ Ï"H Êæ¯?5Ü ‰«fú5Û<}D 1Y¥ûû9ˆ>ø°©E¸ÙÆg¢ÇˆE+N<ýÄîû î‹â³ÿyäûQ™‘ÂSKäX»mÉxFú³ò+ò>¾¨ÄSޏ-œÈÙŸ™TºQ`ú1FŒWx“ßÛb³mØ?ÍóòÕ›¿ê¤}¥yâ¿-yÒœ–›“ÉèÔ-:S'éH>æIÊp”?FÕ@Ú£0ã2 àqœ¿<Áƒ²•©Ÿ«fú5 XNýú~…#F§ÞÎÛçôfÖa£í6Ì<òùîÕ_\ZŠ2³sò†ÿ]D|úú'à]LC‰ô*zÛ{oûÔ‰ÛK_Ä «÷Å™mÃGdIó©22ưO̳Äßú-&PPžª´ManûIwî¶_aa‚$eó(5ä4.‰ Öò_þñ 2dÑßncôèÑè¯jËR“¢ÏNŸ°wÓ}¿³v^ÔWò©Ï®š'` €²‚:ÞDõÞ½™§NØXúÔ ÙX½O· ›‘!ͧÊÈ’ò£Í=ùÊS•ƒ@ *ìúOH ‹ÉwX¸À]>5ð âÊ \<(_F>âË7^a*b ,±=`þÃ>2où‘£ºFAgìãùKB\a°'Æ16³öô4VÀX¸¡Aa (aÅú5y*ÿx\ÑUZ = °Â`ú1FŒW©e'?&ãøSL,7?ln„8“*@eʰ‚×¼a˜-¸ËÕ½S¬À\ _“±ÁWøQ¬£ßYšgiÔŠøß/Œ63F0)J06Rð~¸àOa‚߆N A2¬ {Œã$+áÝ%ÝBž^ŒÌ)æRd¹ñ?Èj ¸Ê›XÀŽSq%E¿&cƒo¼‚¸!l+,>Z1ÿPùŒýeSw>ÄóE\[8|UÁ¯ç ææ¦å¦dÑŸƒ€4øYA棳‚ª‚ά !VØÁ`Þñ ²°¸ÂÈe±¿ž‹ç‹Œ  'b A0°Âdéa¬@oUòWf¬@'‹þv>‘÷HKn¾¨¯„ M]u/:«:€†+0èÎRÒV ®Ïˆ+häý (y„­÷ôˆ¼l>r3û¤·r«íÁñV»&#VÀý5 Œ9©¥)ÒPúÌ£a¤²M–YN›fYF7 Ú¸:® üxM½·ñzЭž^Qw·¨SØÛÞÙƒç–|ñZØØ$Äý•õ¬¬€=" •†@„!4^@Ú;^ASóA8ú& {z;Éi©»ñÜ’òýr¬ÀáðÅ!3ˆKY2²ÈÓ ‰:äËÐë¦!äjf”¡U ,¢Z\!>³@ýª¢òzV•VÖ?yò´_Æ+hŠl½b»HPOKýº»±IøôEWmcŽ1”T·ùÇä1YpËò¢Oç’ßÓƒèKF×6ä˰p[ÍŒ2´UR!ÈãÄ6œÃ ô¶Xéae}WÀ”@Oj°ÂU÷( ­Z„ͧ/;k;«ê;pŒ¡¨²1Ï$gXá¿‘çæŠ0XAÞëóç°×ŒGO%@\+ õûx…¾Ä¼o¦!P¨}Ý”ü,Û»&pwšŽ}\Þýš†ò'í«Û‹*[óËß°°‚4J =¬@o Ä Úx8‡Õoxº©­ÏÙ+î h€uÉÖÛ#8y豂öŽWxð¨öu[·Kž÷ÑrÝkåú.¡Í3)K( {r#©{úØF®QŠdùšYÊHŸàR€ñ  tù€K6ô¶Äê¿yœz­,ºâRîD'fDƧŤä 0+üeì, è¶ö¾_aĈÅ©·óö9½™uØèc» 3O„|¾{õ—–¢Ì으áß¾~Ç $p±W+€@ƒ—Õ42lrÎÂ/4a€Yá—ƒåí~¯ ©4zôhô×?µe©Iчg§OØ»i޾ßY;/ê+H0^¬ vVˆK¹#Ï AáñIéÙ qA³¬@ÁÃÖêù ¨d™·üÈQ]£ 3öñàM!®Àr¦YL_há‘ñ`¹ó8»£ÆÌDä¾Îö6°Bsîʼn“.æ¼nçÉ@ýÁ 1I™ò¬x3îäùËXÆ xbà烀ã¸Æ+p0Žp´HÓ% $+¨Úkù¸ Ÿõãôö=èï¸áïi.ohç¤`è-±BTB:‚¿h/ÿ›n>Á×<üÑDzÇÏÓî!i+hr¼‚¦5Ï$Ò¨ñ¿_;lfŒ`R”`l¤àýpÁŸÂ¿ #&œ .öŠ+Ð-aø >V XÇØ4c@XAâïù}?Ä@ ·Å q·A…òê†’Šº‚‡+˜œ³Lq…¯€añÑŠù‡Êgì/›ºó!ž/jäÚÂá« ~µ8W07G0-G0% æ™|ׯ+ˆYAÆÑb¼wÛliÀQ}*öðÞ¢0å³)?—‘¼w‘\=l‹Ð\>Z„leºñô…ÆëIUPþXmýÉ Ñ–xÍ¥-2W€lbmÒ^Ò ¶Ž32ÁÎ ß 6‡°Ðƒl\A&¬õ?+ÜŒIA|“”Ÿ•äs#(Ò; <¯¸B{XA{Ç+PA…‘Ëb=?Ï%.NÄ‚`,`…wp¼‚© Þ3ð¡ûxÒ‰ÚE8iÂããFøP„>®30&\;ÊY$½ã“_„Å÷3˜CÞ ›ò–g 6Ȭ9ë ˆ›×/1äZWºbòÙÜvE¬²YÒUqˆ_‡NYRЄ>ªÇ ˆ¨ˆBþƒÊ€°Ø;¹î—Te” ¾¸ÂÀWð‰¼GZróE½x%lhêª{ÑYÝÐ1X^ѤâxúðÀ÷X:#GL ¦Û6 çjºMìÔñRÚ`t‹(dê[Ü-b«+È-"¿üµ©ÖÁÏ J÷Y€@ :.P  +G$ J@ˆœ‘ã~#ävNñys›”ÌÜ»y%ÚÆ Z÷~”<ˆÖ{zD^6¹™}Ò[¹Õöàx«]“+àþˆš†N–9©e^ä¡·iÄ—óT¬ îs8Zp)P)V " ¶8– î}°=@aè³3øÞXÒ4.ÐAA V¼‡X¡àáãkþÒ³ B£’.ħÞÊã4õÞÆëA·zzEÝÝ¢Nao{gž[òÅkac“÷GTÖw°³‚vú]`MW Å°ït—g¹x>þjý"=G9 Èa w »vÜ5À[^1+pw‚ð÷€¨0^Ó"qNιÉ–>¢«Bú-°ÔÏã+ Ppt½‘™]„@ñAtb†opÔþç’3r†ìxMÍáè›(ìéí$§¥îÆsKÊ÷G(Á Ä+–É<[üñHF²<-AäH†(*U^2¶‘'êÀ7SmVÆØhˆ N7Ï2¶!ý[`¨ŸY!%3÷š‡¿³»b H‡NœG¬€Ÿ64Ç+hŠl½b»HPOKýº»±IøôEWmcŽ1”T·ùÇä)胙Š>©}<¹”dº)é4Q4 sÄKQìANiÉ»,5í%+àEÔ¯ð–_¦¤Å/€‚÷6‚@ƒˆ(¹zÝÎ)F •„(aàçŽèñ ˜èI V¸ê…@¡ƒB‹°±YøôegmcgU}Ž1U¶±Ì3Éáw1BH£ 2SE‹# 2ëË6µ4ÏÔÕ —ÉÏ‹-‰3+¨5^XXJ¬˜v?6yèÄy-d…þ¯Ð—¸‚÷Í4 µ¯›’Ÿe{×îNӱ˻_ÓPþ¤ýau{Qek~ùåYÏõÈÇ Œ¥Tb…Ë2 *ªÁUŒW€ù €@ w0(xø†j[\A{Ç+øàæáfŸ‰#YX¬8ñô»ï'¸/ŠÏzüç‘ïGe>F O-Qâ™Iš/ǦLÚP®~¬ Ä²ÒІd…pYCCˆ+ Òñ Ú¬‹ÖÞ/[á’  ¼Ð©wÎʽÿœ:¼_A&1¢  8õvÞ>§7³}l·aæ‰Ïw¯þâÒR”™“7|øï"âÓ×ï8ï>‚¸ˆ.àäóVžè"X\üâÝEcÏ1zôhô×?µe©Iчg§OØ»i޾ßY;/ê«·ün%H0^†„ñ|T²Ì[~䨮QÐûxð¦W@ Ѐ±BI…vÏ Æ+Àxz‹¬€@¡æÉÓ~¯ ©DÍ3‰4jEüïÆ›#˜%)x?\ð§0ÁoÈ '‡…‚‹ÂqÍÖßa 0ˆÀ62SÌK{Q"Lƒˆ^–òxÞ^ÚG–’â|¢.%'?Rvo©ùñj(¹ ¸8C*­¼ª{O¼Æd=ÔÚ³îöµ¢m¯ò?‡Ì®#¿ï¥·Hä)±?éßÒ÷$½~Ö=ÌšÉÀHöµÃd2¹v‘|C¬{i¨nQ¯ä‡”?³¤?1ÛyÄú+³.Žmùm—ÏTxʰlŒLÆæs­’‚Ëï1¯ÂE@òca·®¤ hõx„‹VÌ?T>cÙÔñ|Q#×_Uð«Å¹‚¹9‚i9‚)Y0'õЯ yV ý EœB2ù½2E¥…åKRW2S|d]œë'_TyVànH…•Wz×ÑWž¼°ÒëäqðŒ‹2‰jþÒ:©‚SÌ͕ܟ4F“Yaj­è hîVÀ¶lž•¥iZ6…VÌ*™’¤!®½4$·ˆÁ ¬?1;ss¬'ë"SX²žì™ E:(³rò¨Ä±Eüg1ß1¯ÒE/Ž»2 Ó€VW ‚ #—Åþz~,ž/J0*\0œˆ%ÁXÀ WÔIrŸÐËus3ä7y€÷g¿¶>ôö'¤!™èÎ_+hïxŸÈ{¤%7_Ô‹W†¦®ºÕ }bþ"àq‰·Á zú¬ÀÀÉ $mÞŸÌVÿo7ô¶R°‚Ö½_%0¢õž‘—ÍGnfŸôVnµ=8Þj×dÄ ¸?¢¦¡“}Njê]Î"E/QfÍgÁÄ A‚ â ý1„2ù\ézЭž^Qw·¨SØÛÞÙƒç–|ñZØØ$Äý•õì¬@¾ QÝȰ‚°B?W€ $Hj°‚öÎáè›(ìéí$§¥îÆsKÊ÷G°ÏAM-ÿzf¹—1[Òßö,-!Aj"m†iHW€ ¤¡Ã w3’V.ÿ&9>b€Æ+hŠl½b»HPOKýº»±IøôEWmcŽ1”T·ùÇäqÍEÒ‚ +Ð&† M'-*‚kºŠ<(R¿±Â»6^$HÞ:+äg§5w6rÙ“&ŽGÐ0@ã0%Г¬pÕ= B+…ac³ðéËÎÚÆÎªúc(ªlã›g’N ò£¸rX #*@\A¥„¸êË+ A‚'×°‚Þ:]Ê_/øzB‡¯Ð—¸‚÷Í4 µ¯›’Ÿe{×îNӱ˻_ÓPþ¤ýau{Qek~¹‚9©‘ihIcz‚j¬€)PaàXa Þ¯ ÄË ”ª“ã-ýq9“¼ä€¨Wᣕҥ¿º€|`œzÐ\ú2%޽Äùä=ÿ¶+·ëXZÇI¼– žS©öB!™MɾH$û–Ö—°<{)·—†ÞqÿpŠ‹©tÖ¨z,1N.…«¤°~åW@…’ªœ°o‘öïù…q{¿iúA3^áÁ£Ú×mÝ.yÞGËu¯•{軄N4ó<éô°¦½¨ª-ÿQkN©V g–éƒóÈÅ ÔÐú·ª0Dâ }ôpýZ›âKán˜oÝa÷LlÏÜ‹—4y•[„Ïkõ$äÞ~ÚvùÖ%×}ƒÙ¯4ÿòA6Ïʵ]ü/¤ï¥¡·Eýqä÷T÷·Å ʰo=®ðÞ¯ ©ç >øàæáfŸ‰#YX¬8ñô»ï'¸/ŠÏzüç‘ïGe>F O-áeÉ(úȹ±er âRôÇ)ࡈc…¯Àr’{ù Ï{d¥w_ÒW4˜Ónj¸îr%¥¯ã•½ñãqò—É{é×wÒ3É]Ë$Ëq¾JVÞgȾљ¾é½\[ĵëX7“ó½øfŒû]¹ê{VÉ›‹4èYåöÒÚ"åÏ#Æ‹åoÜ"3ºÇvñK¶Û0óDÈç»Wqi)ÊÌÎÉ>üwñéëwœ@RgW©ÚŸ@ ݸýë½Ôý8Ïû˜e_PÓ+y-­HîUð¬320JÊ´.{­W!Ø@4"ë•Å/€•½ìÊÞ~r» y—#^S)0_¬+½Ösî:žÍd} .}jMGìi.VCžU$û‚È¡·Ejõ¾Q~]ñT)̳ƒ÷XâiïÔ–A ¥V‰u‹8VžåìÐæ+­V¼_ASiôèÑè¯jËR“¢ÏNŸ°wÓ}¿³v^ÔWê‚€þ€d¿¡¤>²B¿¾_yAaïåUàðØïðx/gª:Qå5~/E»Í•ÞÚp^¦ó÷ȳ‚lT@Àr)gae6“Ù:íRN÷gÚ±gxÖ¡·EÊÆçØF<à‡mZ&Î>þcIV‰”ÈL¦¥Ì*‰8G¥ðG/43JÛXA{烠’}dÞò#Gu‚ÎØÇƒ7…¸ÂÛf¾Ë“¬ ÕTóv¤ÔÜÀÀ`ŠL§†ìœÌ‹c†¿Â‹fŸRy}W’áØL™ÖkÊ6‰bŸ<+µžÜ!÷¾Gì‡Þ©ŸckN.bÏÏ šˆ+p°‚*«Ä²Eì¬0¨¦äÀn^ɤíóA@z7YaàÇ+Pýr}JÍfÛ׸BŸ¯20U´šwá0U´ºSE+dÆtÉ,Çïa©ñ)˜ÿ¨²J,­s”TaDëþÈ4GÅÔ/ã4•¨y&‘F­ˆÿýÂØa3c“¢c#ï‡ þ&øm1áä°Pp±Wè +ˆØ&Ø)=›­3äjrNj¶‹L SEèTÑ\óÓžÒ5çŸ*Z¤`w OÁ,®1BÕUbÝ"®’*MøþÖY¡¡¾V%iõx„‹VÌ?T>cÙÔñ|Q#×_Uð«Å¹‚¹9‚i9‚)Y0'õd…wg>홓¦ŠDû¤fí¯@F.‹ýõüX<_”`T¸`8K‚±€ ®0¸=Ê»1'5L [iP³‚öŽWð‰¼GZróE½x%lhêª{ÑYÝÐÑ'VPcJíIj¬žÖ¿$惀 $­e­{¿JaDë=="/›ÜÌ>é­Üj{p¼Õ®ÉˆpDMC'ûœÔô,Ê¿ŸQ¡ïT8'5žŠ’ö %ñ(æø`ú‹¤PëC”`žIH A¬qõæƒP&Ÿ+]ºÕÓ+êîu {Û;{ðÜ’/^ ›„¸?¢²¾ƒè/\T#r  +0›˜Öß>˜>#¦ #‡4+¼;ã A‚IûYA{çƒpôMöôv’ÓRwã¹%åû#ØßñL½;‰WÀ!Ùw<[Ò_Ï$-!áj~ùúÆHb©Hú„Õ–òõ‹?“T+'¿“NŠÍh–\1m‘50²òr+ Ž®çEB ® $HïR\A½ù ú ¶^±]$(ˆ§¥~ÝÝØ$|ú¢«¶±ÇJªÛücò¸æƒ iA†)õ©8ŸÊ¡Þâ,W ÈÒÚ$7÷ô£~ÉÚP†ìš\ 7`¶(ÿÞH–•—[Æ’ÒµÒÒ·PÂxH AÒZVÐðxL ô¤+\uB ÐŠA¡EØØ,|ú²³¶±³ª¾ÇŠ*Ûøæ™¤{q®Y%•“šôÁ2ü!¹a—!™¦u°®•ÔåËw(°·È¾¥"%V@D:hïëª!® $Hƒ8® Þx…¾Ä¼o¦!P¨}Ý”ü,Û»&pwšŽ}\Þýš†ò'í«Û‹*[óËÌIL2êNuL¨É ØgKîÚ¹f¯–÷è곂l‹¬SWñ£|ÉÇHí +ÀxH AÒFVÐÞñ Õ¾nëvÉó>Z®{­ÜCß%t¢™çéL§‡5íEUmùZsJ°yç/ÓG ;æ‘‹L ’}¼‚Ó%Óú8ð[„|lûÜc¹V€µB‹gÍ„¸$H ⸂zã”ÉçJ|ðaS‹p³ÏD‹,,Vœxú‰Ý÷ÜÅg=þóÈ÷£2#…§–ð²‚ÜÐ@úc‡\¬ )EÖ¯à‰°ÖÏjÈm,ãÛ™º™xÁµ²9Ò±Ú:¸Æ+@‚ ’Ö²‚Ö½_aĈÅ©·óö9½™uØèc» 3O„|¾{õ—–¢Ì으áß¾~Ç $uv•ª“Lü¸ÖÞûC\$H ® %óAŒ=ýõOmYjRôáÙéönš£ïwÖ΋úJ=ÿ+}ưŸÐB#¨ ½CûŸ`¼$H i+hï|T²Ì[~䨮QÐûxø!® $H´4®0ðóA@z7YÆ+@‚ ’v²‚&Ç+h*QóL"Zÿû…±ÃfÆ&E ÆF Þü)LðÛ0bÂÉa¡ðÃC\$H ½ý¸ÂÀW@ˆ°øhÅüCå3ö—MÝùÏ5rmáðU¿Zœ+˜›#˜–#˜’sRIV€ñ A‚¤=¬ ½ã¨ ÂÈe±¿ž‹ç‹Œ  'b A0°Ä A‚ ’VÄ~¼‚Oä=Ò’›/êÅ+aCSWÝ‹Îê†`…!Æ 0^$H´“´îý (y„­÷ôˆ¼l>r3û¤·r«íÁñV»&#VÀý5 ìsR o#bÕq™ì‹9ækfN±@¾£I6ƒœRRú9’e)r"'æ, ® $HC$® Þ|Êäs¥ëA·zzEÝÝ¢Nao{gž[òÅkac“÷GTÖw°³cªV ¿š‘8ªe–¤¿8‰xÿ2p”ÄÆ 0^$H´‡´w>GßDaOo'9-u7ž[R¾?BÑ|ü¬ žPŠš˜¡O¬ÀæüYWÒÛŽ+ô–šO "za‡C‚ XáíÏÑV°õŠí"AA<-õëîÆ&áÓ]µ8ÆPRÝæ“ÇÇ Ô<+ðÏûÌ®`vAo„dÀ€lìe ɲ‚ãz{# h?ˆB`°B/þL.ÚËQ?ûWZ•”_ÏÁ²E AÒ*VÐðxL ô¤+\uB ÐŠA¡EØØ,|ú²³¶±óÿoï\ «¨î–ËÞåêZt•u»z/½ ·Ô²èEËÃX­‚¥-…¿(EáV €@DÅV©ŠÀ?TÞ* !Q$B’bä‘P æK¼ÿ"PVÿeRŸº\§V`gkuMM¡Ê@÷íÒ/Ôk§õšèrXÁ+¼È1€1+ŒQPÔoÆ _¡ÿþ.\Ì>]ðÚæÖG/º;~Ƙe_þjáÓ¼7‘æåôë÷ƒŒ¬“Ó,#Š=¬ îcþ¼ƒWù¡d–èVƬàýÝ‡Ä âIx/4bk5Ñå °‚wV`Ædj˜Æ+ȵ©ì“EùSpDú±‚Í@˜Xéc»™±_AZ¯‹¯(|dÏ~(X‹T@Åàè´”ÛJ[Hߪp?èÐ/Ôk§õšŒÀH!°‚1+p `¯3þEªÔÙY%d<¹déÔE»V&dáG·p},ÆVPùÛU }¡h-³²ç}óº…ìæ±æñ VÎWÖ cô²ïä&éê¶ÓzM°V0WvL¦¶!+Є&”Ú¸Ü`°ÛÇ@"8ð ,Yûd”<“DõÃÇÝ6æ mÔÛ/2mwí·ýh¯íû{…„“·íÁ&Vàè×@"„”ñÙ,^#‹°Â„¥%ãâŠG¿~õþ—‹h¾¨ü¶ßä ·O8kûm¾í¡|[L.òLF+pôŠk@ agŽÌã^ Jíù†ÂÒv<“ÒGš™ŒÏ:Ù¤l\†jë]+„“T} ñ ¬ð<Çfñ ÷=ö_Ÿ¢ü …¬ îc!èfVð ̘LmÓx…GçS¥vÈÎ*!³àÉ%K§.Úµ2! ÷8ÚYAÕÇBÙÍ ¬`À ó90~!ÖÉb>qHðXícˆW€@ H`޼°KÁ>%Ï$Ñ;'eýð±C·9huÀö‹LÛ]ûm?Úkûþ^!áäm{p㣉8zÅ5@ ae|6WP‘EXaÂÒ’qqÅ£_¿zÿËE4_Ô€?~Ûoò…Û'œµý6ßöP¾-&9©£Œ8zÅ5@ °³Ç¯ 8¢²ÞÒœÔÈm|áúXàñ ®+kcl¶Ø ®-øÇ ì˜L ³x…Q>G•Ú&¬àS¹‘$îÊqºÜÝÝn»ÃÕawÒÜ’ÍŽ†&}QZ× Vˆ:VPõ1“n¦‚ŒXuª²˜µW\AbÕ¡b3„CÐcлB%.áœÍçi”æjp…‘¸¹\U¹qü-vþƒ@BÆ Ïq `¯0ê?ž£Jm+¬H>ˆOvu8]v)-u7Í-©}¡eš7RþÈ3û/áÏžQ‚™É—dÍñF{•3Mp%`ÿXAÝÇLº™ÓgO±‚.y„æè'þUj§8í 5u #rsògÂR%}4) „ˆ˜1™ÚÆï ÄYȵƒÍ ›RuI  ¦¥nînhrÔ6vU5tRÃåŠöôƒÚÜQy2æS¼7}ê.÷I•˜ØØqaº6Ö³ru1ëVs'„uV`WÂÌQb×ÊÍ"ó¡fÁÍê5‰­iâÆ6•WÞt ¡ª^!³[ÆÚÍM\ Ú:2IÀ©„”8ð ,Y˜³‚Ú'lóƒ6l;@@¡‚B‹£á¦£ö†½ªÁ^V×I} …¥íz9©™lÔüïYÿ³ó¸ªÄ +háüâ +pôàR›iq+ü]ºŠóù©ŽW÷Kʼn¾!·¶–ß\È“§Þä§Û$e¯í¤”Z+R©ª[¨;Ù‡xsßXN$|¬ ŒÏ¦ñ j²¶_!uß  UÍMǯç¥Vî\xâ©„Ãç+ë‹«;Š*: KÛÎëå¤Ö7m‹QÍãl X!ܬÀÑkϳ‚´ÈþOš¥U‹u/oÓ­û\\hƒ4Ù+à™ éñUä¡×$všôÊ 1k3è®´Y·077a#€€@ ÁfŽ"(^áÒµªæöԥÅS·'ÏKÚ3rÍö§6Uv–µŸ»Ö–E®®Ÿïy» ¿Vð¸ÐTÔlÂj¦Äœ¤ýàDÐY¡§â¬²‚åX9‹¬ ëа‚ J”p>‰4M²Î ²ÿB%º…¸¹+0/2 H(YÁz¼B,Q‘,b©Za+åF2pà ¦Çìi#“û_·nÒ²Ú!ñsFlŸ•[þãw8UNtöeƒw6Öy@‹hl£Ë`“A]b:ÇËûAlcðXAÕljW𘰻`°‚nÜMî{O±Ç^X% £FôÈ÷ˆ$ïŽîtºS6þlëš!®Ò¹›Þþá+÷V ï#*ëíú9©M9? +pôŠkâ54_öi”ñš;ÛŸiðßBè ™¬Ÿ¦¿[±žz›¿€¦WÏ×»®3 Í­ p?ÁøER WÉ$#Z$0"¡­Ç×<^AE&¬àS¹‘$îÊqºÜÝÝn»ÃÕawÒÜ’ÍŽ†&}QZ×Ù㬠}}¬>Vàè×Dw€3Oëìß0ê/+ô²¬Aeë©·¥ú¥Âòµ…!8£¾É ^¥°ˆOvu8]v)-u7Í-©}¡aÏÇ–Õcö$‡2û³Ÿg++¨û˜I7ë묠úv²6#¥á¢S'Ç•Ažkí×ã´©·uO+³ ¹•=h õ3w[NçHâoùߗÔÛÊ—CuÎQ>ÃêÝ ßtEý<ìZQý”¹Å*Ï‹ËbSNS{= d91}€÷]ïAð³3&SÛø„XC ¹v°YaSÊ¡. Ä´ÔÍÝ MŽÚÆ®ª†Nêc¸\Ñž~°ÀÔ¯@óB1…L©Ìù‰À ae¶YŒWà? Ì=h½°Ðdí¢7ÛéO·–dç¹¶Ì –òs¤ÞÖo<]£éÌ%êBƒÌÝVÓy˜øÛhî·˜z›Ï$ª¹zÜòÔÚÝ ç³Ÿ‡Ýúr_ÕxÓéõ4Ùë©w Óv•ŒrÐË)oÕ#€þ§ëù¼öFÉîÍ:ƒ¯¬À1€ +Ì%*’Å\ªæ¬À}!ÙVذí…6 -ކ›ŽÚöª{Y]'õ1–¶ëç¤f“G«Z!%‹ò$|+D+¨úâ¼¾ƒ”,ç®´´´2Ý\'õ¶áì¢Kº³©†³,§ó0ñ·±ŸÀ‡ÔÛZ¿‚6•¹w3¬gdâW0ÊÃîõ¹w4Êѵ›¦îõôæ]ó9æÀ§û®÷ ȇ×f—5~¨=I\u ½uYa.Ǧñ rmjÛ¯ºï…ªæ¦ã×óR+w.<ñTÂá‚ó•õÅÕE…¥mçŠ[½³‚þ¬ÏX!ÂXíc&ݬ¯úø”ÖÄ Ö6W¥Þö6»˜ï\?s·åtÞ&þ6&Ros¬`ææ±È á;#k¬à£_Á€ôÛ©½Å×SŸÈLæë}×<âáׯÚ,9͹Ð[gð•80‹Wñû¹T©lV¸t­ª¹½;© uiñÔ-ÅÉó’öŒ\³}Å©ÍE•…eí箵å_1fÉsÀ¤œ–ÿ4_™˜·`…° ×ÇFXc…>ïWà“_û¸ÕÏsmå„®ÖÛiU³§¥Úwü\7s·åtÞ¾&þÖÍmæ ·Ó„ü¹›a=#ÿò°›{ØNèå„á-Ö¹ž:2NLŒû®ydKs‰ÌXA®l\hØܦ¿Xä„Ð;&SÛh BJm+¬`¥ÜHÔÔ☽1mdrÿñëÖMZV;$~Έmã³rË<஧ʉîϾ¬û}ê7P/Ò{H*<੬~k¡&HˆXícø¾‚vÔúH¯xbšt“_Û˜(H†Qžkkñ Ì>Íòg¹ñF»Õ/ÔËÜm=·O‰¿]rÔ™e/—%/‹Ql£w3¼gä­+Zv;±·Ø³/½Q÷4µ×Óð@‰é{ö*é>¬ÇÅåá,¯÷]7¯½Á;)ug룰c25¼°K&¬ø÷ú÷ïáÂÅìÓ¯mn}dñ¢»ãgŒYöå¯>ýÀ{Ia^~A¿~?ÈÈ:9mÁ2¢˜E¢‰8zÅ5D«EÓ×BpFQ[5¢î»õ7tæâë·˜´_³xŽ,‚}QLþ›žÝ2qyá UxuæØyŸ¯ŠOQþ‰JVàè× ¢GÃõVˆ´ûFVà âz\2 ž\²tê¢]+²0fõV@¼Iü`Ëñ ¿›CT Ñ ŠË éyVP÷1Ä+@ H‹ÅAPâ˜1™^XA ‹P±‚’g’蓲~øØ¡ÛÆ´:`ûE¦í®ý¶íµ}¯pò¶=¸ñÑÄ l³¯ÀþÜ@ ^YÁbMéw̘ì…¸Õ^hXaÂÒ’qqÅ£_¿zÿËE4_Ô€?~Ûoò…Û'œµý6ßöP¾-&y&£Œ8z+@ H0XÁú÷´_Óx5Y„̯0à‰CwŒ;DóEÙîÜoë'øl¶ÝTÁ QÉ J+@ HØYc€ŠWHË<#Yš|Q·õM]5öŠúN°B”±‚ñ ` *+ô|¼B|_Hò^áèN§;eã϶®â*»é­á¾raú>¢²Þ®—“šKñÐúL.+W¢úF#ÿé&ñ|ÕIül÷µGOª*õ¡¯zÛVüSŸý â $bYÁb¼Â³T©m >•Iâ®§ËÝÝí¶;\v'Í-ÙØìhhrÐ÷¥uz¬ žÎé„m’ñùƳ`zj “¶.ˆ à™Ð…”•F¬ Ù…Ù¶êôIVPõ1“nV€@ a…™3žáÒ=>ú¿S³Â³˜Å+ ÿݳT©m…ÉñÉŽ£§Ë.¥¥î¦¹%µï#ŒY’‚×ìPJÞj¹¾~5·—?Xeãmû<(áúØp°™Îå$p €Â¨‘ÃóþÍÒ;&SÛ†ÿöY¢„&¨A4ج°)åP— bZêæî†&GmcWUC'õ1\®hO?X`Ä ž™_óRâ*çó7LJ©`„Ö3 )ç_7ÈÒ)ÑÛ–ˆ «Ü\C¼›ˆ~óõ1‚d,ýéOåÿ÷‚“©á…²°Æ ܼé+lØv€€B…GÃMGí {Uƒ½¬®“ú KÛõóLr>]¿‚6*Á'VÐNëý FÛ’v¬ŸZàúâ $¬@ôxVÆý1£2÷}¡W`Çd/¬À­öBàWHÝw‚€BUsÓñëy©•;žx*ápÁùÊúâêŽ¢ŠŽÂÒ¶sÅ9©¹· &¶j•oýD¦Î¬nõ„Ù¶ºN‡¾Æ ½‚ $¬`Û¨õøšÆ+¨É"جpéZUs{wRAêÒâ©[Š“ç%í¹fûŠS›‹*; ËÚÏ]kË¿¢Ï üll¾¸B#&¶±Ï7jé¬@ agŽLÞAÌ&*’ÅlªVXÁJ¹‘ 8¨©Å1{cÚÈäþã×­›´¬vHüœÛÆgå–ÿxÀ]N•ÝŸ}Yà ìïÅŸ^ÕûͤÚï`Ó‹m0_á«öêão&M·5c”>À ª>†x‰V˜Í1€VÉÂ;+þ}…þýû_¸p1ûtÁk›[Y¼èîøc–}ù«…O?ðÞDR˜—_Яß2²NN[°Œ(n|4±Ûǯ@ ‘Ã Êøl¯ "‹`ŸÌàÁƒÉÓ³[&./´êá¯Î;ïóUñ)ÊŸB%œ£ÂfCb0Y£W°ô8+,·,”80WP‘EÈÎ*!³àÉ%K§.Úµ2! ÷¸°‚ÒßpM ¤ge¬¢õø¿ƒ;›¨@¢A—Òó¬ îc ½ZϳŽk±8VÖ×UA¡Ñ¡Á'Ù1™^XA °$˜¬Àö1J¯·¥½Ömí%´/+í*˜` ÑÄ ¾Ž“þ±‚2>›Å+ürìlªÔÆÄéqáúØ/Åéö%¬+@û&+ø4Nú:2s `¯ð˱³¨R$¬ êcDÁ P°nV˜Å1€!+Ð"YHµ1±A‚Á l£ôjô ¼¹h!X V€Bµã$7<Î xa–,0±A‚Á ZzÕþÉ“`³ÙÀ P°ª'É𨋠²ëñ5‰WP‘&6HðÞA°ôªþ)(€ `(ÔˆtqÁoVÐz|¯‰èxÀ P°j Z\@¼¤¯Ä+Ìœñ X V€B­°0C¯ð›™D² ¢˜Ø =Ï ê>f¯ àX V€BXÿY“©a¯ צ6&6H0Xíc´GêNÿÀ P°ªË ZP„80WP“&6HðX¥W£éßÈ·†¹ V€öqVÐ…@XAëñ5{Á’&6H0XAK¯n|‹ V€BÃø-&=/â ‘¯V€‚ Ðг‚¥x…{3“*µ1±Az\¸>v¯Ø#}ʳî“£@ ^» &h4±‚¯ã¤¯#3Çfñ J=jcbƒ”þ6 Ž`‚FúÚùý™90d…{ó'¢"Yü‰j¤N6|;aÆ2»÷Ó*î#lØÞm/¬ ’E/`¶°aÙ5¬€{ ¶‰m¯ðÈŸ¨RÛäy{ÿóz¢«Së—m½þÆæº?T;w]Í3ïV?ù·ªG—T=òFå¯_­Ò£®mgÚôúX¹J‘.°a³v¯gÜGذ-ئñ r=j›³Â¶l÷¦ƒÝë3ïÙµ2ÝþvZç›ÛÚÿº¥mÁÇmÏmlù\^u®oÄ_!úÚG… ?Èéï_?ÿΑ9K÷ÍXôÏ?¾’ôô‚ø'Ÿ[?qÎûT•óbÏ1H6½>IÇ]Ÿqš_¥Ð´Ç| Ùµê¥íì³v°î#lØ^mãw²ðêWXœtýåxaÿ¿««ÿóíʱâNuøÜÓÁ{ÔÙvþåýÓ›9‰þ}W{ܶ¦GMtκêiï–Oþ[ÉãK¯Ž_RFtʬ¡$2e¶[™rÝü*…Œ­ãKx©¶·´³ÏÚF£yoÞ€àß‚û¶¹í…X²àž±’KG¾ØþÁöOßÝòÑò»3h¡ËíîîvÙ»\mÎ[­ŽÆ[Žº]•õöû'Ì ê£®´söÊŸ%´WÐZòöé„ÿÁmõÊü¿…’ÈÒ2ϰG7¹J!£Eëø^ªí-íì³vÔ°î)lØ&¶y¼Â ªÔæž±[×4ýëgTã×,¦z*û ÝálëtÞjënlv\¿ÑUUoÏüê\ðXk§Â (4çM!Jp¡¶Q˜Kj:®}Ûqþ¹ Êy±çÈÙ÷ÌÝ_R¾gê˜é&u¬ØÉ{=#£ËåêvºŒ®’û÷Ãö_BÑžHhçЙ{JÊ÷ÓÛxûïYuÆát’6[==¼×ÐľgÎ>zÊ~ïGûHz½eA]<øÎ ‘x_`ÃŽ4Û<^AªGmîKüh¹Ûµ‡jGÕšæÜ'‹R>sꤋ%u'Ï–ž½\{½©«ºÁ^^שeוµ1r͘µWŽWð´“² »úmz­Uƒ½´¦£äÛv ];»ÈµçxÏjO\…óDa…²Êà +hë[´wåxvëtw9\vgs[÷  ê«Ä·gîþRq¾7Ì[2fºßmàl+øräÔE¥IR{ÆÄ}VîänAiZ\àí±ÒÎÚÜ×ÛÊþálJ¼º{ˆ£aã­Š¤Š³r˜ÅíG™°é 5?VÌ‚C +èÖ!3k©Ãsú_­}Ѩý÷Œ‰?ê¨ùlît£}*åÂ>Å>F§íã_´~}”må¶Õ‘#Š·)þ¸ãÜ’¹˜ìGa¿ï‘ö‘TnYæ±ó陹Ÿí<‘rdí§™+?ÚýöÚ’®e²x`³‘A@ÈŸ].‹£„Iý õ[ذ£Ì6yÁ“÷ŒmZ³ØÝÞ^÷qkɵœöÖɱ?›¾3ãøGÎþ}{ÆÂÕ‰©™ùd ,®îà†—+#V\’›ಀm'a#P(«ëT@¡+Áquañš{ײÛNM«q:s—Èd—»„LÕ­º¨ýÉŽ£Òtårwu»::-íÝM-ކ&GÍwvö*ñ+ÝY{Jå™fèŠ\gÏ­zð¥­xGÇѧn^N#­úæ|Ég;2ƒêqñ©Ýߥ—ýk”ûÖšÊìgÜ·Ö—fo,Ù¢`–vÛ)©Õg^܃Sháâ­ï 'ÎÉÆuØU¸`;œÇVë÷ ñ¯ž¾¤•½H$A›œliy mõ}Š5ºÚGÒ+±‚ òŒ.ކ¸Ð³¬€U#lØ^mCV¸‡Œ#tå!÷hXვ¯º¾[O@¡ùü³7OŒ½|`úîjìl½åv_mw&ÿ»ä™¿¼÷U~eQE{úÁ +ÄfX~ÎÍ…k'õ+ükÝOšŽÜ«€ÂVQv'î¾üç"Øm‡‰K*¯I~éEå|Iù0‘¦ˆ«.¶ÜW{SÊ!ñÄÝ©¹D·ïÿ†¬½’vå|šþÕæMHÍÚ´ýÐÆäƒo®þ”Û6æ¥Ã´‚=~ë1gÅ—äSNêfð¯=ä¿Z|Ùþ•ó¹OÜéi©îš}-;|9{©üFÊ–ÖÇtïOçö3Lô¸Ð†ùÝ_ÛéjJr·lr;NµTït;rˆÝq}‹‚Yܶä†&–×$ÍåïÝ0qާ±sôc¼NqeÏÔ©#{PmËô‡a²'€«?L˜Â=îaŸ/ ^„¤œnZBftyÛU‰åRaÙ©stçJ{è-¦v[öú,Ω¦çHHhÉê3%iqB;W Æ0Ñë0Eô.p› ûTNY¼Ì)•¶•Êó–“s®§äÙb¯§ö‘TnÙó›Ý37tOý·&¾]1îÕ³cž?FÉX—Ø%„j‚g¦yyÊ÷xbbcc„ÿIÿ†óúøR¿… ;Êl³x…{ÆÌ Jmî{wÉ‚–S(Ý9üügwlþáž]‰EUõd@w¸Ýµíίœïï/ØöÅÞ‹eíÉûÏrCƒ°dŸì‰W`ÛIYÁÞå$¸@A¡¦Ñ^~½“°ÂÅòöK­{Gï“@A>/ªÂ žŒ’£§³åÃæ«ç)b!Wß'›´ŠŒ]§‹€‚¼ÞrµÛ%ïBýÍ.:†’«ÄmKP*®t‰=%­†Ì:“…v:­šH{ˆ*øâè–}­íÝ/nqï;ãú²Û±opeiáá’Óó<~þ¯¿w§|â;”Ûm¹2´Á×v¥.?ötkÝn§ûf{Ã~b—eŽ´©…¹wâÊ^}ï†fñ£+„ëùtjµãDÂÐ7z=i¼Â¡Žàv"_ùŸžþ nKúŒN}‰*FO—ö)ÎÄô¸ÃVžqˆ÷‘VPö)oëi϶W‘Ón+·‡Ö)%X0:.1GÜVÜçâœnR_jõˆ;7ß#ž P"ÞGÚÎø#rá²d$^(áq û?v"ü‰”)Óy´¤¶k5µ:n:j¿ëª¸Þy­¦ƒ[<-ý¥E…ÎÜïRŠ“RV¼Ô×Yl§ß†e¶Y¼‚RÚÜ3÷ç?ýuÞÔg<;õ±Ù“~·:~Gu‡Ãîr·:»Ênu~UÞ™S3aÆki‡¯jYA (2F \;)+´uv7·uP¨½A†';ž+œ¿ÖšWÔzöß-þüÊz弨J+xq(TÊÉŒ(¬žåñQ{M,Ú¤U4¼+eß¿Ió¨w!yïé­_žÔ÷.ÈÛ S²Ó³ä%-Ñm§¶‚/]ÎÖçÍ–î­Çœ?ÿ‹û‹´O\•éî=ýæ¿ïêŽâýswÆÜ~V¤ ¾¶³ûF¢ûÖwç1‚ nÇ)b_+ø`Åû_/[}rñ;9o.ýŠô&æÞ‰+cõ½S<2Lä-Wü´]ÁO‘WØJ˜Èr±¾vŸ1ã?&T½ú +xöÉWö4PD ûäÊ¥öÐýsÛÊí‘êPž8‘#x)È/8·DܹP~j«öiIå–‰ñ ßlãÖÉñ E•íº‚ÿ¬ zm³6ÃgVv† ; lãw²0Ÿ³“¿È>Vz£´Ó]xÓþMMó¾ËmËÒrzï-˜¥>®Lú¶=޼8í¶l ^ßÈ^3‰³$ï‚Ô¦ÍOƒ|ïôë(…U â®â¶”å.YuF×›¥Ë \תk”žÄ«•ºŽFßAô0+`Õ¶WÛ„ȸ@WÓ©z¶—$edUÛ³ª\{Kì.yêÏkßY2G?ÎHö%ø£¤÷¨«ÚIÆ©§f.ÐêVY6~ö9…óÜ¿íßܶ‚“ßIVTÓè?—Ÿ"+*Ê ÓØëà‡MZE@árqùg_žlø®ƒzîóxG§óÍñ¬Wwˆ¬ m+{¦)û¤¬ ®,ýo½P_D®êºÞÔõë·Üi9]wL®s]Zyñß{áĤüúSã¾Ò•¾­îßÁíGö¸L ¤ ¾¶³"÷¿¯|ÐYóAíñ‡K/üþC2’^  ðæ’¯^}ãØ_YÁ³­àNwžY._«Å9ÄþLt¿ Ížž^ë8?lô&‚Bœ ­/\[O®=ò¼.ôqC¡ZŒ^}Ù[0Mí-êpžqn>CzT.îö«µr{NÄËå̶£UÇZœS]VY—4G(¶òLie]iZœ§ l›%{Óq‡T_8„ÐNé\HzPz¬£å5tWÓÓ Ž8C7ᮉ.+Ð[–yìÜç‚_!'!å(ýÄ¥òö ×ÚŒº±â[i9¡TP U¬@aô¾ÑÔ> vØæñ Rmj[™¹ç/ßúvòéE GŸxékÞãÒ»Û?Ò. ¸F¹XƒÖ°¯ j§¹MAaéþëİÝ÷1W‡àó›Éxyõ<¬ÞŽ9ÏÄ‘U h‘}èƒQÃÊqÉèI+‰»r:ì’w¡ÃãQpÔ|×U~ÝN@ ¦”è¶ŒAÚ§ìiŽ«m°Ðtæ*ÄcÔ6_Î_*ûæ|éÙKÕ?‰u¯þ¼ý{ÿyÑ•7ÿÛÜ>÷äø¬Š=c’~Úõéú‹ÿë{Ü~dË4©]ëíÌÉ/=|úÚ¾œâÇ®’ÞC= ^XpXŽWðìGÄå7“ñ÷¨‚ø„æ ç²Rú]eIv.V‡1¯œÌõW•K^(M¹Ê[0Mã-à< Âõy+Û!üôôħAj¡p‡i·eúƒtŽò½&E'LÓx¦É›‹ýY>ezP©òe‰£Ç)Šž#'Áß#]V0ºei‡¯š¼”ä~3I~®PŒd”È€¾Àdâ¥ÐF/õµ±õOذûˆm¯ " _WüOþáñÓ ÷ܸ¼*eãÜà}JÅ'2â@!ôtöæêOÉ(I½ ÷y¼©¥›ó+œ¿ÖFY!ØíQð…êÆ¤wLm}éãÊÛû–€Ñ)ÇïO)гå§íïÆ}{çíá¢Z®¬’¡ÿMñÕÃ_% ûÜ!9^«ÐÙº¬`tËXí‘Ç¿G“¸°a{µ½¼ƒ`ÉÂçpÒ¤'¾Žöúsã‚Ç ¾RÒýó2t= ¡´©wAö(HßW¸TÞ.D_^ießA„²mÿíñR Diù„ÿgÓùÕ÷møŸˆF íÚô+€ÛáýêbO½ÄÄ}„ ÛÜîÉx]Yôú«G2SƒÍ >QRX< ZFEŽÝ1xÂŽ_޻濃¬a›Ø½=Ï$î#lØÇ+ŒžN•Úç¾½iÏy]}£¶°aÙÂOî#lØVlÓx…ÑÓ¨R;v᪒²êˆzÎ-ûPÛNذa‡Ì&ÃBiE]/2€à>†mÅ6d…aBD½M-°aÆ 6ì¾i{añwäÓ`Æ 6lØ}Ö6‹WIÁ† 6lذÍâ@R°aÆ 6lÄ+À† 6lذýŒWx†¨HÏÀ† 6lذû¬m¯…B¡P( V€B¡P( V€B¡P( V€B¡P( V€B¡P( V€B¡P( V€B¡P( VPéÐY{JÊ÷L~p ®8 L:³¯S¸PÿYA˜æNšÅÕé¬Nœ5¬…Fîpïû³6ôÁŽÊ›¶úyWF¿ÿH¹2ï|ãPN-û#ë3åÐ7)‹Îá‹»&VX!НF¤<¿Òã–'Ú%)‹¸G›”•¿IÉ!|aeš˜ç{/+@¡‘É “Sª…IÅ žg|胋Ë%Dº"×ÙkŸâ?l¯r0çõV¶Î8¦;SŠ:Ñ8|é\o'НF>¢‘{Ty¥rò„Ê%ÄÖ£Þ~±3ŽhWtÐaKT> a'dÜq]¡ìªw/S ÐÈgúÏÄlé1¤OŸÉ&f¬Ðk‰胯o)Óm8pÈk824ù¢Ñ3¯{Mè™&f;Ìz ÞS„”öL&Œ.à þ¬ØýYA^XðРî:CÅs£ÎL‰ ”“WN[6 PhpYAy VP¹7•’ÞûBd~x|ééÊËec|t²‚î5áIê-ï|ãè3W#¢YáÁ)qÙÂMaÿiô-H à{¼‚êe„Tè¡r®&÷òB ôäÑ- Ðø&?hæÐ-W<Ò8%:{)ßëNol¡4qÎêc¬àõš€"ƒ$ƒ{´Wä‰XPð'^ VÐñ%Uû* )qè ‘ +øË âÛ‡¬ËÁó>¢·ùÛ7qèûÛû.+X¹&`…È`úŠÿhŠfÚ ÂûxA]Ìûm5rVNu†\âñI;z +øÁ ä÷¼7võ›Ä^‹øz±}ú„רF°BD±s%‡½ìKèñ"-(øÉ Ò¬¯þ%•òn"NŽŸ*ÉÎU¢¸’^½:B{Ño®Ä+ô¸"ª …B¡ÐÈÞA@¡P( 탠V€B¡P( àÀ P( …B½(X …B¡P¨_¬…B¡P(ª(X …B¡P¨5VX@ ¢'+Œ…@ 1–ÿòÔ™®Ì(IEND®B`‚stxxl-1.4.1/doc/tutorial_vector.dox000644 001411 000144 00000006773 12405153572 017224 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_vector.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_vector STXXL Vector This section introduces into the STXXL vector container (to learn more about the structure of stxxl::vector, see section \ref design_vector). ### Creating a STXXL vector Before we can use a STXXL vector, we first have to define and then to instantiate a vector object. To manage the configuration of the vector type we use the generator template. A minimal configuration is shown below - as one can see the value_type (integer in our case) is the only only stricly neccessary parameter. See \ref design_vector_generator for additional configuration parameters and information. \code typedef stxxl::VECTOR_GENERATOR::result vector; vector my_vector; // creates empty vector object \endcode ### Insert elements We can fill the vector by calling push_back() which appends a new value at the end: \code for (int i = 0; i < 1024*1024; i++) { my_vector.push_back(i); } // my_vector stores: 0 1 2 3 4 5 6 [...] 1024*1024 \endcode ### Access elements To read and/or modify the values of a STXXL vector, simply use the []-operator: \code std::cout << "element at position 99: " << my_vector[99] << std::endl; my_vector[99] = 0; // zeroing element at position 99 \endcode In addition, the STXXL vector provides different iterators to advance the vector which can be used as follows: \code // create iterator which starts at the beginning of my_vector vector::iterator iter = my_vector.begin(); // access an element std::cout << "first element: " << *iter << std::endl; // go to next element iter++; \endcode Alongside with the many advantages of iterators, there are several things which need to bear in mind when using them. Details are described in \ref design_vector_notes. ### Delete elements The operation pop_back() removes the last element of the vector (without returning it). The following code snippet is emptying my_vector: \code // empty() returns true, if the vector is empty while (!my_vector.empty()) { my_vector.pop_back(); } \endcode ### Determine size / Check whether vector is empty To determine the number of elements a vector currently stores, call size(): \code std::cout << "size of vector: " << my_vector.size() << std::endl; \endcode To check if the vector is empty, call the empty() function which returns true in that case: \code std::cout << "vector empty? " << my_vector.empty() << std::endl; \endcode ### A minimal working example of STXXL's vector (See \ref examples/containers/vector1.cpp for the sourcecode of the following example). \snippet examples/containers/vector1.cpp example See \ref examples/containers/vector2.cpp for the sourcecode of a more comprehensive example. \example examples/containers/vector1.cpp This example code is explained in the \ref tutorial_vector section. \example examples/containers/vector2.cpp This example code is explained in the \ref tutorial_vector section. */ } // namespace stxxl stxxl-1.4.1/doc/tutorial_pqueue.dox000644 001411 000144 00000012010 12405153572 017203 0ustar00tbusers000000 000000 // -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- /*************************************************************************** * doc/tutorial_pqueue.dox * * Usage Tutorial for STXXL * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ namespace stxxl { /** \page tutorial_pqueue STXXL Priority Queue This page introduces into the stxxl::priority_queue container (to learn more about the structure of stxxl::priority_queue, see section \ref design_pqueue). Basically, the priority queue provides insertion of new elements as well as access and deletion of the element on top. The invariant guarantees that the top element is the largest (or smallest if desired) of all inserted elements identified by comparison realized by the customizable comparator class. ### Creating a STXXL priority queue To manage the configuration of the priority queue type, we use the generator template stxxl::PRIORITY_QUEUE_GENERATOR. This generator template expects a value type (which is an integer in our example), a class which we name Comparator(a,b) to compare two given elements a and b, a internal memory limit in bytes and the number of elements to be stored (in 1024 units). See section \ref design_pqueue_generator for additional configuration parameters and information. Thus the definition may look as follows: \code // template parameter typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pqueue_type; \endcode The ComparatorGreater(a,b) class is needed to compare two given elements a and b and has to be defined by hand (and before the priority queue definition above): \code struct ComparatorGreater { bool operator () (const int &a, const int &b) const { return (a > b); } int min_value() const { return std::numeric_limits::max(); } }; \endcode The compare-operator () of two elements a and b returns true, if a is larger than b, otherwise false. Consequently, this priority queue serves it's smallest element on top . The additional min_value() function ensures that Comparator(min_value(),x) is true for each and every x. iLikewise the minimum-on-top Comparator, we can easily define a largest element on top Comparator which stores the the largest contained integer on top as well: \code struct ComparatorLess { bool operator () (const int & a, const int & b) const { return a::min(); } }; \endcode Note that CompareType must define a strict weak ordering. These and some other details are available in the Notes part of \ref design_pqueue_generator To create a STXXL priority queue instance, a resizable buffered writing and prefetched reading pool (to overlap I/O and computation) is needed: \code typedef pqueue_type::block_type block_type; const unsigned int mem_for_pools = 16 * 1024 * 1024; // restricts memory consumption of the pools stxxl::read_write_pool pool((mem_for_pools / 2) / block_type::raw_size, (mem_for_pools / 2) / block_type::raw_size); pqueue_type my_pqueue(pool); // creates priority queue object with read-write-pool \endcode ### Insert / Access / Delete elements To insert a new element into the priority queue, call push(): \code my_pqueue.push(5); \endcode The priority queue only allows to access the top element, which is the smallest or largest element (depending on the used comparator class) of all inserted elements. Calling top() on an instance returns this element: \code int x; x = my_pqueue.top(); \endcode Erasing elements is only possible on the top of the priority queue by calling pop(). Note that after removing the element on top, the priority queue still holds the above mentioned property. \code my_pqueue.pop(); \endcode ### Determine size / Check whether the priority queue is empty To determine the size (i.e. the number of elements) of an instance, call size(): \code std::cout << "priority queue stores: " << my_pqueue.size() << " elements" << std::endl; \endcode To check if the priority queue is empty, call empty() which returns true in case: \code std::cout << "empty priority queue? " << my_pqueue.empty() << std::endl; \endcode ### A minimal working example of STXXL's priority queue (See \ref examples/containers/pqueue1.cpp for the sourcecode of the following example). \snippet examples/containers/pqueue1.cpp example See \ref examples/containers/pqueue2.cpp for the sourcecode of another small example. \example examples/containers/pqueue1.cpp This example code is explained in the \ref tutorial_pqueue section \example examples/containers/pqueue2.cpp This example code is explained in the \ref tutorial_pqueue section */ } // namespace stxxl stxxl-1.4.1/Doxyfile000644 001411 000144 00000233363 12424126614 014217 0ustar00tbusers000000 000000 # Doxyfile 1.8.3.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = STXXL # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 1.4.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. #PROJECT_BRIEF = "Standard Template Library for Extra Large Data Sets" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = YES # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = doc/DoxygenLayout.xml # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = doc/references.bib #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text " # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = doc/ \ include/stxxl/ \ lib/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = * # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = *.png *.pdf *.eps *.svg *.bib *.xml *.xmi *.css */CMakeLists.txt *.symbols # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = . examples/ # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = *.cpp # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = doc/images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page (index.html). # This can be useful if you have a project on for instance GitHub and want reuse # the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = doxygen-html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = doc/doxygen-extra.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see #
    # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 12 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search engine # library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = doxygen-latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = YES # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = doxygen-rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = "STXXL_BEGIN_NAMESPACE=namespace stxxl {" \ "STXXL_END_NAMESPACE=}" \ "STXXL_DEPRECATED(x)=x" \ "STXXL_HAVE_MMAP_FILE=1" \ "STXXL_HAVE_LINUXAIO_FILE=1" \ "STXXL_WINDOWS=1" \ "STXXL_POSIX_THREADS=1" \ # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = svg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES stxxl-1.4.1/INSTALL000644 001411 000144 00000001307 12350112610 013516 0ustar00tbusers000000 000000 BASIC INFORMATION For detailed installation instructions, including instruction for installation in a Linux or Windows based environment and application compilation hints (including an example Makefile) please read the doxygen manual: http://stxxl.sourceforge.net/ QUICK INSTALLATION INSTRUCTIONS FOR POSIX COMPATIBLE SYSTEMS * Extract the tarball and change into the stxxl root directory. * Create a separate build directory (in-source building is prohibited): - mkdir build - cd build * Run cmake to detect the build platform (where ".." is the STXXL tree): - cmake .. * Check cmake's output for errors, then run build the library: - make * See http://stxxl.sourceforge.net/ for more details. stxxl-1.4.1/CHANGELOG000644 001411 000144 00000061121 12424126573 013716 0ustar00tbusers000000 000000 Version 1.4.1 (29 October 2014) * support kernel based asynchronous I/O on Linux (new file type "linuxaio"), which exploits Native Command Queuing (NCQ) if available. disable/enable with the define STXXL_FILE_LINUXAIO 0/1 via cmake * adding new disk_config entry device_id, which specifies the physical device id of the "disk" used during prefetching sequence calculations. This used to be identical with the queue id, however, for linuxaio there is only one queue; thus the distinction had to be made. In a default config, no changes are necessary, as the device_id parameter is automatically enumerated. * adding stxxl::binary_buffer which can be used for compact serialization and reading via a stxxl::binary_reader cursor interface. * stxxl::unordered_map is a hash map, backed by external memory. It probably only works well when lots of internal memory is used to buffer access to a relatively small working set. Then, however, fast direct in-memory item access can be used. * stxxl::external_shared_ptr is a proxy class to allow use of shared_ptr classes inside stxxl containers. Reference counts are kept in memory, while data may be swapped out to disk. * removing struct default_completion_handler, using a NULL pointer in default complete handler instead, since otherwise a default_completion_handler objects is cloned for _each_io_request_! Using a NULL pointer avoids superfluous new/delete work on the heap. * minor changes: - disable TPIE benchmarks by default, removing a warning. - compilation and tests work under MinGW 64-bit with special threads. - fixed compilation on 32-bit systems, everything is -Wconversion safe. - adding 32-bit and 64-bit cmdline_parser::add_bytes() variants. - removing all double underscores. - use atomic_counted_object in class file for request reference counting. - adding local/test2.cpp containing a stxxl::sorter example. Version 1.4.0 (12 December 2013) * Reorganized Directory Hierarchy - separating old component based directories into lib/ (libstxxl source files), tests/ (for tests cases), examples/ (for tutorial and example code) and tools/ (for stxxl_tool and more benchmarks). * CMake build system for cross-platform compilation - replaced adhoc Makefiles with CMake build system - CMake scripts can also output MSVC project files and more. - all component tests must register with CMake's test system -> run make test - generates pkg-config and CMake's project config files * Greatly Improved Doxygen Documentation - integrated old LaTeX tutorial - newly written tutorials for almost all STXXL containers and algorithms - embedded design documentation from Roman Dementiev's PhD thesis - new compilation instructions for CMake build system * Improved .stxxl Config Options for File I/O Implementations - looking for .stxxl in more places than just the current directory (see the docs for which places exactly). - parse file size with SI and IEC units, like "10GiB" in disk= config lines. - parse additional file I/O implementation options. - support for "unlink_on_open" (keep file descriptor but unlink file) and "delete_at_exit" options. - support for "direct=off/try/on" to turn off O_DIRECT for tmpfs and similar. - the default open mode is now direct=try -> prints a warning if O_DIRECT is unavailable on the system. * .stxxl Config Options Represented as disk_config Class - all disk= config lines are parsed into disk_config objects. - allow API calls to configure disks via stxxl::config::add_disk() - delay config file load till config::initialize() is called by block_manager * Command line parser stxxl::cmdline_parser - stand-alone command line parser with ability to parse SI and IEC units for "bytes sizes". - automatically output nicely formatted help for all options and flags. - using the cmdline_parser for all stxxl_tool subtools. * Created stxxl_tool from multiple small tools and benchmarks - stxxl_tool is a collection of subtools, previous scattered around in the directory hierarchy. - goal is to have one tool installed in binary distribution together with libstxxl library and includes. - primary tool for standardized platform speed tests. - provide good command line help on these subtools via cmdline_parser. * Adding skew3 as "real-world" STXXL application to examples subdirectory. - enhanced integrated stxxl::tuple construction for this application. * Enable use of raw block devices on Linux - these do provide more stable I/O throughput than even XFS. - disable growing of these devices * Most platform configuration is now done by CMake and saved into the header file * Enhanced stxxl::block_manager to count maximum, current and total number of bytes allocated in external memory. * Support Visual Studio 2012 and 2013 _without_ Boost by using std::thread - VS 2012 and 2013 support STL threads, mutexes, condition_variables and more, which are basically modeled after the Boost libraries. Thus we can simply uses these included libraries instead of Boost. - Fixed bad runtime deadlock bug occurring when joining std::threads after main() exits. The bug persists in VS 2012 and 2013. * Implementing counting_ptr reference counted objects - used like boost::intrusive_ptr with a base class. - reimplementing request_ptr using just counting_ptr. - (optionally) use atomic operations for thread-safe reference counting - applying counting_ptr to stream::sorted_runs to avoid extra copying * New STXXL container "stxxl::sequence" - basically a deque container without random access to blocks. - efficient {push/pop}_{front/back} at both ends. - access to block sequence via stream and reverse_stream constructions * New STXXL container "stxxl::sorter" - combination of stream::runs_creator and stream::runs_merger into a convenient sorting container. * Added vector_bufreader and vector_bufwriter to overlapping I/O and computation when accessing a stxxl::vector in scanning mode. - documented problems of scanning in a vector - C++11 range loop access support to stxxl::vector via vector_bufreader. * Added uint40 and uint48 types for integers with five and six bytes. * Adding STXXL_CHECK macros to always check outcomes in tests. * Renaming mng/mng.{h,cpp} -> mng/block_manager.{h,cpp}. * Reworking version.h - add integer version like 10400 for 1.4.0 - add get_version_string() for longer version string with features - check_library_version() for mismatch between headers and libraries * Reworking error_handling.h to throw much more useful exceptions with errno information and more. * Various file renames (which are only possible at major version changes). * Fixing bug in shrinking vector with vector::resize(n,true). ------------------------------------------ Version 1.3.2 (unreleased) * New Features - Add defines STXXL_VERSION_{MAJOR,MINOR,PATCHLEVEL} and library routines stxxl::version_{major,minor,patchlevel}() for getting STXXL version information and an inline function check_library_version() to check at runtime for mismatching header and library versions. (Bz:#6) Available via * Merged matrix branch which allows external memory matrix operations. - merged dsr::argument_helper for command line processing - also required an internal memory addressable_priority_queue, a shared_object_pointer (instrusive pointer) - adds block_scheduler as submodule to prefetch matrix blocks in a pre-recorded order - contains a test and benchmark. * Fixed bug in stxxl::priority_queue - added dump_sizes() - The priority queue may become fragmented after a significant number of insertions and deletes so that all slots in all groups are being used, but the last external group is nearly empty (counting elements). In that case we merge the whole last external group into a temporary ext_merger and swap this overflow merger and the (now empty) last external merger afterwards. Increasing the number of groups beyond 4 is not possible without significant changes elsewhere. * Fixed bug in stxxl::queue - added special case when first block has space at the beginning - added another test N * (push,pop,push), N * (pop, push, pop) * Using LIKELY and UNLIKEY to prioritize branchs in gcc. * Platform Support - add GCC 4.6 - add clang++ 2.9 - update ICPC 12.0 (Intel C++ Composer XE 2011 Update 4): drop workarounds for bugs fixed in Update 4 - Boost 1.46: Boost Filesystem Version 3 ------------------------------------------ Version 1.3.1 (10 March 2011) * Possibly breaking changes - No longer open syscall files with O_SYNC|O_DSYNC|O_RSYNC by default when doing direct I/O, to avoid a write performance decrease starting with Linux kernel version 2.6.32. A stxxl::file::SYNC flag has been added to allow forcing the O_*SYNC flags when opening files. - Lock files by default (if implemented for this file type), can by disabled via stxxl::file::NO_LOCK. - block_prefetcher now takes a completion_handler instead of a raw function pointer. Furthermore, the completion_handler is already executed after the read operation completes, not only when the issuer waits for it to complete. The I/O thread issues the call to the completion_handler, so for multiple disks, this may happen concurrently. * Internal changes, not user-visible - Change handling of (empty) filler elements in typed_block to fix mismatching struct size in nested blocks. - Removed debugmon which was very limited and disabled for a long time. * Bugfixes - Allow prefetch schedule computation of vectors that are bound to a file. - Fix race growing files during concurrent block allocation. - Allow reading a block that spans over end-of-file, fill remainder with zeroes. - Crash at program termination when using global stxxl containers. - Enable syscall_file to read/write >=2GiB of data in a single I/O operation. * New public headers: stxxl/stats, stxxl/request * Parallel mode can be switched on for internal STXXL computation selectively (i.e. without enabling it for the non-stxxl part of an application), by setting USE_PARALLEL_MODE=yes in make.settings.local. * Platform Support - add ICPC 12.0, works with both MCSTL (needs libstdc++ from GCC 4.2) and parallel mode (needs libstdc++ from GCC 4.3 (not 4.4/4.5)) - add CLANG++ 2.8 - add MSVC 2010/Windows 7 - allow the library name generated by MSVC to be changed by setting LIBNAME and redefining STXXL_LIBNAME (defaults to "stxxl") * Under-the-hood improvements - code cleanup - I/O-layer: renaming and reorganization of the request implementation * Documentation updates * Several new test programs * Several test programs improved ------------------------------------------ Version 1.3.0 (12 August 2010) * New file types - fileperblock_syscall/fileperblock_mmap/fileperblock_boostfd/fileperblock_wincall: Use a separate file for each block, which is accessed by means of the underlying file type. The "filename" given is used as a prefix of the block file names. The advantage of these file types is that unused disk space is freed earlier on the file system level. - wbtl_file: Do library-based write-combining (good for writing small blocks onto SSDs) * I/O subsystem - separate the disk number of a file (which queue to put requests in) from the fact that blocks for this file are allocated via the block manager (disk number -1 otherwise). - separate wait time counting for read and write I/Os - wait times can be logged to a separate log file (compile with -DSTXXL_WAIT_LOG_ENABLED and set STXXLWAITLOGFILE in the environment) * Parallel PQ - priority_queue now supports parallelized operations utilizing the libstdc++ parallel mode * Other new functionality - file requests can now be canceled (success not guaranteed), completion handlers are called anyway. - log file locations are now configurable by the environment variables STXXLLOGFILE and STXXLERRLOGFILE - single queue I/O scheduling - reverse_iterator added to containers types vector, deque and map - autogrow files (specified in .stxxl by a size of 0), will be deleted on normal program termination - add infrastructure to build multiple binaries with different CXXFLAGS from a single source - overwriting deleted memfile regions with uninitialized data is now optional (STXXL_CLEAR_FREE_MEMFILE_MEM) - read_write_pool that combines prefetch_pool with write_pool and ensures cache coherency - add a replaceable exit handler implementation, can be overwritten e.g. for library inclusion * Many, many bug fixes, in particular concerning - priority queue - optimal prefetch schedule - possible race condition while creating singleton instances - random_shuffle() * Platform Support - add GCC 4.4 (parallel mode features not working in GCC 4.3 can now be used) - add GCC 4.5 - initial support for GCC 4.6 - add ICPC 11.x - add Boost 1.42 - add FreeBSD - drop Solaris * New benchmarks - io/benchmark_disks: more command line paramaters for finer testing granularity - measure hard-disk and flash-disk combined performance and determine best block size pairs (io/benchmark_disk_and_flash) - benchmark using regular STXXL configuration (benchmark_configured_disks) * Possibly breaking changes - API name changes vector::touch -> vector::block_externally_updated - Template parameter changes stream::sorted_runs, trigger_entry, trigger_entry_cmp, trigger_entry_iterator - Priority of write changes - Other name changes (considered internal) typed_block<>::has_filler -> !typed_block<>::has_only_data file::delete_region -> file::discard vector::alloc_strategy -> vector::alloc_strategy_type - stxxl::sort(..., M) and stxxl::stream::sort(..., M) now adhere to to the memory limit M more strictly and throw errors instead of overallocating - Execute completion handler before notifying waiters, so far after. * Deprecated methods: - stxxl::stats::reset(), stxxl::stats::_reset_io_wait_time(): use stxxl::stats_data instead to store snapshots of the counters and compute differences * Under-the-hood improvements - I/O layer cleanup - aligned_alloc * Doxy now also lists undocumented members * stop requiring STXXL_ROOT to be a hard coded absolute path set in make.settings.local, a default of CURDIR works fine * document #defines in defines.h - switch on/off file types with STXXL_HAVE_xxx_FILE (to avoid platform compatibility problems) ------------------------------------------ Version 1.2.1 (14 August 2008) * add support for the libstdc++ parallel_mode (successor of MCSTL), new make targets: library_g++_pmode, tests_g++_pmode, clean_g++_pmode (requires g++ 4.3.2 or later) * new stxxl file type stxxl::mem_file (for debugging purposes only), helps debugging with valgrind because no memory cell ever leaves valgrind's control * properly destroy the singleton objects (block manager, disk queue threads, logger, stats, config, ...) at program exit * fixed a bug (recursive locking) in recursive block allocation * added a test program for recursive block allocation * sensible dependencies for nmake (MSVC): only rebuild files as necessary * improve performance of createdisks * human-readable output for I/O stats * replace hard coded min/max values by std::numeric_limits<>::min/max in examples * fix a case where only half of the available memory was used during recursive merging * verify stxxl::set_seed() functionality in tests * remove stxxl disk files created from default configuration (no .stxxl file found) and other temporary files at program end * stop using deprecated functions, switch to the replacements instead * unify the mutex lock handling by introducing stxxl::scoped_mutex_lock * unify the I/O wait time counting to work like read/write time counting * simplify I/O time counting with scoped_{read,write,wait}_timer * add some more tests * more code cleanup + reformatting * move some more code to the library * reduce some include dependencies * build system tuning * propagate region deletion (when blocks are freed) to the file level * fix problem in sorters where blocks were released too early * specialize is_sorted() to use const_vector_iterators, no extra writes * add c++0x style const_iterator cbegin()/cend() to all containers ------------------------------------------ Version 1.2.0 (05 July 2008) * made the block management layer thread-safe * made all size_types unsigned * stxxl::priority_queue - fixed a rare assertion - fixed a race condition by using proper block hinting - insert_queue: replaced std::priority_queue with a special implementation internal_priority_queue that allows for fast exporting of all elements - even more bugs and inefficiencies fixed - significant speed improvements * random number generators are now all seedable, should allow redoing identical program runs for debugging purposes * stxxl::noncopyable, inspired by boost::noncopyable: inheriting from this class forbids copying of objects when this is undesirable - used in all classes that had implemented their own variants previously * stxxl::vector, all sorting functions: replaced two2one_dim_array_row_adapter with array_of_sequences_iterator which is much faster, especially if blocks have padding * if required, verify that the sentinels satisfy a strict weak ordering * stxxl::vector: several operations sped up, several more implemented * fix existing allocation strategies and add experimental support for distinguishing between regular disks and flash devices * stxxl::stable_ksort - add experimental warning, some parts are not yet implemented - fixed an off-by-one error in memory allocation vs. usage * btree: fuse last two nodes/leaves if possible, rebalancing can fail * btree tests: ensure uniqueness of test data if required * reduce function call overhead of stxxl::debugmon if it's not activated * add public interface headers: stxxl/types, stxxl/aligned_alloc * add compatibility wrappers for standard extensions hash_map, hash_set, auto_ptr * MCSTL is only supported with g++ 4.2 and icpc 10.x * lots of small bugfixes * made the TPIE, BDB and LEDA_SM tests compile again * general code cleanup - fixed most compiler warnings - elimination of duplicate and unused code - cleaned up and sanitized debugging macros - no more 'using namespace std' and 'using namespace stxxl' - fixed ambiguities noted by g++ 4.3 - unify the #include directives - add/unify/cleanup copyright headers * general code reformatting (uncrustify) * add support for new compiler releases * portability fixes for different platforms * implemented file truncation on windows platforms * build system - lots of small modifications - now only requires GNU make 3.78 or later - check whether STXXL_ROOT has been set correctly and if unset, try autoconfiguration by creating make.settings.local with defaults - improved and simplified boost support for posix systems - Mac OS X support * changed all tests so that they behave well in their default parameters, system assumptions and return values and can be run from a script - use aligned_alloc/aligned_dealloc appropriately * added some more test programs * add misc/run_all_tests that runs all tests with sensible parameters, optionally via valgrind * checked all tests with valgrind - fixed use of uninitialized memory when writing to normal files - (optionally) initialize typed_blocks and payload data in test structs to suppress (most) uninitialized write errors when writing to disk files - fix mismatched new/delete in mcstl * update install and usage instructions * spell checked sources and documentation ------------------------------------------ Version 1.1.0 (31 July 2007) * stxxl is now hosted on SourceForge: http://stxxl.sourceforge.net/ * Restructured the source layout: - includes moves to include/ - introduced some public headers: stxxl.h, stxxl/algorithm, stxxl/bits, stxxl/deque, stxxl/io, stxxl/ksort, stxxl/mallocstats, stxxl/map, stxxl/mng, stxxl/priority_queue, stxxl/queue, stxxl/random, stxxl/scan, stxxl/sort, stxxl/stable_ksort, stxxl/stack, stxxl/stream, stxxl/timer, stxxl/vector - the header "stxxl" is no longer available, please use "stxxl.h" instead - the use of any other (internal) header is discouraged, additional public headers can be added as the need arises * Overhauled the build system: - merged configuration files, simplified option tuning - support object files and binaries with and without mcstl support existing in parallel - the library build creates stxxl.mk which can be included in an applications Makefile to set the correct compiler/linker switches for stxxl - similarly mcstxxl.mk is created for a stxxl compiled with mcstl support - add dependency tracking and improve parallelism during build * compiler support matrix: (for an up-to-date list, please see the doxygen documentation) compiler | stxxl stxxl + mcstl --------------+------------------------ GCC 4.3 | x x GCC 4.2 | x x GCC 4.1 | x n/a GCC 4.0 | x n/a GCC 3.4 | x n/a GCC 3.3 | o n/a GCC 2.95 | - n/a ICPC 9.1.051 | x x¹ ICPC 10.0.025 | x x¹ MSVC 2005 8.0 | x n/a x = full support o = partial support - = unsupported n/a = compiler does not support OpenMP which is needed by MCSTL ¹ = does not work with STL GCC 4.2.0 (ICPC bug), workaround: the first include in the program must be "stxxl/bits/common/intel_compatibility.h" * pipelined stream::sort, stream::runs_creator and stream::runs_merger are parallelized using MCSTL * obsolete files removed * fixed include dependencies * fixed lots of warnings * fixed lots of small bugs * add header compilation tests * stxxl::vector: implemented some more functions * const char* stxxl::get_version_string(): new function * comparators inherit from std::binary_function<> * cleanup, reformat the code to a consistent format (Thanks, uncrustify!) ------------------------------------------ Version 1.0e (4 June 2007) * Bugfix: for objects with the destructors, memory block were allocated in a non-aligned fashion * Internal CPU efficiency of Stxxl priority queue is improved (loser trees in external mergers are integrated) * Fixed bug with conversion of const/non-const iterators, implemented comparison and difference operations on const/non-const iterators (for vector, map, and deque) * Added operator[] to vector and deque iterators * stxxl::random_shuffle added * improved Makefile structure (thanx to Andreas Beckmann) * Allow parallel build for g++ ------------------------------------------ Version 1.0d (16 January 2007) * Support of Visual Studio 2005 Express (VC++ 8.0) * Algorithms and data structures of Stxxl can now use more than 4GB of main memory on 64-bit processors/compilers * Support of error reporting using the C++ exception mechanism ------------------------------------------- Version 1.0c (21 September 2006) * An implementation of an I/O-efficient deque * STXXL uses MCSTL library (optional) to improve the performance of stxxl::sort and pipelined sort on SMP and multicore processors. ------------------------------------------- Version 0.99 (22 March 2006) * Better compiler support: g++ (versions 3.3.x-4.0.x) and Microsoft Visual C++ 7.1 (.NET) * New B+Tree-based implementation of map (compatible with all listed above compilers): I/O-efficient map ------------------------------------------ Version 0.9 (9 August 2005) * STXXL has been ported to Windows. It now can be run under Windows XP and Windows 2000 * STXXL can be compiled now by g++ (versions 3.0.x-3.4.x, 4.0.x) and Microsoft Visual C++ 7.1 (.NET) * New data structure: I/O efficient FIFO queue ------------------------------------------ Version 0.77 (24 March 2005) * An implementation of queue is available ------------------------------------------ Version 0.75 (23 March 2005) * An implementation of map based on B+tree is available ------------------------------------------ Version 0.7 (25 January 2005) * The implementation of pipelining is extended and improved ------------------------------------------ Version 0.6 (5 September 2004) * Tested implementation of the stream package (aka pipelining) is available ------------------------------------------ Version 0.5 (21 November 2003) * The first implementation of the stream package (aka pipelining) is available * Priority queue is available ------------------------------------------ Version 0.2 (Summer 2003) * The first public release * Vectors, stacks, sorting, scanning are available ------------------------------------------ # Local Variables: # mode: text # mode: flyspell # End: stxxl-1.4.1/include/stxxl/algorithm000644 001411 000144 00000001177 12405153572 017225 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/algorithm * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include //#include #include stxxl-1.4.1/include/stxxl/version.h000644 001411 000144 00000000776 12405153572 017156 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/version.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/aligned_alloc000644 001411 000144 00000001044 12405153572 020005 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/aligned_alloc * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/unordered_map000644 001411 000144 00000001047 12410750556 020060 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/unordered_map * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Markus Westphal * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/types000644 001411 000144 00000001024 12405153572 016372 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/types * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/priority_queue000644 001411 000144 00000001045 12405153572 020316 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/priority_queue * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/ksort000644 001411 000144 00000001015 12405153572 016370 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/ksort * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/map000644 001411 000144 00000001017 12405153572 016005 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/map * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/stack000644 001411 000144 00000001023 12405153572 016332 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/stack * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/deque000644 001411 000144 00000001023 12405153572 016330 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/deque * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/stats000644 001411 000144 00000001022 12405153572 016362 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/stats * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/sort000644 001411 000144 00000001013 12405153572 016213 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/sort * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/cmdline000644 001411 000144 00000001010 12405153572 016634 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/cmdline * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/vector000644 001411 000144 00000001025 12405153572 016531 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/vector * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/mng000644 001411 000144 00000001156 12405153572 016015 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/mng * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007, 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include stxxl-1.4.1/include/stxxl/stable_ksort000644 001411 000144 00000001033 12405153572 017722 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/stable_ksort * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/stream000644 001411 000144 00000001074 12405153572 016526 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/stream * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include stxxl-1.4.1/include/stxxl/io000644 001411 000144 00000001005 12405153572 015634 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/io * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/random_shuffle000644 001411 000144 00000001044 12405153572 020224 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/random_shuffle * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/random000644 001411 000144 00000001017 12405153572 016510 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/random * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/scan000644 001411 000144 00000001013 12405153572 016150 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/scan * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/sorter000644 001411 000144 00000001012 12405153572 016541 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/sorter * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/request000644 001411 000144 00000001102 12405153572 016713 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/request * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include stxxl-1.4.1/include/stxxl/bits/version.h000644 001411 000144 00000005636 12405153572 020117 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/version.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007, 2011 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_VERSION_HEADER #define STXXL_VERSION_HEADER #include #include #include STXXL_BEGIN_NAMESPACE // STXXL_VERSION_{MAJOR,MINOR,PATCH} are defined in cmake generated config.h // construct an integer version number, like "10400" for "1.4.0". #define STXXL_VERSION_INTEGER (STXXL_VERSION_MAJOR* 10000 + STXXL_VERSION_MINOR* 100 + STXXL_VERSION_PATCH) #define stringify_(x) #x #define stringify(x) stringify_(x) //! Return "X.Y.Z" version string (of headers) inline const char * get_version_string() { return STXXL_VERSION_STRING; } //! Return longer "X.Y.Z (feature) (version)" version string (of headers) inline const char * get_version_string_long() { return "STXXL" " v" STXXL_VERSION_STRING #ifdef STXXL_VERSION_PHASE " (" STXXL_VERSION_PHASE ")" #endif #ifdef STXXL_VERSION_GIT_SHA1 " (git " STXXL_VERSION_GIT_SHA1 ")" #endif // STXXL_VERSION_GIT_SHA1 #if STXXL_PARALLEL " + gnu parallel(" stringify(__GLIBCXX__) ")" #endif // STXXL_PARALLEL #if STXXL_BOOST_CONFIG " + Boost " stringify(BOOST_VERSION) #endif ; } #undef stringify #undef stringify_ //! return X if the STXXL library version is X.Y.Z int version_major(); //! return Y if the STXXL library version is X.Y.Z int version_minor(); //! return Z if the STXXL library version is X.Y.Z int version_patch(); //! return integer version number of the STXXL library int version_integer(); //! returns "X.Y.Z" version string of library const char * get_library_version_string(); //! returns longer "X.Y.Z (feature) (version)" string of library const char * get_library_version_string_long(); //! Check for a mismatch between library and headers inline int check_library_version() { if (version_major() != STXXL_VERSION_MAJOR) return 1; if (version_minor() != STXXL_VERSION_MINOR) return 2; if (version_patch() != STXXL_VERSION_PATCH) return 3; return 0; } //! Check and print mismatch between header and library versions inline void print_library_version_mismatch() { if (stxxl::check_library_version() != 0) { STXXL_ERRMSG("version mismatch between headers" << " (" << STXXL_VERSION_STRING ") and library" << " (" << get_library_version_string() << ")"); } } STXXL_END_NAMESPACE #endif // !STXXL_VERSION_HEADER stxxl-1.4.1/include/stxxl/bits/defines.h000644 001411 000144 00000007074 12405153572 020045 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/defines.h * * Document all defines that may change the behavior of stxxl. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008-2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_DEFINES_HEADER #define STXXL_DEFINES_HEADER //#define STXXL_HAVE_MMAP_FILE 0/1 //#define STXXL_HAVE_SIMDISK_FILE 0/1 //#define STXXL_HAVE_BOOSTFD_FILE 0/1 //#define STXXL_HAVE_WINCALL_FILE 0/1 //#define STXXL_HAVE_WBTL_FILE 0/1 //#define STXXL_HAVE_LINUXAIO_FILE 0/1 // default: 0/1 (platform and type dependent) // used in: io/*_file.h, io/*_file.cpp, mng/mng.cpp // affects: library // effect: enables/disables some file implementations //#define STXXL_CHECK_BLOCK_ALIGNING // default: not defined // used in: io/*_file.cpp // effect: call request::check_alignment() from request::request(...) //#define STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION 0/1 // default: 1 // used in: io/*_queue*.cpp // affects: library // effect: check (and warn) for multiple concurrently pending I/O requests // for the same block, usually causing coherency problems on // out-of-order execution //#define STXXL_DO_NOT_COUNT_WAIT_TIME // default: not defined // used in: io/iostats.{h,cpp} // effect: makes calls to wait time counting functions no-ops //#define STXXL_WAIT_LOG_ENABLED // default: not defined // used in: common/log.cpp, io/iostats.cpp // effect: writes wait timing information to the file given via environment // variable STXXLWAITLOGFILE, does nothing if this is not defined //#define STXXL_PRINT_TIMESTAMP_ALWAYS // default: not defined // used in: common/verbose.cpp // affects: library // effect: prefix all MSG/ERRMSG/VERBOSE with elapsed time since program start //#define STXXL_SORT_OPTIMAL_PREFETCHING 0/1 // default: 1 // used in: algo/*sort.h, stream/sort_stream.h // effect if defined to 0: does not reorder prefetch requests to a disk // optimal schedule (Hutchinson, Sanders, Vitter: Duality between // prefetching and queued writing on parallel disks, 2005) //#define STXXL_CHECK_ORDER_IN_SORTS 0/1 // default: 0 // used in: algo/*sort.h, stream/sort_stream.h, containers/priority_queue.h // effect if set to 1: perform additional checking of sorted results //#define STXXL_NO_WARN_RECURSIVE_SORT // default: not defined // used in: algo/sort_base.h // affects: programs // effect if defined: does not print error messages about possibly inefficient // recursive merging //#define STXXL_HACK_SINGLE_IO_THREAD // default: not defined // used in: io/disk_queues.h // affects: programs // effect if defined: uses only a single I/O thread instead of one per disk // used e.g. by EcoSort which puts input file, output file and // scratch on a single disk (RAID0) //#define STXXL_MNG_COUNT_ALLOCATION 0/1 // default: 1 // used in: mng/block_manager.h // effect if defined: counts current, total and maximum allocation of bytes in // block manager. The numbers are exported via block_manager's get_ // functions. This can be used to determine the maximum disk space required by // an application. //#define STXXL_NO_DEPRECATED 0/1 // default: 0 // used in deprecated.h // turns off deprecated warnings for some forced template instantiations #endif // !STXXL_DEFINES_HEADER stxxl-1.4.1/include/stxxl/bits/utils/malloc.h000644 001411 000144 00000011243 12405375303 021026 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/utils/malloc.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_UTILS_MALLOC_HEADER #define STXXL_UTILS_MALLOC_HEADER #include #include #if STXXL_HAVE_MALLINFO_PROTO #include #endif #include #include #include STXXL_BEGIN_NAMESPACE //! Access to some useful malloc statistics. //! malloc is default C++ allocator class malloc_stats { #if STXXL_HAVE_MALLINFO_PROTO public: typedef int return_type; //! Returns number of bytes allocated from system not including mmapped regions. return_type from_system_nmmap() const { struct mallinfo info = mallinfo(); return info.arena; } //! Returns number of free chunks. return_type free_chunks() const { struct mallinfo info = mallinfo(); return info.ordblks; } //! Number of bytes allocated and in use. return_type used() const { struct mallinfo info = mallinfo(); return info.uordblks; } //! Number of bytes allocated but not in use. return_type not_used() const { struct mallinfo info = mallinfo(); return info.fordblks; } //! Top-most, releasable (via malloc_trim) space (bytes). return_type releasable() const { struct mallinfo info = mallinfo(); return info.keepcost; } //! Maximum total allocated space (bytes) (always 0 ?). return_type max_allocated() const { struct mallinfo info = mallinfo(); return info.usmblks; } //! Number of fastbin blocks. return_type fastbin_blocks() const { struct mallinfo info = mallinfo(); return info.smblks; } //! Space available in freed fastbin blocks (bytes). return_type fastbin_free() const { struct mallinfo info = mallinfo(); return info.fsmblks; } //! Returns number of bytes allocated from system using mmap. return_type from_system_mmap() const { struct mallinfo info = mallinfo(); return info.hblkhd; } //! Number of chunks allocated via mmap(). return_type mmap_chunks() const { struct mallinfo info = mallinfo(); return info.hblks; } //! Returns \b total number of bytes allocated from system including mmapped regions. return_type from_system_total() const { return from_system_nmmap() + from_system_mmap(); } #endif }; //! Prints current malloc statistics in a convenient way. inline std::ostream& operator << (std::ostream& s, const malloc_stats& st) { #if STXXL_HAVE_MALLINFO_PROTO s << "MALLOC statistics" << std::endl; s << "=================================================================" << std::endl; s << "Space allocated from system not using mmap: " << st.from_system_nmmap() << " bytes" << std::endl; s << " number of free chunks : " << st.free_chunks() << std::endl; s << " space allocated and in use : " << st.used() << " bytes" << std::endl; s << " space allocated but not in use : " << st.not_used() << " bytes" << std::endl; s << " top-most, releasable (via malloc_trim) space: " << st.releasable() << " bytes" << std::endl; s << " maximum total allocated space (?) : " << st.max_allocated() << " bytes" << std::endl; s << " FASTBIN blocks " << std::endl; s << " number of fastbin blocks: " << st.fastbin_blocks() << std::endl; s << " space available in freed fastbin blocks: " << st.fastbin_free() << " bytes" << std::endl; s << "Space allocated from system using mmap: " << st.from_system_mmap() << " bytes" << std::endl; s << " number of chunks allocated via mmap(): " << st.mmap_chunks() << std::endl; s << "Total space allocated from system (mmap and not mmap): " << st.from_system_total() << " bytes" << std::endl; s << "=================================================================" << std::endl; #else s << "MALLOC statistics are not supported on this platform"; STXXL_UNUSED(st); #endif return s; } class malloc_setup { }; STXXL_END_NAMESPACE #endif // !STXXL_UTILS_MALLOC_HEADER stxxl-1.4.1/include/stxxl/bits/compat/type_traits.h000644 001411 000144 00000004234 12405375303 022253 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/compat/type_traits.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009-2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMPAT_TYPE_TRAITS_HEADER #define STXXL_COMPAT_TYPE_TRAITS_HEADER #include #include #if __cplusplus >= 201103L #include #elif defined(__GNUG__) && (__GNUC__ >= 4) #include #elif STXXL_BOOST_CONFIG #include #endif STXXL_BEGIN_NAMESPACE namespace compat { #if __cplusplus >= 201103L using std::remove_const; #elif defined(__GNUG__) && (__GNUC__ >= 4) using std::tr1::remove_const; #elif STXXL_BOOST_CONFIG using boost::remove_const; #else template struct remove_const { typedef Type type; }; template struct remove_const { typedef Type type; }; #endif #if defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40300) // That is a small subset of what GCC 4.3 does: // Utility for finding the signed versions of unsigned integral types. template struct _make_signed { typedef Type type; }; template <> struct _make_signed { typedef signed char type; }; template <> struct _make_signed { typedef signed char type; }; template <> struct _make_signed { typedef signed short type; }; template <> struct _make_signed { typedef signed int type; }; template <> struct _make_signed { typedef signed long type; }; template <> struct _make_signed { typedef signed long long type; }; template struct make_signed { typedef typename _make_signed::type type; }; #endif } // namespace compat STXXL_END_NAMESPACE #endif // !STXXL_COMPAT_TYPE_TRAITS_HEADER stxxl-1.4.1/include/stxxl/bits/compat/unique_ptr.h000644 001411 000144 00000003314 12405375303 022075 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/compat/unique_ptr.h * * compatibility interface to unique_ptr (C++0x), previously auto_ptr * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMPAT_UNIQUE_PTR_HEADER #define STXXL_COMPAT_UNIQUE_PTR_HEADER #include #include STXXL_BEGIN_NAMESPACE template struct compat_unique_ptr { #if __cplusplus >= 201103L && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400) typedef std::unique_ptr result; #else // auto_ptr is inherently broken and is deprecated by unique_ptr in c++0x typedef std::auto_ptr result; #endif }; STXXL_END_NAMESPACE #if defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) == 30400) namespace workaround_gcc_3_4 { // std::swap in gcc 3.4 is broken, tmp is declared const there template inline void swap(Type& a, Type& b) { // concept requirements __glibcxx_function_requires(_SGIAssignableConcept) Type tmp = a; a = b; b = tmp; } } // namespace workaround_gcc_3_4 namespace std { // overload broken std::swap to call a working swap() template inline void swap(std::auto_ptr& a, std::auto_ptr& b) { workaround_gcc_3_4::swap(a, b); } } // namespace std #endif #endif // !STXXL_COMPAT_UNIQUE_PTR_HEADER stxxl-1.4.1/include/stxxl/bits/compat/hash_map.h000644 001411 000144 00000005431 12411366426 021467 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/compat/hash_map.h * * compatibility interface to C++ standard extension hash_map * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008, 2010, 2011 Andreas Beckmann * Copyright (C) 2009, 2010 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMPAT_HASH_MAP_HEADER #define STXXL_COMPAT_HASH_MAP_HEADER #include #include #if __cplusplus >= 201103L #include #elif STXXL_MSVC #include #elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ (!defined(__ICC) || (__ICC > 1110)) #include #else #include #endif STXXL_BEGIN_NAMESPACE template struct compat_hash { #if __cplusplus >= 201103L typedef std::hash result; #elif STXXL_MSVC typedef stdext::hash_compare result; #elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ (!defined(__ICC) || (__ICC > 1110)) typedef std::tr1::hash result; #else typedef __gnu_cxx::hash result; #endif }; template ::result> struct compat_hash_map { #if __cplusplus >= 201103L typedef std::unordered_map result; #elif STXXL_MSVC typedef stdext::hash_map result; #elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ (!defined(__ICC) || (__ICC > 1110)) typedef std::tr1::unordered_map result; #else typedef __gnu_cxx::hash_map result; #endif }; template ::result> struct compat_hash_multimap { #if __cplusplus >= 201103L typedef std::unordered_multimap result; #elif STXXL_MSVC typedef stdext::hash_multimap result; #elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ (!defined(__ICC) || (__ICC > 1110)) typedef std::tr1::unordered_multimap result; #else typedef __gnu_cxx::hash_multimap result; #endif }; STXXL_END_NAMESPACE #endif // !STXXL_COMPAT_HASH_MAP_HEADER stxxl-1.4.1/include/stxxl/bits/libstxxl.h000644 001411 000144 00000001360 12405153572 020271 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/libstxxl.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_LIBSTXXL_HEADER #define STXXL_LIBSTXXL_HEADER #include #if STXXL_MSVC #ifndef STXXL_LIBNAME #define STXXL_LIBNAME "stxxl" #endif //-tb #pragma comment (lib, "lib" STXXL_LIBNAME ".lib") #endif #endif // !STXXL_LIBSTXXL_HEADER stxxl-1.4.1/include/stxxl/bits/deprecated.h000644 001411 000144 00000002211 12405153572 020514 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/deprecated.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008-2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_DEPRECATED_HEADER #define STXXL_DEPRECATED_HEADER #include // deprecated functions #if STXXL_NO_DEPRECATED // dont issue deprecated warnings for forced instantiation tests -tb #define STXXL_DEPRECATED(x) x #elif STXXL_MSVC #define STXXL_DEPRECATED(x) __declspec(deprecated) x #elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 30400) // no __attribute__ ((__deprecated__)) in GCC 3.3 #define STXXL_DEPRECATED(x) x #else #define STXXL_DEPRECATED(x) x __attribute__ ((__deprecated__)) #endif #endif // !STXXL_DEPRECATED_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/config.h.in000644 001411 000144 00000010425 12405153572 020274 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/bits/config.h.in * * Template file processed by cmake to set all define switches for this build * according to the cmake build options. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012-2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONFIG_HEADER #define STXXL_CONFIG_HEADER // the STXXL library version variables #define STXXL_VERSION_MAJOR ${STXXL_VERSION_MAJOR} #define STXXL_VERSION_MINOR ${STXXL_VERSION_MINOR} #define STXXL_VERSION_PATCH ${STXXL_VERSION_PATCH} #define STXXL_VERSION_STRING "${STXXL_VERSION_STRING}" #define STXXL_VERSION_PHASE "${STXXL_VERSION_PHASE}" // if this is a git repository, add the refspec and commit sha #cmakedefine STXXL_VERSION_GIT_REFSPEC "${STXXL_VERSION_GIT_REFSPEC}" #cmakedefine STXXL_VERSION_GIT_SHA1 "${STXXL_VERSION_GIT_SHA1}" #cmakedefine STXXL_DIRECT_IO_OFF ${STXXL_DIRECT_IO_OFF} // default: 0/1 (platform dependent) // cmake: detection of platform and flag // effect: disables use of O_DIRECT flag on unsupported platforms #cmakedefine STXXL_HAVE_MMAP_FILE ${STXXL_HAVE_MMAP_FILE} // default: 0/1 (platform dependent) // used in: io/mmap_file.h/cpp // effect: enables/disables memory mapped file implementation #cmakedefine STXXL_HAVE_LINUXAIO_FILE ${STXXL_HAVE_LINUXAIO_FILE} // default: 0/1 (platform dependent) // used in: io/linuxaio_file.h/cpp // effect: enables/disables Linux AIO file implementation #cmakedefine STXXL_POSIX_THREADS ${STXXL_POSIX_THREADS} // default: off // cmake: detection of pthreads by cmake // effect: uses POSIX thread and mutexes on linux #cmakedefine STXXL_STD_THREADS ${STXXL_STD_THREADS} // default: off // cmake: detection of and // effect: uses std thread and mutex on windows or (forced on linux) #cmakedefine STXXL_WINDOWS ${STXXL_WINDOWS} // default: off // cmake: detection of ms windows platform (32- or 64-bit) // effect: enables windows-specific api calls (mingw or msvc) #cmakedefine STXXL_MSVC ${STXXL_MSVC} // default: off // cmake: detection of ms visual c++ via CMake (contains version number) // effect: enables msvc-specific headers and macros #cmakedefine STXXL_HAVE_CXX11_RANGE_FOR_LOOP ${STXXL_HAVE_CXX11_RANGE_FOR_LOOP} // default: off // run-time: detection C++11 support for "for (auto i : obj) { }" // effect: enables some C++11 construct (currently only allowed in examples) #cmakedefine STXXL_HAVE_SYNC_ADD_AND_FETCH ${STXXL_HAVE_SYNC_ADD_AND_FETCH} // default: off // cmake: detection of __sync_add_and_fetch() intrinsic // effect: enables use of atomics in counting_ptr #cmakedefine STXXL_PARALLEL_MODE_EXPLICIT ${STXXL_PARALLEL_MODE_EXPLICIT} // default: off // cmake: -DUSE_GNU_PARALLEL=ON // effect: explicitly enables use of __gnu_parallel algorithms #cmakedefine STXXL_BOOST_CONFIG ${STXXL_BOOST_CONFIG} #cmakedefine STXXL_BOOST_FILESYSTEM ${STXXL_BOOST_FILESYSTEM} #cmakedefine STXXL_BOOST_RANDOM ${STXXL_BOOST_RANDOM} #cmakedefine STXXL_BOOST_THREADS ${STXXL_BOOST_THREADS} #cmakedefine STXXL_BOOST_TIMESTAMP ${STXXL_BOOST_TIMESTAMP} // default: off // cmake: -DUSE_BOOST=ON // effect: enables use of boost libraries in different parts of STXXL. #if STXXL_BOOST_CONFIG #include #endif #cmakedefine STXXL_STD_RANDOM ${STXXL_STD_RANDOM} // default: off // cmake: detection of // effect: uses std random generator on windows or (forced on linux) #cmakedefine STXXL_HAVE_MALLINFO_PROTO ${STXXL_HAVE_MALLINFO_PROTO} // default: off // cmake: detection of mallinfo() function in // effect: used by stxxl_tool/mallinfo for malloc stats #cmakedefine STXXL_HAVE_MLOCK_PROTO ${STXXL_HAVE_MLOCK_PROTO} // default: off // cmake: detection of mlock() function in // effect: used by stxxl_tool/mlock for locking physical pages #cmakedefine STXXL_WITH_VALGRIND ${STXXL_WITH_VALGRIND} // default: off // cmake: option USE_VALGRIND=ON // effect: run all tests with valgrind and pre-initialize some memory buffers #endif // !STXXL_CONFIG_HEADER stxxl-1.4.1/include/stxxl/bits/namespace.h000644 001411 000144 00000001222 12405153572 020351 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/namespace.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_NAMESPACE_HEADER #define STXXL_NAMESPACE_HEADER #define STXXL_BEGIN_NAMESPACE namespace stxxl { #define STXXL_END_NAMESPACE \ } #endif // !STXXL_NAMESPACE_HEADER stxxl-1.4.1/include/stxxl/bits/common/simple_vector.h000644 001411 000144 00000011037 12410750556 022566 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/simple_vector.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2011 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_SIMPLE_VECTOR_HEADER #define STXXL_COMMON_SIMPLE_VECTOR_HEADER #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup support //! \{ /*! * Simpler non-growing vector without initialization. * * simple_vector can be used a replacement for std::vector when only a * non-growing array is needed. The advantages of simple_vector are that it * does not initilize memory for POD types (faster), allows simpler inlines and * is less error prone to copying and other problems.. */ template class simple_vector : private noncopyable { public: typedef ValueType value_type; typedef size_t size_type; protected: //! size of allocated memory size_type m_size; //! pointer to allocated memory area value_type* m_array; public: // *** simple pointer iterators typedef value_type* iterator; typedef const value_type* const_iterator; typedef value_type& reference; typedef const value_type& const_reference; public: //! allocate empty simple vector simple_vector() : m_size(0), m_array(NULL) { } //! allocate vector's memory simple_vector(size_type sz) : m_size(sz), m_array(NULL) { if (m_size > 0) m_array = new value_type[m_size]; } //! swap vector with another one void swap(simple_vector& obj) { std::swap(m_size, obj.m_size); std::swap(m_array, obj.m_array); } //! delete vector ~simple_vector() { delete[] m_array; } //! return iterator to beginning of vector iterator data() { return m_array; } //! return iterator to beginning of vector const_iterator data() const { return m_array; } //! return mutable iterator to first element iterator begin() { return m_array; } //! return constant iterator to first element const_iterator begin() const { return m_array; } //! return constant iterator to first element const_iterator cbegin() const { return begin(); } //! return mutable iterator beyond last element iterator end() { return m_array + m_size; } //! return constant iterator beyond last element const_iterator end() const { return m_array + m_size; } //! return constant iterator beyond last element const_iterator cend() const { return end(); } //! return number of items in vector size_type size() const { return m_size; } //! return the i-th position of the vector reference operator [] (size_type i) { assert(i < m_size); return *(begin() + i); } //! return constant reference to the i-th position of the vector const_reference operator [] (size_type i) const { assert(i < m_size); return *(begin() + i); } //! resize the array to contain exactly newsize items void resize(size_type newsize) { if (m_array) { STXXL_MSG("Warning: resizing non-empty simple_vector"); value_type* tmp = m_array; m_array = new value_type[newsize]; memcpy((void*)m_array, (void*)tmp, sizeof(value_type) * STXXL_MIN(m_size, newsize)); delete[] tmp; m_size = newsize; } else { m_array = new value_type[newsize]; m_size = newsize; } } //! Zero the whole array content. void memzero() { memset(m_array, 0, m_size * sizeof(value_type)); } }; // \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::simple_vector& a, stxxl::simple_vector& b) { a.swap(b); } } // namespace std #endif // !STXXL_COMMON_SIMPLE_VECTOR_HEADER stxxl-1.4.1/include/stxxl/bits/common/new_alloc.h000644 001411 000144 00000007475 12414452316 021666 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/new_alloc.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2006 Roman Dementiev * Copyright (C) 2007, 2008, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_NEW_ALLOC_HEADER #define STXXL_COMMON_NEW_ALLOC_HEADER #include #include #include STXXL_BEGIN_NAMESPACE template class new_alloc; template struct new_alloc_rebind; template struct new_alloc_rebind{ typedef new_alloc other; }; template struct new_alloc_rebind { typedef std::allocator other; }; // designed for typed_block (to use with std::vector) template class new_alloc { public: // type definitions typedef Type value_type; typedef Type* pointer; typedef const Type* const_pointer; typedef Type& reference; typedef const Type& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; // rebind allocator to type Rebind, use new_alloc only if Rebind == Type template struct rebind { typedef typename new_alloc_rebind::other other; }; // return address of values pointer address(reference value) const { return &value; } const_pointer address(const_reference value) const { return &value; } new_alloc() throw () { } new_alloc(const new_alloc&) throw () { } template new_alloc(const new_alloc&) throw () { } ~new_alloc() throw () { } template operator std::allocator() { static std::allocator helper_allocator; return helper_allocator; } // return maximum number of elements that can be allocated size_type max_size() const throw () { return std::numeric_limits::max() / sizeof(Type); } // allocate but don't initialize num elements of type Type pointer allocate(size_type num, const void* = 0) { return static_cast(Type::operator new (num * sizeof(Type))); } // _GLIBCXX_RESOLVE_LIB_DEFECTS // 402. wrong new expression in [some_] allocator::construct // initialize elements of allocated storage p with value value void construct(pointer p, const Type& value) { // initialize memory with placement new ::new ((void*)p)Type(value); } #ifdef __GXX_EXPERIMENTAL_CXX0X__ template void construct(pointer p, Args&& ... args) { ::new ((void*)p)Type(std::forward(args) ...); } #endif // destroy elements of initialized storage p void destroy(pointer p) { // destroy objects by calling their destructor p->~Type(); } // deallocate storage p of deleted elements void deallocate(pointer p, size_type /*num*/) { Type::operator delete (p); } }; // return that all specializations of this allocator are interchangeable template inline bool operator == (const new_alloc&, const new_alloc&) throw () { return true; } template inline bool operator != (const new_alloc&, const new_alloc&) throw () { return false; } STXXL_END_NAMESPACE #endif // !STXXL_COMMON_NEW_ALLOC_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/common/types.h000644 001411 000144 00000003603 12405375303 021054 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/types.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_TYPES_HEADER #define STXXL_COMMON_TYPES_HEADER #include #include STXXL_BEGIN_NAMESPACE #if STXXL_MSVC typedef __int8 int8; typedef unsigned __int8 uint8; typedef __int16 int16; typedef unsigned __int16 uint16; typedef __int32 int32; typedef unsigned __int32 uint32; typedef __int64 int64; typedef unsigned __int64 uint64; #else typedef char int8; typedef unsigned char uint8; typedef short int16; typedef unsigned short uint16; typedef int int32; typedef unsigned int uint32; typedef long long int int64; typedef unsigned long long int uint64; #endif // integer types declarations enum { my_pointer_size = sizeof(void*) }; template struct choose_int_types { }; template <> struct choose_int_types<4> // for 32-bit processors/compilers { typedef int32 int_type; typedef uint32 unsigned_type; }; template <> struct choose_int_types<8> // for 64-bit processors/compilers { typedef int64 int_type; typedef uint64 unsigned_type; }; typedef choose_int_types::int_type int_type; typedef choose_int_types::unsigned_type unsigned_type; typedef unsigned_type internal_size_type; // fits in internal memory typedef uint64 external_size_type; // may require external memory STXXL_END_NAMESPACE #endif // !STXXL_COMMON_TYPES_HEADER stxxl-1.4.1/include/stxxl/bits/common/exithandler.h000644 001411 000144 00000002570 12414452316 022221 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/exithandler.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_EXITHANDLER_HEADER #define STXXL_COMMON_EXITHANDLER_HEADER #include STXXL_BEGIN_NAMESPACE // There are several possibilities for the exit handlers. To use the default // implementation (which uses atexit()), nothing special has to be done. // // To work around problems with atexit() being used in a dll you may #define // STXXL_NON_DEFAULT_EXIT_HANDLER at library compilation time. In this case // the library/application should call stxxl::run_exit_handlers() during // shutdown. // // To provide your own exit handler implementation, #define // STXXL_EXTERNAL_EXIT_HANDLER and implement stxxl::register_exit_handler(void // (*)(void)) and stxxl::run_exit_handlers() in your application. int register_exit_handler(void (* function)(void)); void run_exit_handlers(); STXXL_END_NAMESPACE #endif // !STXXL_COMMON_EXITHANDLER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/common/is_sorted.h000644 001411 000144 00000003432 12405375303 021703 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/is_sorted.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_IS_SORTED_HEADER #define STXXL_COMMON_IS_SORTED_HEADER #include STXXL_BEGIN_NAMESPACE template bool is_sorted_helper(ForwardIterator first, ForwardIterator last) { if (first == last) return true; ForwardIterator next = first; for (++next; next != last; first = next, ++next) { if (*next < *first) return false; } return true; } template bool is_sorted_helper(ForwardIterator first, ForwardIterator last, StrictWeakOrdering comp) { if (first == last) return true; ForwardIterator next = first; for (++next; next != last; first = next, ++next) { if (comp(*next, *first)) return false; } return true; } template bool is_sorted(ForwardIterator first, ForwardIterator last) { return is_sorted_helper(first, last); } template bool is_sorted(ForwardIterator first, ForwardIterator last, StrictWeakOrdering comp) { return is_sorted_helper(first, last, comp); } STXXL_END_NAMESPACE #endif // !STXXL_COMMON_IS_SORTED_HEADER stxxl-1.4.1/include/stxxl/bits/common/aligned_alloc.h000644 001411 000144 00000012413 12414452316 022464 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/aligned_alloc.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_ALIGNED_ALLOC_HEADER #define STXXL_COMMON_ALIGNED_ALLOC_HEADER #include #include #include #include #ifndef STXXL_VERBOSE_ALIGNED_ALLOC #define STXXL_VERBOSE_ALIGNED_ALLOC STXXL_VERBOSE2 #endif STXXL_BEGIN_NAMESPACE template struct aligned_alloc_settings { static bool may_use_realloc; }; template bool aligned_alloc_settings::may_use_realloc = true; // meta_info_size > 0 is needed for array allocations that have overhead // // meta_info // aligned begin of data unallocated behind data // v v v // ----===============#MMMM========================------ // ^ ^^ ^ // buffer result result+m_i_size+size // pointer to buffer // (---) unallocated, (===) allocated memory template inline void * aligned_alloc(size_t size, size_t meta_info_size = 0) { STXXL_VERBOSE2("stxxl::aligned_alloc<" << Alignment << ">(), size = " << size << ", meta info size = " << meta_info_size); #if !defined(STXXL_WASTE_MORE_MEMORY_FOR_IMPROVED_ACCESS_AFTER_ALLOCATED_MEMORY_CHECKS) // malloc()/realloc() variant that frees the unused amount of memory // after the data area of size 'size'. realloc() from valgrind does not // preserve the old memory area when shrinking, so out-of-bounds // accesses can't be detected easily. // Overhead: about Alignment bytes. size_t alloc_size = Alignment + sizeof(char*) + meta_info_size + size; char* buffer = (char*)std::malloc(alloc_size); #else // More space consuming and memory fragmenting variant using // posix_memalign() instead of malloc()/realloc(). Ensures that the end // of the data area (of size 'size') will match the end of the allocated // block, so no corrections are neccessary and // access-behind-allocated-memory problems can be easily detected by // valgrind. Usually produces an extra memory fragment of about // Alignment bytes. // Overhead: about 2 * Alignment bytes. size_t alloc_size = Alignment * div_ceil(sizeof(char*) + meta_info_size, Alignment) + size; char* buffer; if (posix_memalign((void**)&buffer, Alignment, alloc_size) != 0) throw std::bad_alloc(); #endif if (buffer == NULL) throw std::bad_alloc(); #ifdef STXXL_ALIGNED_CALLOC memset(buffer, 0, alloc_size); #endif char* reserve_buffer = buffer + sizeof(char*) + meta_info_size; char* result = reserve_buffer + Alignment - (((unsigned_type)reserve_buffer) % (Alignment)) - meta_info_size; STXXL_VERBOSE2("stxxl::aligned_alloc<" << Alignment << ">() address " << (void*)result << " lost " << (result - buffer) << " bytes"); //-tb: check that there is space for one char* before the "result" pointer // delivered to the user. this char* is set below to the beginning of the // allocated area. assert(long(result - buffer) >= long(sizeof(char*))); // free unused memory behind the data area // so access behind the requested size can be recognized size_t realloc_size = (result - buffer) + meta_info_size + size; if (realloc_size < alloc_size && aligned_alloc_settings::may_use_realloc) { char* realloced = (char*)std::realloc(buffer, realloc_size); if (buffer != realloced) { // hmm, realloc does move the memory block around while shrinking, // might run under valgrind, so disable realloc and retry STXXL_ERRMSG("stxxl::aligned_alloc: disabling realloc()"); std::free(realloced); aligned_alloc_settings::may_use_realloc = false; return aligned_alloc(size, meta_info_size); } assert(result + size <= buffer + realloc_size); } *(((char**)result) - 1) = buffer; STXXL_VERBOSE2( "stxxl::aligned_alloc<" << Alignment << ">(), allocated at " << (void*)buffer << " returning " << (void*)result); STXXL_VERBOSE_ALIGNED_ALLOC( "stxxl::aligned_alloc<" << Alignment << ">(size = " << size << ", meta info size = " << meta_info_size << ") => buffer = " << (void*)buffer << ", ptr = " << (void*)result); return result; } template inline void aligned_dealloc(void* ptr) { if (!ptr) return; char* buffer = *(((char**)ptr) - 1); STXXL_VERBOSE_ALIGNED_ALLOC("stxxl::aligned_dealloc<" << Alignment << ">(), ptr = " << ptr << ", buffer = " << (void*)buffer); std::free(buffer); } STXXL_END_NAMESPACE #endif // !STXXL_COMMON_ALIGNED_ALLOC_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/common/mutex.h000644 001411 000144 00000006404 12405375303 021054 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/mutex.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_MUTEX_HEADER #define STXXL_COMMON_MUTEX_HEADER #include #include #if STXXL_STD_THREADS #include #elif STXXL_BOOST_THREADS #include #elif STXXL_POSIX_THREADS #include #include #include #else #error "Thread implementation not detected." #endif STXXL_BEGIN_NAMESPACE #if STXXL_STD_THREADS typedef std::mutex mutex; #elif STXXL_BOOST_THREADS typedef boost::mutex mutex; #elif STXXL_POSIX_THREADS class mutex : private noncopyable { //! mutex handle pthread_mutex_t m_mutex; public: //! construct unlocked mutex mutex() { STXXL_CHECK_PTHREAD_CALL(pthread_mutex_init(&m_mutex, NULL)); } //! destroy mutex handle ~mutex() { // try simple delete first int res = pthread_mutex_destroy(&m_mutex); if (res == 0) return; // try to lock and unlock mutex res = pthread_mutex_trylock(&m_mutex); if (res == 0 || res == EBUSY) { STXXL_CHECK_PTHREAD_CALL(pthread_mutex_unlock(&m_mutex)); } else { STXXL_THROW_ERRNO2(resource_error, "pthread_mutex_trylock() failed", res); } STXXL_CHECK_PTHREAD_CALL(pthread_mutex_destroy(&m_mutex)); } //! lock mutex, may block void lock() { STXXL_CHECK_PTHREAD_CALL(pthread_mutex_lock(&m_mutex)); } //! unlock mutex void unlock() { STXXL_CHECK_PTHREAD_CALL(pthread_mutex_unlock(&m_mutex)); } //! return platform specific handle pthread_mutex_t & native_handle() { return m_mutex; } }; #endif #if STXXL_STD_THREADS typedef std::unique_lock scoped_mutex_lock; #elif STXXL_BOOST_THREADS typedef boost::mutex::scoped_lock scoped_mutex_lock; #else //! Aquire a lock that's valid until the end of scope. class scoped_mutex_lock { //! mutex reference mutex& m_mutex; //! marker if already unlocked by this thread (needs no synchronization) bool is_locked; public: //! lock mutex scoped_mutex_lock(mutex& m) : m_mutex(m), is_locked(true) { m_mutex.lock(); } //! unlock mutex hold when object goes out of scope. ~scoped_mutex_lock() { unlock(); } //! unlock mutex hold prematurely void unlock() { if (is_locked) { is_locked = false; m_mutex.unlock(); } } //! return platform specific handle pthread_mutex_t & native_handle() { return m_mutex.native_handle(); } }; #endif STXXL_END_NAMESPACE #endif // !STXXL_COMMON_MUTEX_HEADER stxxl-1.4.1/include/stxxl/bits/common/binary_buffer.h000644 001411 000144 00000047137 12411366426 022542 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/binary_buffer.h * * Classes binary_buffer and binary_reader to construct data blocks with * variable length content. Programs construct blocks using * binary_buffer::put() and read them using * binary_reader::get(). The operation sequences should match. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013-2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_BINARY_BUFFER_HEADER #define STXXL_COMMON_BINARY_BUFFER_HEADER #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup support //! \{ /*! * binary_buffer represents a dynamically growable area of memory, which can be * modified by appending integral data types via put() and other basic * operations. */ class binary_buffer { protected: //! Allocated buffer pointer. char* m_data; //! Size of valid data. size_t m_size; //! Total capacity of buffer. size_t m_capacity; public: //! Create a new empty object inline binary_buffer() : m_data(NULL), m_size(0), m_capacity(0) { } //! Copy-Constructor, duplicates memory content. inline binary_buffer(const binary_buffer& other) : m_data(NULL), m_size(0), m_capacity(0) { assign(other); } //! Constructor, copy memory area. inline binary_buffer(const void* data, size_t n) : m_data(NULL), m_size(0), m_capacity(0) { assign(data, n); } //! Constructor, create object with n bytes pre-allocated. inline binary_buffer(size_t n) : m_data(NULL), m_size(0), m_capacity(0) { alloc(n); } //! Constructor from std::string, copies string content. inline binary_buffer(const std::string& str) : m_data(NULL), m_size(0), m_capacity(0) { assign(str.data(), str.size()); } //! Destroys the memory space. inline ~binary_buffer() { dealloc(); } //! Return a pointer to the currently kept memory area. inline const char * data() const { return m_data; } //! Return a writeable pointer to the currently kept memory area. inline char * data() { return m_data; } //! Return the currently used length in bytes. inline size_t size() const { return m_size; } //! Return the currently allocated buffer capacity. inline size_t capacity() const { return m_capacity; } //! Explicit conversion to std::string (copies memory of course). inline std::string str() const { return std::string(reinterpret_cast(m_data), m_size); } //! Set the valid bytes in the buffer, use if the buffer is filled //! directly. inline binary_buffer & set_size(size_t n) { assert(n <= m_capacity); m_size = n; return *this; } //! Make sure that at least n bytes are allocated. inline binary_buffer & alloc(size_t n) { if (m_capacity < n) { m_capacity = n; m_data = static_cast(realloc(m_data, m_capacity)); } return *this; } //! Deallocates the kept memory space (we use dealloc() instead of free() //! as a name, because sometimes "free" is replaced by the preprocessor) inline binary_buffer & dealloc() { if (m_data) free(m_data); m_data = NULL; m_size = m_capacity = 0; return *this; } //! Detach the memory from the object, returns the memory pointer. inline const char * detach() { const char* data = m_data; m_data = NULL; m_size = m_capacity = 0; return data; } //! Clears the memory contents, does not deallocate the memory. inline binary_buffer & clear() { m_size = 0; return *this; } //! Copy a memory range into the buffer, overwrites all current //! data. Roughly equivalent to clear() followed by append(). inline binary_buffer & assign(const void* data, size_t len) { if (len > m_capacity) alloc(len); memcpy(m_data, data, len); m_size = len; return *this; } //! Copy the contents of another buffer object into this buffer, overwrites //! all current data. Roughly equivalent to clear() followed by append(). inline binary_buffer & assign(const binary_buffer& other) { if (&other != this) assign(other.data(), other.size()); return *this; } //! Assignment operator: copy other's memory range into buffer. inline binary_buffer& operator = (const binary_buffer& other) { if (&other != this) assign(other.data(), other.size()); return *this; } //! Align the size of the buffer to a multiple of n. Fills up with 0s. inline binary_buffer & align(size_t n) { assert(n > 0); size_t rem = m_size % n; if (rem != 0) { size_t add = n - rem; if (m_size + add > m_capacity) dynalloc(m_size + add); memset(m_data + m_size, 0, add); m_size += add; } assert((m_size % n) == 0); return *this; } //! Dynamically allocate more memory. At least n bytes will be available, //! probably more to compensate future growth. inline binary_buffer & dynalloc(size_t n) { if (m_capacity < n) { // place to adapt the buffer growing algorithm as need. size_t newsize = m_capacity; while (newsize < n) { if (newsize < 256) newsize = 512; else if (newsize < 1024 * 1024) newsize = 2 * newsize; else newsize += 1024 * 1024; } alloc(newsize); } return *this; } // *** Appending Write Functions *** //! Append a memory range to the buffer inline binary_buffer & append(const void* data, size_t len) { if (m_size + len > m_capacity) dynalloc(m_size + len); memcpy(m_data + m_size, data, len); m_size += len; return *this; } //! Append the contents of a different buffer object to this one. inline binary_buffer & append(const class binary_buffer& bb) { return append(bb.data(), bb.size()); } //! Append to contents of a std::string, excluding the null (which isn't //! contained in the string size anyway). inline binary_buffer & append(const std::string& s) { return append(s.data(), s.size()); } //! Put (append) a single item of the template type T to the buffer. Be //! careful with implicit type conversions! template inline binary_buffer & put(const Type item) { if (m_size + sizeof(Type) > m_capacity) dynalloc(m_size + sizeof(Type)); *reinterpret_cast(m_data + m_size) = item; m_size += sizeof(Type); return *this; } //! Append a varint to the buffer. inline binary_buffer & put_varint(uint32 v) { if (v < 128) { put(uint8(v)); } else if (v < 128 * 128) { put((uint8)(((v >> 0) & 0x7F) | 0x80)); put((uint8)((v >> 7) & 0x7F)); } else if (v < 128 * 128 * 128) { put((uint8)(((v >> 0) & 0x7F) | 0x80)); put((uint8)(((v >> 7) & 0x7F) | 0x80)); put((uint8)((v >> 14) & 0x7F)); } else if (v < 128 * 128 * 128 * 128) { put((uint8)(((v >> 0) & 0x7F) | 0x80)); put((uint8)(((v >> 7) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)((v >> 21) & 0x7F)); } else { put((uint8)(((v >> 0) & 0x7F) | 0x80)); put((uint8)(((v >> 7) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)(((v >> 21) & 0x7F) | 0x80)); put((uint8)((v >> 28) & 0x7F)); } return *this; } //! Append a varint to the buffer. inline binary_buffer & put_varint(int v) { return put_varint((uint32)v); } //! Append a varint to the buffer. inline binary_buffer & put_varint(uint64 v) { if (v < 128) { put(uint8(v)); } else if (v < 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)((v >> 07) & 0x7F)); } else if (v < 128 * 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)((v >> 14) & 0x7F)); } else if (v < 128 * 128 * 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)((v >> 21) & 0x7F)); } else if (v < ((uint64)128) * 128 * 128 * 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)(((v >> 21) & 0x7F) | 0x80)); put((uint8)((v >> 28) & 0x7F)); } else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)(((v >> 21) & 0x7F) | 0x80)); put((uint8)(((v >> 28) & 0x7F) | 0x80)); put((uint8)((v >> 35) & 0x7F)); } else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)(((v >> 21) & 0x7F) | 0x80)); put((uint8)(((v >> 28) & 0x7F) | 0x80)); put((uint8)(((v >> 35) & 0x7F) | 0x80)); put((uint8)((v >> 42) & 0x7F)); } else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128 * 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)(((v >> 21) & 0x7F) | 0x80)); put((uint8)(((v >> 28) & 0x7F) | 0x80)); put((uint8)(((v >> 35) & 0x7F) | 0x80)); put((uint8)(((v >> 42) & 0x7F) | 0x80)); put((uint8)((v >> 49) & 0x7F)); } else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128 * 128 * 128 * 128) { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)(((v >> 21) & 0x7F) | 0x80)); put((uint8)(((v >> 28) & 0x7F) | 0x80)); put((uint8)(((v >> 35) & 0x7F) | 0x80)); put((uint8)(((v >> 42) & 0x7F) | 0x80)); put((uint8)(((v >> 49) & 0x7F) | 0x80)); put((uint8)((v >> 56) & 0x7F)); } else { put((uint8)(((v >> 00) & 0x7F) | 0x80)); put((uint8)(((v >> 07) & 0x7F) | 0x80)); put((uint8)(((v >> 14) & 0x7F) | 0x80)); put((uint8)(((v >> 21) & 0x7F) | 0x80)); put((uint8)(((v >> 28) & 0x7F) | 0x80)); put((uint8)(((v >> 35) & 0x7F) | 0x80)); put((uint8)(((v >> 42) & 0x7F) | 0x80)); put((uint8)(((v >> 49) & 0x7F) | 0x80)); put((uint8)(((v >> 56) & 0x7F) | 0x80)); put((uint8)((v >> 63) & 0x7F)); } return *this; } //! Put a string by saving it's length followed by the data itself. inline binary_buffer & put_string(const char* data, size_t len) { return put_varint((uint32)len).append(data, len); } //! Put a string by saving it's length followed by the data itself. inline binary_buffer & put_string(const std::string& str) { return put_string(str.data(), str.size()); } //! Put a binary_buffer by saving it's length followed by the data itself. inline binary_buffer & put_string(const binary_buffer& bb) { return put_string(bb.data(), bb.size()); } }; /*! * binary_buffer_ref represents a memory area as pointer and valid length. It * is not deallocated or otherwise managed. This class can be used to pass * around references to binary_buffer objects. */ class binary_buffer_ref { protected: //! Allocated buffer pointer. const char* m_data; //! Size of valid data. size_t m_size; public: //! Constructor, assign memory area from binary_buffer. binary_buffer_ref(const binary_buffer& bb) : m_data(bb.data()), m_size(bb.size()) { } //! Constructor, assign memory area from pointer and length. binary_buffer_ref(const void* data, size_t n) : m_data(reinterpret_cast(data)), m_size(n) { } //! Constructor, assign memory area from string, does NOT copy. inline binary_buffer_ref(const std::string& str) : m_data(str.data()), m_size(str.size()) { } //! Return a pointer to the currently kept memory area. const void * data() const { return m_data; } //! Return the currently valid length in bytes. size_t size() const { return m_size; } //! Explicit conversion to std::string (copies memory of course). inline std::string str() const { return std::string(reinterpret_cast(m_data), m_size); } //! Compare contents of two binary_buffer_refs. bool operator == (const binary_buffer_ref& br) const { if (m_size != br.m_size) return false; return memcmp(m_data, br.m_data, m_size) == 0; } //! Compare contents of two binary_buffer_refs. bool operator != (const binary_buffer_ref& br) const { if (m_size != br.m_size) return true; return memcmp(m_data, br.m_data, m_size) != 0; } }; /*! * binary_reader represents a binary_buffer_ref with an additional cursor with which * the memory can be read incrementally. */ class binary_reader : public binary_buffer_ref { protected: //! Current read cursor size_t m_curr; public: //! Constructor, assign memory area from binary_buffer. inline binary_reader(const binary_buffer_ref& br) : binary_buffer_ref(br), m_curr(0) { } //! Constructor, assign memory area from pointer and length. inline binary_reader(const void* data, size_t n) : binary_buffer_ref(data, n), m_curr(0) { } //! Constructor, assign memory area from string, does NOT copy. inline binary_reader(const std::string& str) : binary_buffer_ref(str), m_curr(0) { } //! Return the current read cursor. inline size_t curr() const { return m_curr; } //! Reset the read cursor. inline binary_reader & rewind() { m_curr = 0; return *this; } //! Check that n bytes are available at the cursor. inline bool cursor_available(size_t n) const { return (m_curr + n <= m_size); } //! Throws a std::underflow_error unless n bytes are available at the //! cursor. inline void check_available(size_t n) const { if (!cursor_available(n)) throw (std::underflow_error("binary_reader underrun")); } //! Return true if the cursor is at the end of the buffer. inline bool empty() const { return (m_curr == m_size); } //! Advance the cursor given number of bytes without reading them. inline binary_reader & skip(size_t n) { check_available(n); m_curr += n; return *this; } //! Fetch a number of unstructured bytes from the buffer, advancing the //! cursor. inline binary_reader & read(void* outdata, size_t datalen) { check_available(datalen); memcpy(outdata, m_data + m_curr, datalen); m_curr += datalen; return *this; } //! Fetch a number of unstructured bytes from the buffer as std::string, //! advancing the cursor. inline std::string read(size_t datalen) { check_available(datalen); std::string out(m_data + m_curr, datalen); m_curr += datalen; return out; } //! Fetch a single item of the template type Type from the buffer, //! advancing the cursor. Be careful with implicit type conversions! template inline Type get() { check_available(sizeof(Type)); Type ret = *reinterpret_cast(m_data + m_curr); m_curr += sizeof(Type); return ret; } //! Fetch a varint with up to 32-bit from the buffer at the cursor. inline uint32 get_varint() { uint32 u, v = get(); if (!(v & 0x80)) return v; v &= 0x7F; u = get(), v |= (u & 0x7F) << 7; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 14; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 21; if (!(u & 0x80)) return v; u = get(); if (u & 0xF0) throw (std::overflow_error("Overflow during varint decoding.")); v |= (u & 0x7F) << 28; return v; } //! Fetch a 64-bit varint from the buffer at the cursor. inline uint64 get_varint64() { uint64 u, v = get(); if (!(v & 0x80)) return v; v &= 0x7F; u = get(), v |= (u & 0x7F) << 7; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 14; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 21; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 28; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 35; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 42; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 49; if (!(u & 0x80)) return v; u = get(), v |= (u & 0x7F) << 56; if (!(u & 0x80)) return v; u = get(); if (u & 0xFE) throw (std::overflow_error("Overflow during varint64 decoding.")); v |= (u & 0x7F) << 63; return v; } //! Fetch a string which was put via put_string(). inline std::string get_string() { uint32 len = get_varint(); return read(len); } //! Fetch a binary_buffer_ref to a binary string or blob which was put via //! put_string(). Does NOT copy the data. inline binary_buffer_ref get_binary_buffer_ref() { uint32 len = get_varint(); // save object binary_buffer_ref br(m_data + m_curr, len); // skip over sub block data skip(len); return br; } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_COMMON_BINARY_BUFFER_HEADER stxxl-1.4.1/include/stxxl/bits/common/state.h000644 001411 000144 00000003227 12405375303 021032 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/state.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_STATE_HEADER #define STXXL_COMMON_STATE_HEADER #include #include #include STXXL_BEGIN_NAMESPACE template class state : private noncopyable { typedef ValueType value_type; //! mutex for condition variable mutex m_mutex; //! condition variable condition_variable m_cond; //! current state value_type m_state; public: state(const value_type& s) : m_state(s) { } void set_to(const value_type& new_state) { scoped_mutex_lock lock(m_mutex); m_state = new_state; lock.unlock(); m_cond.notify_all(); } void wait_for(const value_type& needed_state) { scoped_mutex_lock lock(m_mutex); while (needed_state != m_state) m_cond.wait(lock); } value_type operator () () { scoped_mutex_lock lock(m_mutex); return m_state; } }; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_STATE_HEADER stxxl-1.4.1/include/stxxl/bits/common/utils.h000644 001411 000144 00000020274 12414452316 021053 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/utils.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2006 Roman Dementiev * Copyright (C) 2007-2009 Andreas Beckmann * Copyright (C) 2008 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_UTILS_HEADER #define STXXL_COMMON_UTILS_HEADER #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //////////////////////////////////////////////////////////////////////////// #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))) # define STXXL_ATTRIBUTE_UNUSED __attribute__ ((unused)) #else # define STXXL_ATTRIBUTE_UNUSED #endif //////////////////////////////////////////////////////////////////////////// #if defined(__GXX_EXPERIMENTAL_CXX0X__) #define STXXL_STATIC_ASSERT(x) static_assert(x, #x) #else #define STXXL_STATIC_ASSERT(x) { typedef int static_assert_dummy_type[(x) ? 1 : -1] STXXL_ATTRIBUTE_UNUSED; } #endif //////////////////////////////////////////////////////////////////////////// //! Split a string by given separator string. Returns a vector of strings with //! at least min_fields and at most limit_fields static inline std::vector split(const std::string& str, const std::string& sep, unsigned int min_fields = 0, unsigned int limit_fields = std::numeric_limits::max()) { std::vector result; if (str.empty()) { result.resize(min_fields); return result; } std::string::size_type CurPos(0), LastPos(0); while (1) { if (result.size() + 1 == limit_fields) break; CurPos = str.find(sep, LastPos); if (CurPos == std::string::npos) break; result.push_back( str.substr(LastPos, std::string::size_type(CurPos - LastPos)) ); LastPos = CurPos + sep.size(); } std::string sub = str.substr(LastPos); result.push_back(sub); if (result.size() < min_fields) result.resize(min_fields); return result; } //////////////////////////////////////////////////////////////////////////// //! Format any ostream-able type into a string template std::string to_str(const Type& t) { std::ostringstream oss; oss << t; return oss.str(); } //////////////////////////////////////////////////////////////////////////// //! Parse a string like "343KB" or "44 GiB" into the corresponding size in //! bytes. Returns the number of bytes and sets ok = true if the string could //! be parsed correctly. If no units indicator is given, use def_unit in //! k/m/g/t/p (powers of ten) or in K/M/G/T/P (power of two). bool parse_SI_IEC_size(const std::string& str, uint64& size, char def_unit = 0); //! Format a byte size using SI (K, M, G, T) suffixes (powers of ten). Returns //! "123 M" or similar. std::string format_SI_size(uint64 number); //! Format a byte size using IEC (Ki, Mi, Gi, Ti) suffixes (powers of //! two). Returns "123 Ki" or similar. std::string format_IEC_size(uint64 number); //////////////////////////////////////////////////////////////////////////// inline stxxl::int64 atoi64(const char* s) { #if STXXL_MSVC return _atoi64(s); #else return atoll(s); #endif } //////////////////////////////////////////////////////////////////////////// inline stxxl::uint64 atouint64(const char* s) { #if STXXL_MSVC return _strtoui64(s, NULL, 10); #else return strtoull(s, NULL, 10); #endif } //////////////////////////////////////////////////////////////////////////// template inline const Type& STXXL_MIN(const Type& a, const Type& b) { return std::min(a, b); } template inline const Type& STXXL_MAX(const Type& a, const Type& b) { return std::max(a, b); } //////////////////////////////////////////////////////////////////////////// //! calculate the log2 floor of an integral type using math.h template inline Integral log2_ceil(Integral i) { return Integral(ceil(log2(i))); } //! calculate the log2 ceiling of an integral type using math.h template inline Integral log2_floor(Integral i) { return Integral(log2(i)); } //////////////////////////////////////////////////////////////////////////// //! calculate the log2 floor of an integer type (by repeated bit shifts) template unsigned int ilog2_floor(IntegerType i) { unsigned int p = 0; while (i >= 256) i >>= 8, p += 8; while (i >>= 1) ++p; return p; } //! calculate the log2 ceiling of an integer type (by repeated bit shifts) template unsigned int ilog2_ceil(const IntegerType& i) { if (i <= 1) return 0; return ilog2_floor(i - 1) + 1; } //////////////////////////////////////////////////////////////////////////// template inline typename compat::remove_const::type div_ceil(Integral n, Integral2 d) { #if 0 // ambiguous overload for std::div(unsigned_anything, unsigned_anything) typedef __typeof__ (std::div(n, d)) div_type; div_type result = std::div(n, d); return result.quot + (result.rem != 0); #else return n / d + ((n % d) != 0); #endif } //////////////////////////////////////////////////////////////////////////// #ifdef __GNUC__ #define HAVE_BUILTIN_EXPECT #endif #ifdef HAVE_BUILTIN_EXPECT #define LIKELY(c) __builtin_expect((c), 1) #else #define LIKELY(c) c #endif #ifdef HAVE_BUILTIN_EXPECT #define UNLIKELY(c) __builtin_expect((c), 0) #else #define UNLIKELY(c) c #endif //////////////////////////////////////////////////////////////////////////// inline size_t longhash1(uint64 key_) { key_ += ~(key_ << 32); key_ ^= (key_ >> 22); key_ += ~(key_ << 13); key_ ^= (key_ >> 8); key_ += (key_ << 3); key_ ^= (key_ >> 15); key_ += ~(key_ << 27); key_ ^= (key_ >> 31); return (size_t)key_; } //////////////////////////////////////////////////////////////////////////// template inline void swap_1D_arrays(Type* a, Type* b, unsigned_type size) { for (unsigned_type i = 0; i < size; ++i) std::swap(a[i], b[i]); } //////////////////////////////////////////////////////////////////////////// //! round n up to next larger multiple of 2^power. example: (48,4) = 64, (48,3) = 48. template inline Integral round_up_to_power_of_two(Integral n, unsigned_type power) { Integral pot = Integral(1) << power, // = 0..0 1 0^power mask = pot - 1; // = 0..0 0 1^power if (n & mask) // n not divisible by pot return (n & ~mask) + pot; else return n; } //////////////////////////////////////////////////////////////////////////// template inline typename Container::value_type pop(Container& c) { typename Container::value_type r = c.top(); c.pop(); return r; } template inline typename Container::value_type pop_front(Container& c) { typename Container::value_type r = c.front(); c.pop_front(); return r; } template inline typename Container::value_type pop_back(Container& c) { typename Container::value_type r = c.back(); c.pop_back(); return r; } template inline typename Container::value_type pop_begin(Container& c) { typename Container::value_type r = *c.begin(); c.erase(c.begin()); return r; } //////////////////////////////////////////////////////////////////////////// STXXL_END_NAMESPACE #endif // !STXXL_COMMON_UTILS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/common/tmeta.h000644 001411 000144 00000005654 12405375303 021032 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/tmeta.h * * Template Metaprogramming Tools * (from the Generative Programming book Krysztof Czarnecki, Ulrich Eisenecker) * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_TMETA_HEADER #define STXXL_COMMON_TMETA_HEADER #include #include STXXL_BEGIN_NAMESPACE //! IF template metaprogramming statement. //! //! If \c Flag is \c true then \c IF<>::result is of type Type1 //! otherwise of \c IF<>::result is of type Type2 template struct IF { typedef Type1 result; }; template struct IF { typedef Type2 result; }; //! If \c Flag is \c true then \c IF<>::result is Num1 //! otherwise of \c IF<>::result is Num2 template struct IF_N { enum { result = Num1 }; }; template struct IF_N { enum { result = Num2 }; }; const int DEFAULT = ~(~0u >> 1); // initialize with the smallest int struct NilCase { }; template struct CASE { enum { tag = tag_ }; typedef Type_ Type; typedef Next_ Next; }; template class SWITCH { typedef typename Case::Next NextCase; enum { caseTag = Case::tag, found = (caseTag == tag || caseTag == DEFAULT) }; public: typedef typename IF::result >::result result; }; template class SWITCH { public: typedef NilCase result; }; //! \internal, use LOG2 instead template class LOG2_floor { public: enum { value = LOG2_floor::value + 1 }; }; template <> class LOG2_floor<1> { public: enum { value = 0 }; }; template <> class LOG2_floor<0> { public: enum { value = 0 }; }; template class LOG2 { public: enum { floor = LOG2_floor::value, ceil = LOG2_floor::value + 1 }; }; template <> class LOG2<1> { public: enum { floor = 0, ceil = 0 }; }; template <> class LOG2<0> { public: enum { floor = 0, ceil = 0 }; }; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_TMETA_HEADER stxxl-1.4.1/include/stxxl/bits/common/timer.h000644 001411 000144 00000011066 12411366426 021035 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/timer.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2005 Roman Dementiev * Copyright (C) 2007-2009 Andreas Beckmann * Copyright (C) 2008 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_TIMER_HEADER #define STXXL_COMMON_TIMER_HEADER #include #include #include #include #if STXXL_BOOST_TIMESTAMP #include #include #elif STXXL_WINDOWS #ifndef NOMINMAX #define NOMINMAX #endif #include #else #include #include #endif STXXL_BEGIN_NAMESPACE //! \addtogroup support //! \{ //! Returns number of seconds since the epoch, high resolution. inline double timestamp() { #if STXXL_BOOST_TIMESTAMP boost::posix_time::ptime MyTime = boost::posix_time::microsec_clock::local_time(); boost::posix_time::time_duration Duration = MyTime - boost::posix_time::time_from_string("1970-01-01 00:00:00.000"); double sec = double(Duration.hours()) * 3600. + double(Duration.minutes()) * 60. + double(Duration.seconds()) + double(Duration.fractional_seconds()) / (pow(10., Duration.num_fractional_digits())); return sec; #elif STXXL_WINDOWS return GetTickCount() / 1000.0; #else struct timeval tp; gettimeofday(&tp, NULL); return double(tp.tv_sec) + double(tp.tv_usec) / 1000000.; #endif } class timer { bool running; double accumulated; double last_clock; //! return current timestamp static inline double timestamp() { return stxxl::timestamp(); } public: inline timer(bool start_immediately = false) : running(false), accumulated(0.), last_clock(0) { if (start_immediately) start(); } //! start timer inline void start() { running = true; last_clock = timestamp(); } //! stop timer inline void stop() { running = false; accumulated += timestamp() - last_clock; } //! return accumulated time inline void reset() { accumulated = 0.; last_clock = timestamp(); } //! return currently accumulated time in milliseconds inline double mseconds() { if (running) return (accumulated + timestamp() - last_clock) * 1000.; return (accumulated * 1000.); } //! return currently accumulated time in microseconds inline double useconds() { if (running) return (accumulated + timestamp() - last_clock) * 1000000.; return (accumulated * 1000000.); } //! return currently accumulated time in seconds (as double) inline double seconds() { if (running) return (accumulated + timestamp() - last_clock); return (accumulated); } }; /*! * Simple scoped timer, which takes a text message and prints the duration * until the scope is destroyed. */ class scoped_print_timer { protected: //! message std::string m_message; //! bytes processed uint64 m_bytes; //! timer class timer m_timer; public: //! save message and start timer scoped_print_timer(const std::string& message, const uint64 bytes = 0) : m_message(message), m_bytes(bytes), m_timer(true) { STXXL_MSG("Starting " << message); } //! on destruction: tell the time ~scoped_print_timer() { if (m_bytes == 0) { STXXL_MSG("Finished " << m_message << " after " << m_timer.seconds() << " seconds"); } else { double bps = (double)m_bytes / m_timer.seconds(); STXXL_MSG("Finished " << m_message << " after " << m_timer.seconds() << " seconds. " << "Processed " << format_IEC_size(m_bytes) << "B" << " @ " << format_IEC_size((uint64)bps) << "B/s"); } } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_COMMON_TIMER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/common/semaphore.h000644 001411 000144 00000004621 12405375303 021674 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/semaphore.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_SEMAPHORE_HEADER #define STXXL_COMMON_SEMAPHORE_HEADER #include #include #include STXXL_BEGIN_NAMESPACE class semaphore : private noncopyable { //! value of the semaphore int v; //! mutex for condition variable mutex m_mutex; //! condition variable condition_variable m_cond; public: //! construct semaphore semaphore(int init_value = 1) : v(init_value) { } //! function increments the semaphore and signals any threads that are //! blocked waiting a change in the semaphore int operator ++ (int) { scoped_mutex_lock lock(m_mutex); int res = ++v; lock.unlock(); m_cond.notify_one(); return res; } //! function decrements the semaphore and blocks if the semaphore is <= 0 //! until another thread signals a change int operator -- (int) { scoped_mutex_lock lock(m_mutex); while (v <= 0) m_cond.wait(lock); return --v; } //! function does NOT block but simply decrements the semaphore should not //! be used instead of down -- only for programs where multiple threads //! must up on a semaphore before another thread can go down, i.e., allows //! programmer to set the semaphore to a negative value prior to using it //! for synchronization. int decrement() { scoped_mutex_lock lock(m_mutex); return --v; } #if 0 //! function returns the value of the semaphore at the time the //! critical section is accessed. obviously the value is not guaranteed //! after the function unlocks the critical section. int get_value() { scoped_mutex_lock lock(m_mutex); return v; } #endif }; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_SEMAPHORE_HEADER stxxl-1.4.1/include/stxxl/bits/common/seed.h000644 001411 000144 00000001537 12405375303 020634 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/seed.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_SEED_HEADER #define STXXL_COMMON_SEED_HEADER #include STXXL_BEGIN_NAMESPACE //! set the global stxxl seed value void set_seed(unsigned seed); //! get a seed value for prng initialization, subsequent calls return a //! sequence of different values unsigned get_next_seed(); STXXL_END_NAMESPACE #endif // !STXXL_COMMON_SEED_HEADER stxxl-1.4.1/include/stxxl/bits/common/addressable_queues.h000644 001411 000144 00000013624 12414452316 023554 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/addressable_queues.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_ADDRESSABLE_QUEUES_HEADER #define STXXL_COMMON_ADDRESSABLE_QUEUES_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! An internal fifo queue that allows removing elements addressed with (a copy //! of) themselves. //! \tparam KeyType Type of contained elements. template class addressable_fifo_queue { typedef std::list container_type; typedef typename container_type::iterator container_iterator; typedef std::map meta_type; typedef typename meta_type::iterator meta_iterator; container_type vals; meta_type meta; public: //! Type of handle to an entry. For use with insert and remove. typedef meta_iterator handle; //! Create an empty queue. addressable_fifo_queue() { } ~addressable_fifo_queue() { } //! Check if queue is empty. //! \return If queue is empty. bool empty() const { return vals.empty(); } //! Insert new element. If the element is already in, it is moved to the //! back. //! \param e Element to insert. //! \return pair Iterator to element; if element was newly //! inserted. std::pair insert(const KeyType& e) { container_iterator ei = vals.insert(vals.end(), e); std::pair r = meta.insert(std::make_pair(e, ei)); if (! r.second) { // element was already in vals.erase(r.first->second); r.first->second = ei; } return r; } //! Erase element from the queue. //! \param e Element to remove. //! \return If element was in. bool erase(const KeyType& e) { handle mi = meta.find(e); if (mi == meta.end()) return false; vals.erase(mi->second); meta.erase(mi); return true; } //! Erase element from the queue. //! \param i Iterator to element to remove. void erase(handle i) { vals.erase(i->second); meta.erase(i); } //! Access top element in the queue. //! \return Const reference to top element. const KeyType & top() const { return vals.front(); } //! Remove top element from the queue. //! \return Top element. KeyType pop() { assert(! empty()); const KeyType e = top(); meta.erase(e); vals.pop_front(); return e; } }; //! An internal priority queue that allows removing elements addressed with (a //! copy of) themselves. //! \tparam KeyType Type of contained elements. //! \tparam PriorityType Type of Priority. template > class addressable_priority_queue { struct cmp // like < for pair, but uses Cmp for < on first { bool operator () (const std::pair& left, const std::pair& right) const { Cmp c; return c(left.first, right.first) || ((! c(right.first, left.first)) && left.second < right.second); } }; typedef std::set, cmp> container_type; typedef typename container_type::iterator container_iterator; typedef std::map meta_type; typedef typename meta_type::iterator meta_iterator; container_type vals; meta_type meta; public: //! Type of handle to an entry. For use with insert and remove. typedef meta_iterator handle; //! Create an empty queue. addressable_priority_queue() { } ~addressable_priority_queue() { } //! Check if queue is empty. //! \return If queue is empty. bool empty() const { return vals.empty(); } //! Insert new element. If the element is already in, it's priority is updated. //! \param e Element to insert. //! \param o Priority of element. //! \return pair Iterator to element; if element was newly inserted. std::pair insert(const KeyType& e, const PriorityType o) { std::pair s = vals.insert(std::make_pair(o, e)); std::pair r = meta.insert(std::make_pair(e, s.first)); if (! r.second && s.second) { // was already in with different priority vals.erase(r.first->second); r.first->second = s.first; } return r; } //! Erase element from the queue. //! \param e Element to remove. //! \return If element was in. bool erase(const KeyType& e) { handle mi = meta.find(e); if (mi == meta.end()) return false; vals.erase(mi->second); meta.erase(mi); return true; } //! Erase element from the queue. //! \param i Iterator to element to remove. void erase(handle i) { vals.erase(i->second); meta.erase(i); } //! Access top (= min) element in the queue. //! \return Const reference to top element. const KeyType & top() const { return vals.begin()->second; } //! Remove top (= min) element from the queue. //! \return Top element. KeyType pop() { assert(! empty()); const KeyType e = top(); meta.erase(e); vals.erase(vals.begin()); return e; } }; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_ADDRESSABLE_QUEUES_HEADER stxxl-1.4.1/include/stxxl/bits/common/condition_variable.h000644 001411 000144 00000004135 12405375303 023544 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/condition_variable.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_CONDITION_VARIABLE_HEADER #define STXXL_COMMON_CONDITION_VARIABLE_HEADER #include #include #include #if STXXL_STD_THREADS #include #elif STXXL_BOOST_THREADS #include #elif STXXL_POSIX_THREADS #include #include #include #include #else #error "Thread implementation not detected." #endif STXXL_BEGIN_NAMESPACE #if STXXL_STD_THREADS typedef std::condition_variable condition_variable; #elif STXXL_BOOST_THREADS typedef boost::condition condition_variable; #elif STXXL_POSIX_THREADS class condition_variable : private noncopyable { //! pthread handle to condition pthread_cond_t cond; public: //! initialize condition variable condition_variable() { STXXL_CHECK_PTHREAD_CALL(pthread_cond_init(&cond, NULL)); } //! destroy condition variable ~condition_variable() { STXXL_CHECK_PTHREAD_CALL(pthread_cond_destroy(&cond)); } //! notify one waiting thread void notify_one() { STXXL_CHECK_PTHREAD_CALL(pthread_cond_signal(&cond)); } //! notify all waiting threads void notify_all() { STXXL_CHECK_PTHREAD_CALL(pthread_cond_broadcast(&cond)); } //! wait for a signal on the condition variable void wait(scoped_mutex_lock& lock) { STXXL_CHECK_PTHREAD_CALL(pthread_cond_wait(&cond, &lock.native_handle())); } }; #endif STXXL_END_NAMESPACE #endif // !STXXL_COMMON_CONDITION_VARIABLE_HEADER stxxl-1.4.1/include/stxxl/bits/common/counting_ptr.h000644 001411 000144 00000040024 12410750556 022424 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/counting_ptr.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_COUNTING_PTR_HEADER #define STXXL_COMMON_COUNTING_PTR_HEADER #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup support //! \{ /*! * High-performance smart pointer used as a wrapping reference counting * pointer. * * This smart pointer class requires two functions in the templated type: void * inc_reference() and void dec_reference(). These must increment and decrement * a reference counter inside the templated object. When initialized, the type * must have reference count zero. It is _not_ immediately called with * add_reference(). Each new object referencing the data calls add_reference() * and each destroying holder calls del_reference(). When the data object * determines that it's internal counter is zero, then it must destroy itself. * * Accompanying the counting_ptr is a const_counting_ptr and a class * counted_object, from which reference counted classes must be derive * from. The class counted_object implement all methods required for reference * counting. * * The whole method is more similar to boost' instrusive_ptr, but also yields * something resembling shared_ptr. */ template class counting_ptr { public: //! contained type. typedef Type element_type; private: //! the pointer to the currently referenced object. Type* m_ptr; protected: //! increment reference counter for current object. void inc_reference() { inc_reference(m_ptr); } //! increment reference counter of other object. void inc_reference(Type* o) { if (o) o->inc_reference(); } //! decrement reference counter of current object and maybe delete it. void dec_reference() { if (m_ptr && m_ptr->dec_reference()) delete m_ptr; } public: //! default constructor: contains a NULL pointer. counting_ptr() : m_ptr(NULL) { } //! constructor with pointer: initializes new reference to ptr. counting_ptr(Type* ptr) : m_ptr(ptr) { inc_reference(); } //! copy-constructor: also initializes new reference to ptr. counting_ptr(const counting_ptr& other_ptr) : m_ptr(other_ptr) { inc_reference(); } //! assignment operator: dereference current object and acquire reference on new one. counting_ptr& operator = (const counting_ptr& other_ptr) { return operator = (other_ptr.m_ptr); } //! assignment to pointer: dereference current and acquire reference to new ptr. counting_ptr& operator = (Type* ptr) { inc_reference(ptr); dec_reference(); m_ptr = ptr; return *this; } //! destructor: decrements reference counter in ptr. ~counting_ptr() { dec_reference(); } //! return the enclosed object as reference. Type& operator * () const { assert(m_ptr); return *m_ptr; } //! return the enclosed pointer. Type* operator -> () const { assert(m_ptr); return m_ptr; } //! implicit cast to the enclosed pointer. operator Type* () const { return m_ptr; } //! return the enclosed pointer. Type * get() const { return m_ptr; } //! test equality of only the pointer values. bool operator == (const counting_ptr& other_ptr) const { return m_ptr == other_ptr.m_ptr; } //! test inequality of only the pointer values. bool operator != (const counting_ptr& other_ptr) const { return m_ptr != other_ptr.m_ptr; } //! cast to bool check for a NULL pointer operator bool () const { return valid(); } //! test for a non-NULL pointer bool valid() const { return (m_ptr != NULL); } //! test for a NULL pointer bool empty() const { return (m_ptr == NULL); } //! if the object is referred by this counting_ptr only bool unique() const { return m_ptr && m_ptr->unique(); } //! make and refer a copy if the original object was shared. void unify() { if (m_ptr && ! m_ptr->unique()) operator = (new Type(*m_ptr)); } //! swap enclosed object with another counting pointer (no reference counts need change) void swap(counting_ptr& b) { std::swap(m_ptr, b.m_ptr); } }; //! swap enclosed object with another counting pointer (no reference counts need change) template void swap(counting_ptr& a1, counting_ptr& a2) { a1.swap(a2); } /*! * High-performance smart pointer used as a wrapping reference counting * pointer. * * This smart pointer class requires two functions in the templated type: void * inc_reference() and void dec_reference(). These must increment and decrement * a reference counter inside the templated object. When initialized, the type * must have reference count zero. It is _not_ immediately called with * add_reference(). Each new object referencing the data calls add_reference() * and each destroying holder calls del_reference(). When the data object * determines that it's internal counter is zero, then it must destroy itself. * * Accompanying the counting_ptr is a const_counting_ptr and a class * counted_object, from which reference counted classes must be derive * from. The class counted_object implement all methods required for reference * counting. * * The whole method is more similar to boost' instrusive_ptr, but also yields * something resembling shared_ptr. */ template class const_counting_ptr { public: //! contained type. typedef Type element_type; private: //! the pointer to the currently referenced object. const Type* m_ptr; protected: //! increment reference counter for current object. void inc_reference() { inc_reference(m_ptr); } //! increment reference counter of other object. void inc_reference(const Type* o) { if (o) o->inc_reference(); } //! decrement reference counter of current object and maybe delete it. void dec_reference() { if (m_ptr && m_ptr->dec_reference()) delete m_ptr; } public: //! default constructor: contains a NULL pointer. const_counting_ptr() : m_ptr(NULL) { } //! constructor with pointer: initializes new reference to ptr. const_counting_ptr(const Type* ptr) : m_ptr(ptr) { inc_reference(); } //! copy-constructor: also initializes new reference to ptr. const_counting_ptr(const const_counting_ptr& other_ptr) : m_ptr(other_ptr) { inc_reference(); } //! constructor from non-const: also initializes new reference to ptr. const_counting_ptr(const counting_ptr& other_ptr) : m_ptr(other_ptr.get()) { inc_reference(); } //! assignment operator: dereference current object and acquire reference on new one. const_counting_ptr& operator = (const const_counting_ptr& other_ptr) { return operator = (other_ptr.m_ptr); } //! assignment operator: dereference current object and acquire reference on new one. const_counting_ptr& operator = (const counting_ptr& other_ptr) { return operator = (other_ptr.get()); } //! assignment to pointer: dereference current and acquire reference to new ptr. const_counting_ptr& operator = (const Type* ptr) { inc_reference(ptr); dec_reference(); m_ptr = ptr; return *this; } //! destructor: decrements reference counter in ptr. ~const_counting_ptr() { dec_reference(); } //! return the enclosed object as reference. const Type& operator * () const { assert(m_ptr); return *m_ptr; } //! return the enclosed pointer. const Type* operator -> () const { assert(m_ptr); return m_ptr; } //! implicit cast to the enclosed pointer. operator const Type* () const { return m_ptr; } //! return the enclosed pointer. const Type * get() const { return m_ptr; } //! test equality of only the pointer values. bool operator == (const const_counting_ptr& other_ptr) const { return m_ptr == other_ptr.m_ptr; } //! test inequality of only the pointer values. bool operator != (const const_counting_ptr& other_ptr) const { return m_ptr != other_ptr.m_ptr; } //! test equality of only the pointer values. bool operator == (const counting_ptr& other_ptr) const { return m_ptr == other_ptr.get(); } //! test inequality of only the pointer values. bool operator != (const counting_ptr& other_ptr) const { return m_ptr != other_ptr.get(); } //! cast to bool check for a NULL pointer operator bool () const { return m_ptr; } //! test for a non-NULL pointer bool valid() const { return m_ptr; } //! test for a NULL pointer bool empty() const { return !m_ptr; } //! if the object is referred by this const_counting_ptr only bool unique() const { return m_ptr && m_ptr->unique(); } //! swap enclosed object with another const_counting pointer (no reference counts need change) void swap(const_counting_ptr& b) { std::swap(m_ptr, b.m_ptr); } }; //! swap enclosed object with another const_counting pointer (no reference counts need change) template void swap(const_counting_ptr& a1, const_counting_ptr& a2) { a1.swap(a2); } /*! * Provides reference counting abilities for use with counting_ptr. * * Use as superclass of the actual object, this adds a reference_count * value. Then either use counting_ptr as pointer to manage references and * deletion, or just do normal new and delete. * * For thread-safe functions, use atomic_counted_object instead of this class! */ class counted_object { private: //! the reference count is kept mutable to all const_counting_ptr() to //! change the reference count. mutable unsigned_type m_reference_count; public: //! new objects have zero reference count counted_object() : m_reference_count(0) { } //! coping still creates a new object with zero reference count counted_object(const counted_object&) : m_reference_count(0) { } //! assignment operator, leaves pointers unchanged counted_object& operator = (const counted_object&) { return *this; } // changing the contents leaves pointers unchanged ~counted_object() { assert(m_reference_count == 0); } public: //! Call whenever setting a pointer to the object void inc_reference() const { ++m_reference_count; } //! Call whenever resetting (i.e. overwriting) a pointer to the object. //! IMPORTANT: In case of self-assignment, call AFTER inc_reference(). //! \return if the object has to be deleted (i.e. if it's reference count dropped to zero) bool dec_reference() const { return (! --m_reference_count); } //! Test if the counted_object is referenced by only one counting_ptr. bool unique() const { return (m_reference_count == 1); } //! Return the number of references to this object (for debugging) unsigned_type get_reference_count() const { return m_reference_count; } }; #if STXXL_HAVE_SYNC_ADD_AND_FETCH || STXXL_MSVC /*! * Provides reference counting abilities for use with counting_ptr with atomics * operations. * * Use as superclass of the actual object, this adds a reference_count * value. Then either use counting_ptr as pointer to manage references and * deletion, or just do normal new and delete. * * This class does thread-safe increment and decrement using atomic operations * on an integral type. */ class atomic_counted_object { private: //! the reference count is kept mutable to all const_counting_ptr() to //! change the reference count. #if STXXL_MSVC mutable long m_reference_count; #else mutable unsigned_type m_reference_count; #endif public: //! new objects have zero reference count atomic_counted_object() : m_reference_count(0) { } //! coping still creates a new object with zero reference count atomic_counted_object(const atomic_counted_object&) : m_reference_count(0) { } //! assignment operator, leaves pointers unchanged atomic_counted_object& operator = (const atomic_counted_object&) { return *this; } // changing the contents leaves pointers unchanged ~atomic_counted_object() { assert(m_reference_count == 0); } public: //! Call whenever setting a pointer to the object void inc_reference() const { #if STXXL_MSVC _InterlockedIncrement(&m_reference_count); #else __sync_add_and_fetch(&m_reference_count, +1); #endif } //! Call whenever resetting (i.e. overwriting) a pointer to the object. //! IMPORTANT: In case of self-assignment, call AFTER inc_reference(). //! \return if the object has to be deleted (i.e. if it's reference count dropped to zero) bool dec_reference() const { #if STXXL_MSVC return (_InterlockedDecrement(&m_reference_count) == 0); #else return (__sync_add_and_fetch(&m_reference_count, -1) == 0); #endif } //! Test if the counted_object is referenced by only one counting_ptr. bool unique() const { return (m_reference_count == 1); } //! Return the number of references to this object (for debugging) unsigned_type get_reference_count() const { return m_reference_count; } }; #else // no atomic intrinsics found, use mutexes (slow) /*! * Provides reference counting abilities for use with counting_ptr with mutex * locking. * * Use as superclass of the actual object, this adds a reference_count * value. Then either use counting_ptr as pointer to manage references and * deletion, or just do normal new and delete. * * This class does thread-safe increment and decrement using scoped locks. A * faster version of this class is available using atomic operations. */ class atomic_counted_object { private: //! the reference count is kept mutable to all const_counting_ptr() to //! change the reference count. mutable unsigned_type m_reference_count; //! the mutex used to synchronize access to the reference counter. mutable mutex m_reference_count_mutex; public: //! new objects have zero reference count atomic_counted_object() : m_reference_count(0) { } //! coping still creates a new object with zero reference count atomic_counted_object(const atomic_counted_object&) : m_reference_count(0) { } //! assignment operator, leaves pointers unchanged atomic_counted_object& operator = (const atomic_counted_object&) { return *this; } // changing the contents leaves pointers unchanged ~atomic_counted_object() { assert(m_reference_count == 0); } public: //! Call whenever setting a pointer to the object void inc_reference() const { scoped_mutex_lock lock(m_reference_count_mutex); ++m_reference_count; } //! Call whenever resetting (i.e. overwriting) a pointer to the object. //! IMPORTANT: In case of self-assignment, call AFTER inc_reference(). //! \return if the object has to be deleted (i.e. if it's reference count dropped to zero) bool dec_reference() const { scoped_mutex_lock lock(m_reference_count_mutex); return (--m_reference_count == 0); } //! Test if the counted_object is referenced by only one counting_ptr. bool unique() const { scoped_mutex_lock lock(m_reference_count_mutex); return (m_reference_count == 1); } //! Return the number of references to this object (for debugging) unsigned_type get_reference_count() const { scoped_mutex_lock lock(m_reference_count_mutex); return m_reference_count; } }; #endif //! \} STXXL_END_NAMESPACE #endif // !STXXL_COMMON_COUNTING_PTR_HEADER stxxl-1.4.1/include/stxxl/bits/common/uint_types.h000644 001411 000144 00000022544 12411366426 022123 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/uint_types.h * * Class representing a 40-bit or 48-bit unsigned integer encoded in five or * six bytes. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_UINT_TYPES_HEADER #define STXXL_COMMON_UINT_TYPES_HEADER #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE /*! * Construct an 40-bit or 48-bit unsigned integer stored in five or six bytes. * * The purpose of this class is to provide integers with smaller data storage * footprints when more than 32-bit, but less than 64-bit indexes are * needed. This is commonly the case for storing file offsets and indexes. Here * smaller types currently suffice for files < 1 TiB or < 16 TiB. * * The class combines a 32-bit integer with a HighType (either 8-bit or 16-bit) * to get a larger type. Only unsigned values are supported, which fits the * general application of file offsets. * * Calculation in uint_pair are generally done by transforming everything to * 64-bit data type, so that 64-bit register arithmetic can be used. The * exception here is \b increment and \b decrement, which is done directly on * the lower/higher part. Not all arithmetic operations are supported, patches * welcome if you really need the operations. */ #if STXXL_MSVC #pragma pack(push, 1) #endif template class uint_pair { public: //! lower part type, always 32-bit typedef uint32 low_type; //! higher part type, currently either 8-bit or 16-bit typedef HighType high_type; private: //! member containing lower significant integer value low_type low; //! member containing higher significant integer value high_type high; //! return highest value storable in lower part, also used as a mask. static unsigned_type low_max() { return std::numeric_limits::max(); } //! number of bits in the lower integer part, used a bit shift value. static const size_t low_bits = 8 * sizeof(low_type); //! return highest value storable in higher part, also used as a mask. static unsigned_type high_max() { return std::numeric_limits::max(); } //! number of bits in the higher integer part, used a bit shift value. static const size_t high_bits = 8 * sizeof(high_type); public: //! number of binary digits (bits) in uint_pair static const size_t digits = low_bits + high_bits; //! number of bytes in uint_pair static const size_t bytes = sizeof(low_type) + sizeof(high_type); //! empty constructor, does not even initialize to zero! inline uint_pair() { // compile-time assertions about size of low_type STXXL_STATIC_ASSERT(8 * sizeof(low_type) == 32); // compile-time assertions about size of our data structure, this tests // packing of structures by the compiler STXXL_STATIC_ASSERT(sizeof(uint_pair) == bytes); STXXL_STATIC_ASSERT(sizeof(uint_pair) == digits / 8); STXXL_STATIC_ASSERT(digits / 8 == bytes); } //! construct unit pair from lower and higher parts. inline uint_pair(const low_type& l, const high_type& h) : low(l), high(h) { } //! copy constructor inline uint_pair(const uint_pair& a) : low(a.low), high(a.high) { } //! const from a simple 32-bit unsigned integer inline uint_pair(const uint32& a) : low(a), high(0) { } //! const from a simple 32-bit signed integer inline uint_pair(const int32& a) : low(a), high(0) { if (a >= 0) low = a; else low = a, high = (high_type)high_max(); } //! construct from an uint64 (unsigned long long) inline uint_pair(const uint64& a) : low((low_type)(a & low_max())), high((high_type)((a >> low_bits) & high_max())) { // check for overflow assert((a >> (low_bits + high_bits)) == 0); } //! return the number as an uint64 (unsigned long long) inline uint64 ull() const { return ((uint64)high) << low_bits | (uint64)low; } //! implicit cast to an unsigned long long inline operator uint64 () const { return ull(); } //! return the number as a uint64 inline uint64 u64() const { return ((uint64)high) << low_bits | (uint64)low; } //! prefix increment operator (directly manipulates the integer parts) inline uint_pair& operator ++ () { if (UNLIKELY(low == low_max())) ++high, low = 0; else ++low; return *this; } //! prefix decrement operator (directly manipulates the integer parts) inline uint_pair& operator -- () { if (UNLIKELY(low == 0)) --high, low = (low_type)low_max(); else --low; return *this; } //! addition operator (uses 64-bit arithmetic) inline uint_pair& operator += (const uint_pair& b) { uint64 add = low + b.low; low = (low_type)(add & low_max()); high = (high_type)(high + b.high + ((add >> low_bits) & high_max())); return *this; } //! equality checking operator inline bool operator == (const uint_pair& b) const { return (low == b.low) && (high == b.high); } //! inequality checking operator inline bool operator != (const uint_pair& b) const { return (low != b.low) || (high != b.high); } //! less-than comparison operator inline bool operator < (const uint_pair& b) const { return (high < b.high) || (high == b.high && low < b.low); } //! less-or-equal comparison operator inline bool operator <= (const uint_pair& b) const { return (high < b.high) || (high == b.high && low <= b.low); } //! greater comparison operator inline bool operator > (const uint_pair& b) const { return (high > b.high) || (high == b.high && low > b.low); } //! greater-or-equal comparison operator inline bool operator >= (const uint_pair& b) const { return (high > b.high) || (high == b.high && low >= b.low); } //! make a uint_pair outputtable via iostreams, using unsigned long long. friend std::ostream& operator << (std::ostream& os, const uint_pair& a) { return os << a.ull(); } //! return an uint_pair instance containing the smallest value possible static uint_pair min() { return uint_pair(std::numeric_limits::min(), std::numeric_limits::min()); } //! return an uint_pair instance containing the largest value possible static uint_pair max() { return uint_pair(std::numeric_limits::max(), std::numeric_limits::max()); } } #if STXXL_MSVC ; #pragma pack(pop) #else __attribute__ ((packed)); #endif //! \addtogroup support //! \{ //! Construct a 40-bit unsigned integer stored in five bytes. typedef uint_pair uint40; //! Construct a 48-bit unsigned integer stored in six bytes. typedef uint_pair uint48; //! \} STXXL_END_NAMESPACE namespace std { //! template class providing some numeric_limits fields for uint_pair types. template class numeric_limits > { public: //! yes we have information about uint_pair static const bool is_specialized = true; //! return an uint_pair instance containing the smallest value possible static stxxl::uint_pair min() { return stxxl::uint_pair::min(); } //! return an uint_pair instance containing the largest value possible static stxxl::uint_pair max() { return stxxl::uint_pair::max(); } //! return an uint_pair instance containing the smallest value possible static stxxl::uint_pair lowest() { return min(); } //! unit_pair types are unsigned static const bool is_signed = false; //! uint_pair types are integers static const bool is_integer = true; //! unit_pair types contain exact integers static const bool is_exact = true; //! unit_pair radix is binary static const int radix = 2; //! number of binary digits (bits) in uint_pair static const int digits = stxxl::uint_pair::digits; //! epsilon is zero static const stxxl::uint_pair epsilon() { return stxxl::uint_pair(0, 0); } //! rounding error is zero static const stxxl::uint_pair round_error() { return stxxl::uint_pair(0, 0); } //! no exponent static const int min_exponent = 0; //! no exponent static const int min_exponent10 = 0; //! no exponent static const int max_exponent = 0; //! no exponent static const int max_exponent10 = 0; //! no infinity static const bool has_infinity = false; }; } // namespace std #endif // !STXXL_COMMON_UINT_TYPES_HEADER stxxl-1.4.1/include/stxxl/bits/common/log.h000644 001411 000144 00000002361 12405375303 020471 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/log.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004-2005 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_LOG_HEADER #define STXXL_COMMON_LOG_HEADER #include #include #include STXXL_BEGIN_NAMESPACE class logger : public singleton { friend class singleton; std::ofstream log_stream_; std::ofstream errlog_stream_; std::ofstream* waitlog_stream_; logger(); ~logger(); public: inline std::ofstream & log_stream() { return log_stream_; } inline std::ofstream & errlog_stream() { return errlog_stream_; } inline std::ofstream * waitlog_stream() { return waitlog_stream_; } }; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_LOG_HEADER stxxl-1.4.1/include/stxxl/bits/common/external_shared_ptr.h000644 001411 000144 00000007260 12410750556 023753 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/external_shared_ptr.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Daniel Godas-Lopez * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_EXTERNAL_SHARED_PTR_HEADER #define STXXL_COMMON_EXTERNAL_SHARED_PTR_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup support //! \{ /*! * This class takes a shared pointer, increments its reference count and wraps * it in a way that the resulting object can be copied, dumped to disk, and * destroyed without affecting the refcount. When the object is retrieved from * disk and recreated on internal memory, it will still hold a reference to the * same memory block and can be used right away by calling the "get" method or * unwrapped with the "unwrap" method to decrement the refcount. * * In the context of this template, a shared pointer is an object of a class P * that fulfills the following requirements: * * - Can be copy-constructed * - Has an assignment operator (so that the get method can be used) * - Contains a pointer to a reference count stored outside the class * - Increments the reference count on copy-construction * - Decrements the reference count on destruction * * Both the Boost and c++0x implementations of shared_ptr fulfill these * requirements. At the moment of writing the author is not aware of any * implementations of shared pointers that can't be used with this wrapper. */ template class external_shared_ptr { private: /*! * We store the pointer like this so that the refcount does not get * incremented when the wrapper is copy-constructed, or decremented when * the wrapper is destroyed. * * The whole external_shared_ptr object will be aligned by the compiler to * a multiple of its size. The size of the object is sizeof(P) as the * buffer is its only member. The buffer is placed in the class at offset 0 * so the alignment of the stored P should be alright without any * additional hints. */ char data[sizeof(P)]; public: /*! * This constructor needs to be defined so that the [] operator in maps and * hash tables works. If unwrap() or get() are called for an object * constructed this way the behavior is undefined. */ external_shared_ptr() { } /*! * Copy the pointer to internal storage and increment the refcount (the * destructor never gets called). */ external_shared_ptr(P ptr) { new (data)P(ptr); } /*! * Call the destructor to decrement the refcount. If this is called more * than once the results are undefined. */ void unwrap() { P* p = reinterpret_cast((void*)data); p->~P(); } /*! * If this is called after unwrap() the behaviour is undefined. */ P get() const { P* p = reinterpret_cast((void*)data); return *p; } bool operator == (const external_shared_ptr& x) const { P* p1 = reinterpret_cast((void*)data); P* p2 = reinterpret_cast((void*)x.data); return *p1 == *p2; } //! Output contained data items friend std::ostream& operator << (std::ostream& os, const external_shared_ptr& p) { return os << p.get(); } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_COMMON_EXTERNAL_SHARED_PTR_HEADER stxxl-1.4.1/include/stxxl/bits/common/exceptions.h000644 001411 000144 00000003720 12414452316 022071 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/exceptions.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_EXCEPTIONS_HEADER #define STXXL_COMMON_EXCEPTIONS_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE class io_error : public std::ios_base::failure { public: io_error() throw () : std::ios_base::failure("") { } io_error(const std::string& message) throw () : std::ios_base::failure(message) { } }; class resource_error : public std::runtime_error { public: resource_error() throw () : std::runtime_error("") { } resource_error(const std::string& message) throw () : std::runtime_error(message) { } }; class bad_ext_alloc : public std::runtime_error { public: bad_ext_alloc() throw () : std::runtime_error("") { } bad_ext_alloc(const std::string& message) throw () : std::runtime_error(message) { } }; class bad_parameter : public std::runtime_error { public: bad_parameter() throw () : std::runtime_error("") { } bad_parameter(const std::string& message) throw () : std::runtime_error(message) { } }; class unreachable : public std::runtime_error { public: unreachable() throw () : std::runtime_error("") { } unreachable(const std::string& message) throw () : std::runtime_error(message) { } }; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_EXCEPTIONS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/common/rand.h000644 001411 000144 00000017777 12414452316 020655 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/rand.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2003, 2005 Roman Dementiev * Copyright (C) 2007 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_RAND_HEADER #define STXXL_COMMON_RAND_HEADER #include #include #include #include #include #if STXXL_STD_RANDOM #include #elif STXXL_BOOST_RANDOM #include #endif // Recommended seeding procedure: // by default, the global seed is initialized from a high resolution timer and the process id // 1. stxxl::set_seed(seed); // optionally, do this if you wan't to us a specific seed to replay a certain program run // 2. seed = stxxl::get_next_seed(); // store/print/... this value can be used for step 1 to replay the program with a specific seed // 3. stxxl::srandom_number32(); // seed the global state of stxxl::random_number32 // 4. create all the other prngs used. STXXL_BEGIN_NAMESPACE extern unsigned ran32State; //! \addtogroup support //! \{ //! Fast uniform [0, 2^32) pseudo-random generator with period 2^32, random //! bits: 32. //! \warning Uses a global state and is not reentrant or thread-safe! struct random_number32 { typedef unsigned value_type; //! Returns a random number from [0, 2^32) inline value_type operator () () const { return (ran32State = 1664525 * ran32State + 1013904223); } //! Returns a random number from [0, N) inline value_type operator () (const value_type& N) const { return operator () () % N; } }; //! Set a seed value for \c random_number32. inline void srandom_number32(unsigned seed = 0) { if (!seed) seed = get_next_seed(); ran32State = seed; } //! Fast uniform [0, 2^32) pseudo-random generator with period 2^32, random //! bits: 32. //! Reentrant variant of random_number32 that keeps it's private state. struct random_number32_r { typedef unsigned value_type; mutable unsigned state; random_number32_r(unsigned seed = 0) { if (!seed) seed = get_next_seed(); state = seed; } //! Returns a random number from [0, 2^32) inline value_type operator () () const { return (state = 1664525 * state + 1013904223); } }; //! Fast uniform [0, 255] pseudo-random generator with period 2^8, random bits: //! 8 (one byte). class random_number8_r { random_number32_r m_rnd32; uint32 m_value; unsigned int m_pos; public: typedef uint8 value_type; random_number8_r(unsigned seed = 0) : m_rnd32(seed), m_pos(4) { } //! Returns a random byte from [0, 255] inline value_type operator () () { if (++m_pos >= 4) { m_value = m_rnd32(); m_pos = 0; } return ((uint8*)&m_value)[m_pos]; } }; //! Fast uniform [0.0, 1.0) pseudo-random generator //! \warning Uses a global state and is not reentrant or thread-safe! struct random_uniform_fast { typedef double value_type; random_number32 rnd32; random_uniform_fast(unsigned /*seed*/ = 0) { } //! Returns a random number from [0.0, 1.0) inline value_type operator () () const { return (double(rnd32()) * (0.5 / 0x80000000)); } }; #if STXXL_MSVC #pragma warning(push) #pragma warning(disable:4512) // assignment operator could not be generated #endif //! Slow and precise uniform [0.0, 1.0) pseudo-random generator //! period: at least 2^48, random bits: at least 31 //! //! \warning Seed is not the same as in the fast generator \c random_uniform_fast struct random_uniform_slow { typedef double value_type; #if STXXL_STD_RANDOM typedef std::default_random_engine gen_type; mutable gen_type gen; typedef std::uniform_real_distribution<> uni_type; mutable uni_type uni; random_uniform_slow(unsigned seed = 0) : gen(seed ? seed : get_next_seed()), uni(0.0, 1.0) { } #elif STXXL_BOOST_RANDOM typedef boost::minstd_rand base_generator_type; base_generator_type generator; boost::uniform_real<> uni_dist; mutable boost::variate_generator > uni; random_uniform_slow(unsigned seed = 0) : uni(generator, uni_dist) { if (!seed) seed = get_next_seed(); uni.engine().seed(seed); } #else mutable unsigned short state48[3]; /* * embedded erand48.c * * Copyright (c) 1993 Martin Birgmeier * All rights reserved. * * You may redistribute unmodified or modified versions of this source * code provided that the above copyright notice and this and the * following conditions are retained. * * This software is provided ``as is'', and comes with no warranties * of any kind. I shall in no event be liable for anything that happens * to anyone/anything when using this software. */ static void _dorand48(unsigned short xseed[3]) { unsigned long accu; unsigned short temp[2]; static const unsigned short _mult[3] = { 0xe66d, 0xdeec, 0x0005 }; static const unsigned short _add = 0x000b; accu = (unsigned long)_mult[0] * (unsigned long)xseed[0] + (unsigned long)_add; temp[0] = (unsigned short)accu; /* lower 16 bits */ accu >>= sizeof(unsigned short) * 8; accu += (unsigned long)_mult[0] * (unsigned long)xseed[1] + (unsigned long)_mult[1] * (unsigned long)xseed[0]; temp[1] = (unsigned short)accu; /* middle 16 bits */ accu >>= sizeof(unsigned short) * 8; accu += _mult[0] * xseed[2] + _mult[1] * xseed[1] + _mult[2] * xseed[0]; xseed[0] = temp[0]; xseed[1] = temp[1]; xseed[2] = (unsigned short)accu; } static double _erand48(unsigned short xseed[3]) { _dorand48(xseed); return ldexp((double)xseed[0], -48) + ldexp((double)xseed[1], -32) + ldexp((double)xseed[2], -16); } /* end erand48.c */ random_uniform_slow(unsigned seed = 0) { if (!seed) seed = get_next_seed(); state48[0] = (unsigned short)(seed & 0xffff); state48[1] = (unsigned short)(seed >> 16); state48[2] = 42; _dorand48(state48); } #endif //! Returns a random number from [0.0, 1.0) inline value_type operator () () const { #if STXXL_STD_RANDOM return uni(gen); #elif STXXL_BOOST_RANDOM return uni(); #else return _erand48(state48); #endif } }; //! Uniform [0, N) pseudo-random generator template struct random_number { typedef unsigned value_type; UniformRGen uniform; random_number(unsigned seed = 0) : uniform(seed) { } //! Returns a random number from [0, N) inline value_type operator () (value_type N) const { return static_cast(uniform() * double(N)); } }; //! Slow and precise uniform [0, 2^64) pseudo-random generator struct random_number64 { typedef stxxl::uint64 value_type; random_uniform_slow uniform; random_number64(unsigned seed = 0) : uniform(seed) { } //! Returns a random number from [0, 2^64) inline value_type operator () () const { return static_cast(uniform() * (18446744073709551616.)); } //! Returns a random number from [0, N) inline value_type operator () (value_type N) const { return static_cast(uniform() * double(N)); } }; #if STXXL_MSVC #pragma warning(pop) // assignment operator could not be generated #endif //! \} STXXL_END_NAMESPACE #endif // !STXXL_COMMON_RAND_HEADER stxxl-1.4.1/include/stxxl/bits/common/onoff_switch.h000644 001411 000144 00000004142 12405375303 022377 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/onoff_switch.h * * Kind of binary semaphore: initially OFF, then multiple waiters can attach * to the switch, which get notified one-by-one when switched ON. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_ONOFF_SWITCH_HEADER #define STXXL_COMMON_ONOFF_SWITCH_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE class onoff_switch : private noncopyable { //! mutex for condition variable mutex m_mutex; //! condition variable condition_variable m_cond; //! the switch's state bool m_on; public: //! construct switch onoff_switch(bool flag = false) : m_on(flag) { } //! turn switch ON and notify one waiter void on() { scoped_mutex_lock lock(m_mutex); m_on = true; lock.unlock(); m_cond.notify_one(); } //! turn switch OFF and notify one waiter void off() { scoped_mutex_lock lock(m_mutex); m_on = false; lock.unlock(); m_cond.notify_one(); } //! wait for switch to turn ON void wait_for_on() { scoped_mutex_lock lock(m_mutex); if (!m_on) m_cond.wait(lock); } //! wait for switch to turn OFF void wait_for_off() { scoped_mutex_lock lock(m_mutex); if (m_on) m_cond.wait(lock); } //! return true if switch is ON bool is_on() { scoped_mutex_lock lock(m_mutex); return m_on; } }; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_ONOFF_SWITCH_HEADER stxxl-1.4.1/include/stxxl/bits/common/cmdline.h000644 001411 000144 00000055672 12411366426 021343 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/cmdline.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_CMDLINE_HEADER #define STXXL_COMMON_CMDLINE_HEADER #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup support //! \{ /** * Command line parser which automatically fills variables and prints nice * usage messages. * * This is a straightforward command line parser in C++, which will recognize * short options -s, long options --long and parameters, both required and * optional. It will automatically parse integers and byte sizes with * SI/IEC suffixes (e.g. 1 GiB). It also works with lists of strings, * e.g. multiple filenames. * * Maybe most important it will nicely format the options and parameters * description using word wrapping. */ class cmdline_parser : private noncopyable { protected: //! base class of all options and parameters struct argument { //! single letter short option, or 0 is none char m_key; //! long option key or name for parameters std::string m_longkey; //! option type description, e.g. "<#>" to indicate numbers std::string m_keytype; //! longer description, which will be wrapped std::string m_desc; //! required, process() fails if the option/parameter is not found. bool m_required; //! found during processing of command line bool m_found; //! repeated argument, i.e. std::vector bool m_repeated; //! contructor filling most attributes argument(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required) : m_key(key), m_longkey(longkey), m_keytype(keytype), m_desc(desc), m_required(required), m_found(false), m_repeated(false) { } //! empty virtual destructor virtual ~argument() { } //! return formatted type name to user virtual const char * type_name() const = 0; //! process one item from command line for this argument virtual bool process(int& argc, const char* const*& argv) = 0; //! format value to ostream virtual void print_value(std::ostream& os) const = 0; //! return 'longkey [keytype]' std::string param_text() const { std::string s = m_longkey; if (m_keytype.size()) { s += ' ' + m_keytype; } return s; } //! return '-s, --longkey [keytype]' std::string option_text() const { std::string s; if (m_key) { s += '-'; s += m_key; s += ", "; } s += "--"; s += m_longkey; if (m_keytype.size()) { s += ' ' + m_keytype; } return s; } }; //! specialization of argument for boolean flags (can only be set to true). struct argument_flag : public argument { //! reference to boolean to set to true bool& m_dest; //! contructor filling most attributes argument_flag(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required, bool& dest) : argument(key, longkey, keytype, desc, required), m_dest(dest) { } virtual const char * type_name() const { return "flag"; } //! "process" argument: just set to true, no argument is used. virtual bool process(int&, const char* const*&) { m_dest = true; return true; } virtual void print_value(std::ostream& os) const { os << (m_dest ? "true" : "false"); } }; //! specialization of argument for integer options or parameters struct argument_int : public argument { int& m_dest; //! contructor filling most attributes argument_int(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required, int& dest) : argument(key, longkey, keytype, desc, required), m_dest(dest) { } virtual const char * type_name() const { return "integer"; } //! parse signed integer using sscanf. virtual bool process(int& argc, const char* const*& argv) { if (argc == 0) return false; if (sscanf(argv[0], "%d", &m_dest) == 1) { --argc, ++argv; return true; } else { return false; } } virtual void print_value(std::ostream& os) const { os << m_dest; } }; //! specialization of argument for unsigned integer options or parameters struct argument_uint : public argument { unsigned int& m_dest; //! contructor filling most attributes argument_uint(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required, unsigned int& dest) : argument(key, longkey, keytype, desc, required), m_dest(dest) { } virtual const char * type_name() const { return "unsigned integer"; } //! parse unsigned integer using sscanf. virtual bool process(int& argc, const char* const*& argv) { if (argc == 0) return false; if (sscanf(argv[0], "%u", &m_dest) == 1) { --argc, ++argv; return true; } else { return false; } } virtual void print_value(std::ostream& os) const { os << m_dest; } }; //! specialization of argument for SI/IEC suffixes byte size options or parameters struct argument_bytes32 : public argument { uint32& m_dest; //! contructor filling most attributes argument_bytes32(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required, uint32& dest) : argument(key, longkey, keytype, desc, required), m_dest(dest) { } virtual const char * type_name() const { return "bytes"; } //! parse byte size using SI/IEC parser from stxxl. virtual bool process(int& argc, const char* const*& argv) { if (argc == 0) return false; uint64 dest; if (parse_SI_IEC_size(argv[0], dest) && (uint64)(m_dest = (uint32)dest) == dest) { --argc, ++argv; return true; } else { return false; } } virtual void print_value(std::ostream& os) const { os << m_dest; } }; //! specialization of argument for SI/IEC suffixes byte size options or parameters struct argument_bytes64 : public argument { uint64& m_dest; //! contructor filling most attributes argument_bytes64(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required, uint64& dest) : argument(key, longkey, keytype, desc, required), m_dest(dest) { } virtual const char * type_name() const { return "bytes"; } //! parse byte size using SI/IEC parser from stxxl. virtual bool process(int& argc, const char* const*& argv) { if (argc == 0) return false; if (parse_SI_IEC_size(argv[0], m_dest)) { --argc, ++argv; return true; } else { return false; } } virtual void print_value(std::ostream& os) const { os << m_dest; } }; //! specialization of argument for string options or parameters struct argument_string : public argument { std::string& m_dest; //! contructor filling most attributes argument_string(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required, std::string& dest) : argument(key, longkey, keytype, desc, required), m_dest(dest) { } virtual const char * type_name() const { return "string"; } //! "process" string argument just by storing it. virtual bool process(int& argc, const char* const*& argv) { if (argc == 0) return false; m_dest = argv[0]; --argc, ++argv; return true; } virtual void print_value(std::ostream& os) const { os << '"' << m_dest << '"'; } }; //! specialization of argument for multiple string options or parameters struct argument_stringlist : public argument { std::vector& m_dest; //! contructor filling most attributes argument_stringlist(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool required, std::vector& dest) : argument(key, longkey, keytype, desc, required), m_dest(dest) { m_repeated = true; } virtual const char * type_name() const { return "string list"; } //! "process" string argument just by storing it in vector. virtual bool process(int& argc, const char* const*& argv) { if (argc == 0) return false; m_dest.push_back(argv[0]); --argc, ++argv; return true; } virtual void print_value(std::ostream& os) const { os << '['; for (size_t i = 0; i < m_dest.size(); ++i) { if (i != 0) os << ','; os << '"' << m_dest[i] << '"'; } os << ']'; } }; protected: //! option and parameter list type typedef std::vector arglist_type; //! list of options available arglist_type m_optlist; //! list of parameters, both required and optional arglist_type m_paramlist; //! formatting width for options, '-s, --switch <#>' int m_opt_maxlong; //! formatting width for parameters, 'param <#>' int m_param_maxlong; //! argv[0] for usage. const char* m_progname; //! verbose processing of arguments bool m_verbose_process; //! user set description of program, will be wrapped std::string m_description; //! user set author of program, will be wrapped std::string m_author; //! set line wrap length unsigned int m_linewrap; //! maximum length of a type_name() result static const int m_maxtypename = 16; private: //! update maximum formatting width for new option void calc_opt_max(const argument* arg) { m_opt_maxlong = STXXL_MAX((int)arg->option_text().size() + 2, m_opt_maxlong); } //! update maximum formatting width for new parameter void calc_param_max(const argument* arg) { m_param_maxlong = STXXL_MAX((int)arg->param_text().size() + 2, m_param_maxlong); } public: //! Wrap a long string at spaces into lines. Prefix is added //! unconditionally to each line. Lines are wrapped after wraplen //! characters if possible. static void output_wrap(std::ostream& os, const std::string& text, size_t wraplen, size_t indent_first = 0, size_t indent_rest = 0, size_t current = 0, size_t indent_newline = 0); public: //! Construct new command line parser cmdline_parser() : m_opt_maxlong(8), m_param_maxlong(8), m_progname(NULL), m_verbose_process(true), m_linewrap(80) { } //! Delete all added arguments ~cmdline_parser() { for (size_t i = 0; i < m_optlist.size(); ++i) delete m_optlist[i]; m_optlist.clear(); for (size_t i = 0; i < m_paramlist.size(); ++i) delete m_paramlist[i]; m_paramlist.clear(); } //! Set description of program, text will be wrapped void set_description(const std::string& description) { m_description = description; } //! Set author of program, will be wrapped. void set_author(const std::string& author) { m_author = author; } //! Set verbose processing of command line arguments void set_verbose_process(bool verbose_process) { m_verbose_process = verbose_process; } // ************************************************************************ //! add boolean option flag -key, --longkey [keytype] with description and store to dest void add_flag(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, bool& dest) { m_optlist.push_back( new argument_flag(key, longkey, keytype, desc, false, dest) ); calc_opt_max(m_optlist.back()); } //! add signed integer option -key, --longkey [keytype] with description and store to dest void add_int(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, int& dest) { m_optlist.push_back( new argument_int(key, longkey, keytype, desc, false, dest) ); calc_opt_max(m_optlist.back()); } //! add unsigned integer option -key, --longkey [keytype] with description and store to dest void add_uint(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, unsigned int& dest) { m_optlist.push_back( new argument_uint(key, longkey, keytype, desc, false, dest) ); calc_opt_max(m_optlist.back()); } //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and store to 64-bit dest void add_bytes(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, stxxl::uint32& dest) { m_optlist.push_back( new argument_bytes32(key, longkey, keytype, desc, false, dest) ); calc_opt_max(m_optlist.back()); } //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and store to 64-bit dest void add_bytes(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, stxxl::uint64& dest) { m_optlist.push_back( new argument_bytes64(key, longkey, keytype, desc, false, dest) ); calc_opt_max(m_optlist.back()); } //! add string option -key, --longkey [keytype] and store to dest void add_string(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, std::string& dest) { m_optlist.push_back( new argument_string(key, longkey, keytype, desc, false, dest) ); calc_opt_max(m_optlist.back()); } //! add string list option -key, --longkey [keytype] and store to dest void add_stringlist(char key, const std::string& longkey, const std::string& keytype, const std::string& desc, std::vector& dest) { m_optlist.push_back( new argument_stringlist(key, longkey, keytype, desc, false, dest) ); calc_opt_max(m_optlist.back()); } //! add boolean option flag -key, --longkey with description and store to dest void add_flag(char key, const std::string& longkey, const std::string& desc, bool& dest) { return add_flag(key, longkey, "", desc, dest); } //! add signed integer option -key, --longkey with description and store to dest void add_int(char key, const std::string& longkey, const std::string& desc, int& dest) { return add_int(key, longkey, "", desc, dest); } //! add unsigned integer option -key, --longkey [keytype] with description and store to dest void add_uint(char key, const std::string& longkey, const std::string& desc, unsigned int& dest) { return add_uint(key, longkey, "", desc, dest); } //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and store to 32-bit dest void add_bytes(char key, const std::string& longkey, const std::string& desc, stxxl::uint32& dest) { return add_bytes(key, longkey, "", desc, dest); } //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and store to 64-bit dest void add_bytes(char key, const std::string& longkey, const std::string& desc, stxxl::uint64& dest) { return add_bytes(key, longkey, "", desc, dest); } //! add string option -key, --longkey [keytype] and store to dest void add_string(char key, const std::string& longkey, const std::string& desc, std::string& dest) { return add_string(key, longkey, "", desc, dest); } //! add string list option -key, --longkey [keytype] and store to dest void add_stringlist(char key, const std::string& longkey, const std::string& desc, std::vector& dest) { return add_stringlist(key, longkey, "", desc, dest); } // ************************************************************************ //! add signed integer parameter [name] with description and store to dest void add_param_int(const std::string& name, const std::string& desc, int& dest) { m_paramlist.push_back( new argument_int(0, name, "", desc, true, dest) ); calc_param_max(m_paramlist.back()); } //! add unsigned integer parameter [name] with description and store to dest void add_param_uint(const std::string& name, const std::string& desc, unsigned int& dest) { m_paramlist.push_back( new argument_uint(0, name, "", desc, true, dest) ); calc_param_max(m_paramlist.back()); } //! add SI/IEC suffixes byte size parameter [name] with description and store to dest void add_param_bytes(const std::string& name, const std::string& desc, uint32& dest) { m_paramlist.push_back( new argument_bytes32(0, name, "", desc, true, dest) ); calc_param_max(m_paramlist.back()); } //! add SI/IEC suffixes byte size parameter [name] with description and store to dest void add_param_bytes(const std::string& name, const std::string& desc, uint64& dest) { m_paramlist.push_back( new argument_bytes64(0, name, "", desc, true, dest) ); calc_param_max(m_paramlist.back()); } //! add string parameter [name] with description and store to dest void add_param_string(const std::string& name, const std::string& desc, std::string& dest) { m_paramlist.push_back( new argument_string(0, name, "", desc, true, dest) ); calc_param_max(m_paramlist.back()); } //! add string list parameter [name] with description and store to dest. //! \warning this parameter must be last, as it will gobble all non-option arguments! void add_param_stringlist(const std::string& name, const std::string& desc, std::vector& dest) { m_paramlist.push_back( new argument_stringlist(0, name, "", desc, true, dest) ); calc_param_max(m_paramlist.back()); } // ************************************************************************ //! add optional signed integer parameter [name] with description and store to dest void add_opt_param_int(const std::string& name, const std::string& desc, int& dest) { m_paramlist.push_back( new argument_int(0, name, "", desc, false, dest) ); calc_param_max(m_paramlist.back()); } //! add optional unsigned integer parameter [name] with description and store to dest void add_opt_param_uint(const std::string& name, const std::string& desc, unsigned int& dest) { m_paramlist.push_back( new argument_uint(0, name, "", desc, false, dest) ); calc_param_max(m_paramlist.back()); } //! add optional SI/IEC suffixes byte size parameter [name] with description and store to dest void add_opt_param_bytes(const std::string& name, const std::string& desc, uint32& dest) { m_paramlist.push_back( new argument_bytes32(0, name, "", desc, false, dest) ); calc_param_max(m_paramlist.back()); } //! add optional SI/IEC suffixes byte size parameter [name] with description and store to dest void add_opt_param_bytes(const std::string& name, const std::string& desc, uint64& dest) { m_paramlist.push_back( new argument_bytes64(0, name, "", desc, false, dest) ); calc_param_max(m_paramlist.back()); } //! add optional string parameter [name] with description and store to dest void add_opt_param_string(const std::string& name, const std::string& desc, std::string& dest) { m_paramlist.push_back( new argument_string(0, name, "", desc, false, dest) ); calc_param_max(m_paramlist.back()); } //! add optional string parameter [name] with description and store to dest //! \warning this parameter must be last, as it will gobble all non-option arguments! void add_opt_param_stringlist(const std::string& name, const std::string& desc, std::vector& dest) { m_paramlist.push_back( new argument_stringlist(0, name, "", desc, false, dest) ); calc_param_max(m_paramlist.back()); } // ************************************************************************ //! output nicely formatted usage information including description of all //! parameters and options. void print_usage(std::ostream& os = std::cout); private: //! print error about option. void print_option_error(int argc, const char* const* argv, const argument* arg, std::ostream& os); //! print error about parameter. void print_param_error(int argc, const char* const* argv, const argument* arg, std::ostream& os); public: //! parse command line options as specified by the options and parameters added. //! \return true if command line is okay and all required parameters are present. bool process(int argc, const char* const* argv, std::ostream& os = std::cout); //! print nicely formatted result of processing void print_result(std::ostream& os = std::cout); }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_COMMON_CMDLINE_HEADER stxxl-1.4.1/include/stxxl/bits/common/tuple.h000644 001411 000144 00000046141 12405375303 021045 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/tuple.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_TUPLE_HEADER #define STXXL_COMMON_TUPLE_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE struct Plug { }; template struct tuple_base { typedef T1 first_type; typedef T2 second_type; typedef T3 third_type; typedef T4 fourth_type; typedef T5 fifth_type; typedef T6 sixth_type; template struct item_type { /* typedef typename SWITCH > > > > > > >::result result; */ }; }; //! k-Tuple data type //! //! (defined for k < 7) template struct tuple { //! First tuple component type typedef T1 first_type; //! Second tuple component type typedef T2 second_type; //! Third tuple component type typedef T3 third_type; //! Fourth tuple component type typedef T4 fourth_type; //! Fifth tuple component type typedef T5 fifth_type; //! Sixth tuple component type typedef T6 sixth_type; template struct item_type { typedef typename SWITCH > > > > > > >::result result; }; //! First tuple component first_type first; //! Second tuple component second_type second; //! Third tuple component third_type third; //! Fourth tuple component fourth_type fourth; //! Fifth tuple component fifth_type fifth; //! Sixth tuple component sixth_type sixth; //! Empty constructor tuple() { } //! Construct tuple from components tuple(first_type _first, second_type _second, third_type _third, fourth_type _fourth, fifth_type _fifth, sixth_type _sixth ) : first(_first), second(_second), third(_third), fourth(_fourth), fifth(_fifth), sixth(_sixth) { } //! Equality comparison bool operator == (const tuple& t) const { return first == t.first && second == t.second && third == t.third && fourth == t.fourth && fifth == t.fifth && sixth == t.sixth; } //! Inequality comparison bool operator != (const tuple& t) const { return !(first == t.first && second == t.second && third == t.third && fourth == t.fourth && fifth == t.fifth && sixth == t.sixth); } //! Make tuple ostream-able friend std::ostream& operator << (std::ostream& os, const tuple& t) { return os << '(' << t.first << ',' << t.second << ',' << t.third << ',' << t.fourth << ',' << t.fifth << ',' << t.sixth << ')'; } //! Return minimum value of tuple using numeric_limits static tuple min_value() { return tuple(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); } //! Return maximum value of tuple using numeric_limits static tuple max_value() { return tuple(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); } }; //! Partial specialization for 1- \c tuple template struct tuple { //! First tuple component type typedef T1 first_type; //! First tuple component first_type first; template struct item_type { typedef typename IF::result result; }; //! Empty constructor tuple() { } //! Initializing constructor tuple(first_type first_) : first(first_) { } //! Equality comparison bool operator == (const tuple& t) const { return first == t.first; } //! Inequality comparison bool operator != (const tuple& t) const { return !(first == t.first); } //! Make tuple ostream-able friend std::ostream& operator << (std::ostream& os, const tuple& t) { return os << '(' << t.first << ')'; } //! Return minimum value of tuple using numeric_limits static tuple min_value() { return tuple(std::numeric_limits::min()); } //! Return maximum value of tuple using numeric_limits static tuple max_value() { return tuple(std::numeric_limits::max()); } }; //! Partial specialization for 2- \c tuple (equivalent to std::pair) template struct tuple { //! First tuple component type typedef T1 first_type; //! Second tuple component type typedef T2 second_type; template struct item_type { typedef typename SWITCH > > >::result result; }; //! First tuple component first_type first; //! Second tuple component second_type second; //! Empty constructor tuple() { } //! Initializing constructor tuple(first_type first_, second_type second_) : first(first_), second(second_) { } //! Equality comparison bool operator == (const tuple& t) const { return first == t.first && second == t.second; } //! Inequality comparison bool operator != (const tuple& t) const { return !(first == t.first && second == t.second); } //! Make tuple ostream-able friend std::ostream& operator << (std::ostream& os, const tuple& t) { return os << '(' << t.first << ',' << t.second << ')'; } //! Return minimum value of tuple using numeric_limits static tuple min_value() { return tuple(std::numeric_limits::min(), std::numeric_limits::min()); } //! Return maximum value of tuple using numeric_limits static tuple max_value() { return tuple(std::numeric_limits::max(), std::numeric_limits::max()); } }; //! Partial specialization for 3- \c tuple (triple) template struct tuple { //! First tuple component type typedef T1 first_type; //! Second tuple component type typedef T2 second_type; //! Third tuple component type typedef T3 third_type; template struct item_type { typedef typename SWITCH > > > >::result result; }; //! First tuple component first_type first; //! Second tuple component second_type second; //! Third tuple component third_type third; //! Empty constructor tuple() { } //! Construct tuple from components tuple(first_type _first, second_type _second, third_type _third) : first(_first), second(_second), third(_third) { } //! Equality comparison bool operator == (const tuple& t) const { return first == t.first && second == t.second && third == t.third; } //! Inequality comparison bool operator != (const tuple& t) const { return !(first == t.first && second == t.second && third == t.third); } //! Make tuple ostream-able friend std::ostream& operator << (std::ostream& os, const tuple& t) { return os << '(' << t.first << ',' << t.second << ',' << t.third << ')'; } //! Return minimum value of tuple using numeric_limits static tuple min_value() { return tuple(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); } //! Return maximum value of tuple using numeric_limits static tuple max_value() { return tuple(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); } }; //! Partial specialization for 4- \c tuple template struct tuple { //! First tuple component type typedef T1 first_type; //! Second tuple component type typedef T2 second_type; //! Third tuple component type typedef T3 third_type; //! Fourth tuple component type typedef T4 fourth_type; template struct item_type { typedef typename SWITCH > > > > >::result result; }; //! First tuple component first_type first; //! Second tuple component second_type second; //! Third tuple component third_type third; //! Fourth tuple component fourth_type fourth; //! Empty constructor tuple() { } //! Construct tuple from components tuple(first_type _first, second_type _second, third_type _third, fourth_type _fourth) : first(_first), second(_second), third(_third), fourth(_fourth) { } //! Equality comparison bool operator == (const tuple& t) const { return first == t.first && second == t.second && third == t.third && fourth == t.fourth; } //! Inequality comparison bool operator != (const tuple& t) const { return !(first == t.first && second == t.second && third == t.third && fourth == t.fourth); } //! Make tuple ostream-able friend std::ostream& operator << (std::ostream& os, const tuple& t) { return os << '(' << t.first << ',' << t.second << ',' << t.third << ',' << t.fourth << ')'; } //! Return minimum value of tuple using numeric_limits static tuple min_value() { return tuple(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); } //! Return maximum value of tuple using numeric_limits static tuple max_value() { return tuple(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); } }; //! Partial specialization for 5- \c tuple template struct tuple { //! First tuple component type typedef T1 first_type; //! Second tuple component type typedef T2 second_type; //! Third tuple component type typedef T3 third_type; //! Fourth tuple component type typedef T4 fourth_type; //! Fifth tuple component type typedef T5 fifth_type; template struct item_type { typedef typename SWITCH > > > > > >::result result; }; //! First tuple component first_type first; //! Second tuple component second_type second; //! Third tuple component third_type third; //! Fourth tuple component fourth_type fourth; //! Fifth tuple component fifth_type fifth; //! Empty constructor tuple() { } //! Construct tuple from components tuple(first_type _first, second_type _second, third_type _third, fourth_type _fourth, fifth_type _fifth) : first(_first), second(_second), third(_third), fourth(_fourth), fifth(_fifth) { } //! Equality comparison bool operator == (const tuple& t) const { return first == t.first && second == t.second && third == t.third && fourth == t.fourth && fifth == t.fifth; } //! Inequality comparison bool operator != (const tuple& t) const { return !(first == t.first && second == t.second && third == t.third && fourth == t.fourth && fifth == t.fifth); } //! Make tuple ostream-able friend std::ostream& operator << (std::ostream& os, const tuple& t) { return os << '(' << t.first << ',' << t.second << ',' << t.third << ',' << t.fourth << ',' << t.fifth << ')'; } //! Return minimum value of tuple using numeric_limits static tuple min_value() { return tuple(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); } //! Return maximum value of tuple using numeric_limits static tuple max_value() { return tuple(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); } }; /* template typename tuple_type::item_type::result get(const tuple_type & t) { return NULL; } */ template struct tuple_less1st { typedef TupleType value_type; bool operator () (const value_type& a, const value_type& b) const { return (a.first < b.first); } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; template struct tuple_greater1st { typedef TupleType value_type; bool operator () (const value_type& a, const value_type& b) const { return (a.first > b.first); } static value_type min_value() { return value_type::max_value(); } static value_type max_value() { return value_type::min_value(); } }; template struct tuple_less1st_less2nd { typedef TupleType value_type; bool operator () (const value_type& a, const value_type& b) const { if (a.first == b.first) return (a.second < b.second); return (a.first < b.first); } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; template struct tuple_less2nd { typedef TupleType value_type; bool operator () (const value_type& a, const value_type& b) const { return (a.second < b.second); } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; namespace stream { /** * Counter for creating tuple indexes for example. */ template struct counter { public: typedef ValueType value_type; protected: value_type m_count; public: counter(const value_type& start = 0) : m_count(start) { } const value_type& operator * () const { return m_count; } counter& operator ++ () { ++m_count; return *this; } bool empty() const { return false; } }; /** * Concatenates two tuple streams as streamA . streamB */ template class concatenate { public: typedef typename StreamA::value_type value_type; private: StreamA& A; StreamB& B; public: concatenate(StreamA& A_, StreamB& B_) : A(A_), B(B_) { assert(!A.empty()); assert(!B.empty()); } const value_type& operator * () const { assert(!empty()); if (!A.empty()) { return *A; } else { return *B; } } concatenate& operator ++ () { assert(!empty()); if (!A.empty()) { ++A; } else if (!B.empty()) { ++B; } return *this; } bool empty() const { return (A.empty() && B.empty()); } }; } // namespace stream STXXL_END_NAMESPACE #endif // !STXXL_COMMON_TUPLE_HEADER stxxl-1.4.1/include/stxxl/bits/common/settings.h000644 001411 000144 00000001713 12414452316 021550 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/settings.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_SETTINGS_HEADER #define STXXL_COMMON_SETTINGS_HEADER /*! * @file settings.h * Provides a static class to store runtime tuning parameters. */ #include STXXL_BEGIN_NAMESPACE template class settings { public: static bool native_merge; }; template bool settings::native_merge = false; typedef settings<> SETTINGS; STXXL_END_NAMESPACE #endif // !STXXL_COMMON_SETTINGS_HEADER stxxl-1.4.1/include/stxxl/bits/common/error_handling.h000644 001411 000144 00000017026 12422212055 022702 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/common/error_handling.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007-2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_COMMON_ERROR_HANDLING_HEADER #define STXXL_COMMON_ERROR_HANDLING_HEADER /** \file error_handling.h * Macros for convenient error checking and reporting via exception. */ #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE #if STXXL_MSVC #define STXXL_PRETTY_FUNCTION_NAME __FUNCTION__ #else #define STXXL_PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__ #endif //////////////////////////////////////////////////////////////////////////// //! Throws exception_type with "Error in [location] : [error_message]" #define STXXL_THROW2(exception_type, location, error_message) \ do { \ std::ostringstream msg; \ msg << "Error in " << location << " : " << error_message; \ throw exception_type(msg.str()); \ } while (false) //! Throws exception_type with "Error in [function] : [error_message]" #define STXXL_THROW(exception_type, error_message) \ STXXL_THROW2(exception_type, \ STXXL_PRETTY_FUNCTION_NAME, \ error_message) //! Throws exception_type with "Error in [function] : [error_message] : [errno_value message]" #define STXXL_THROW_ERRNO2(exception_type, error_message, errno_value) \ STXXL_THROW2(exception_type, \ STXXL_PRETTY_FUNCTION_NAME, \ error_message << " : " << strerror(errno_value)) //! Throws exception_type with "Error in [function] : [error_message] : [errno message]" #define STXXL_THROW_ERRNO(exception_type, error_message) \ STXXL_THROW_ERRNO2(exception_type, error_message, errno) //! Throws std::invalid_argument with "Error in [function] : [error_message]" #define STXXL_THROW_INVALID_ARGUMENT(error_message) \ STXXL_THROW2(std::invalid_argument, \ STXXL_PRETTY_FUNCTION_NAME, \ error_message) //! Throws stxxl::unreachable with "Error in file [file], line [line] : this code should never be reachable" #define STXXL_THROW_UNREACHABLE() \ STXXL_THROW2(stxxl::unreachable, \ "file " << __FILE__ << ", line " << __LINE__, \ "this code should never be reachable") //////////////////////////////////////////////////////////////////////////// //! Throws exception_type if (expr) with "Error in [function] : [error_message]" #define STXXL_THROW_IF(expr, exception_type, error_message) \ do { \ if (expr) { \ STXXL_THROW(exception_type, error_message); \ } \ } while (false) //! Throws exception_type if (expr != 0) with "Error in [function] : [error_message]" #define STXXL_THROW_NE_0(expr, exception_type, error_message) \ STXXL_THROW_IF((expr) != 0, exception_type, error_message) //! Throws exception_type if (expr == 0) with "Error in [function] : [error_message]" #define STXXL_THROW_EQ_0(expr, exception_type, error_message) \ STXXL_THROW_IF((expr) == 0, exception_type, error_message) //! Throws exception_type if (expr < 0) with "Error in [function] : [error_message]" #define STXXL_THROW_LT_0(expr, exception_type, error_message) \ STXXL_THROW_IF((expr) < 0, exception_type, error_message) //////////////////////////////////////////////////////////////////////////// //! Throws exception_type if (expr) with "Error in [function] : [error_message] : [errno message]" #define STXXL_THROW_ERRNO_IF(expr, exception_type, error_message) \ do { \ if (expr) { \ STXXL_THROW_ERRNO(exception_type, error_message); \ } \ } while (false) //! Throws exception_type if (expr != 0) with "Error in [function] : [error_message] : [errno message]" #define STXXL_THROW_ERRNO_NE_0(expr, exception_type, error_message) \ STXXL_THROW_ERRNO_IF((expr) != 0, exception_type, error_message) //! Throws exception_type if (expr == 0) with "Error in [function] : [error_message] : [errno message]" #define STXXL_THROW_ERRNO_EQ_0(expr, exception_type, error_message) \ STXXL_THROW_ERRNO_IF((expr) == 0, exception_type, error_message) //! Throws exception_type if (expr < 0) with "Error in [function] : [error_message] : [errno message]" #define STXXL_THROW_ERRNO_LT_0(expr, exception_type, error_message) \ STXXL_THROW_ERRNO_IF((expr) < 0, exception_type, error_message) //////////////////////////////////////////////////////////////////////////// //! Checks pthread call, if return != 0, throws stxxl::resource_error with "Error in [function] : [pthread_expr] : [errno message] #define STXXL_CHECK_PTHREAD_CALL(expr) \ do { \ int res = (expr); \ if (res != 0) { \ STXXL_THROW_ERRNO2(stxxl::resource_error, #expr, res); \ } \ } while (false) //////////////////////////////////////////////////////////////////////////// #if STXXL_WINDOWS || defined(__MINGW32__) //! Throws exception_type with "Error in [function] : [error_message] : [formatted GetLastError()]" #define STXXL_THROW_WIN_LASTERROR(exception_type, error_message) \ do { \ LPVOID lpMsgBuf; \ DWORD dw = GetLastError(); \ FormatMessage( \ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, \ NULL, dw, \ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \ (LPTSTR)&lpMsgBuf, \ 0, NULL); \ std::ostringstream msg; \ msg << "Error in " << STXXL_PRETTY_FUNCTION_NAME \ << " : " << error_message \ << " : error code " << dw << " : " << ((char*)lpMsgBuf); \ LocalFree(lpMsgBuf); \ throw exception_type(msg.str()); \ } while (false) #endif //////////////////////////////////////////////////////////////////////////// STXXL_END_NAMESPACE #endif // !STXXL_COMMON_ERROR_HANDLING_HEADER stxxl-1.4.1/include/stxxl/bits/mng/adaptor.h000644 001411 000144 00000056520 12414452316 020641 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/adaptor.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2003 Roman Dementiev * Copyright (C) 2007 Johannes Singler * Copyright (C) 2009-2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_ADAPTOR_HEADER #define STXXL_MNG_ADAPTOR_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup mnglayer //! //! \{ template class blocked_index { unsigned_type pos; unsigned_type block; unsigned_type offset; //! \invariant block * modulo + offset = pos void set(unsigned_type pos) { this->pos = pos; block = pos / modulo; offset = pos % modulo; } public: blocked_index() { set(0); } blocked_index(unsigned_type pos) { set(pos); } blocked_index(unsigned_type block, unsigned_type offset) { this->block = block; this->offset = offset; pos = block * modulo + offset; } void operator = (unsigned_type pos) { set(pos); } //pre-increment operator blocked_index& operator ++ () { ++pos; ++offset; if (offset == modulo) { offset = 0; ++block; } return *this; } //post-increment operator blocked_index operator ++ (int) { blocked_index former(*this); operator ++ (); return former; } //pre-increment operator blocked_index& operator -- () { --pos; if (offset == 0) { offset = modulo; --block; } --offset; return *this; } //post-increment operator blocked_index operator -- (int) { blocked_index former(*this); operator -- (); return former; } blocked_index& operator += (unsigned_type addend) { set(pos + addend); return *this; } blocked_index& operator >>= (unsigned_type shift) { set(pos >> shift); return *this; } operator unsigned_type () const { return pos; } const unsigned_type & get_block() const { return block; } const unsigned_type & get_offset() const { return offset; } }; #define STXXL_ADAPTOR_ARITHMETICS(pos) \ bool operator == (const self_type& a) const \ { \ return (a.pos == pos); \ } \ bool operator != (const self_type& a) const \ { \ return (a.pos != pos); \ } \ bool operator < (const self_type& a) const \ { \ return (pos < a.pos); \ } \ bool operator > (const self_type& a) const \ { \ return (pos > a.pos); \ } \ bool operator <= (const self_type& a) const \ { \ return (pos <= a.pos); \ } \ bool operator >= (const self_type& a) const \ { \ return (pos >= a.pos); \ } \ self_type operator + (pos_type off) const \ { \ return self_type(array, pos + off); \ } \ self_type operator - (pos_type off) const \ { \ return self_type(array, pos - off); \ } \ self_type& operator ++ () \ { \ pos++; \ return *this; \ } \ self_type operator ++ (int) \ { \ self_type tmp = *this; \ pos++; \ return tmp; \ } \ self_type& operator -- () \ { \ pos--; \ return *this; \ } \ self_type operator -- (int) \ { \ self_type tmp = *this; \ pos--; \ return tmp; \ } \ pos_type operator - (const self_type& a) const \ { \ return pos - a.pos; \ } \ self_type& operator -= (pos_type off) \ { \ pos -= off; \ return *this; \ } \ self_type& operator += (pos_type off) \ { \ pos += off; \ return *this; \ } template struct two2one_dim_array_adapter_base : public std::iterator { typedef OneDimArrayType one_dim_array_type; typedef DataType data_type; typedef PosType pos_type; typedef two2one_dim_array_adapter_base self_type; one_dim_array_type* array; pos_type pos; two2one_dim_array_adapter_base() { } two2one_dim_array_adapter_base(one_dim_array_type* a, pos_type p) : array(a), pos(p) { } two2one_dim_array_adapter_base(const two2one_dim_array_adapter_base& a) : array(a.array), pos(a.pos) { } STXXL_ADAPTOR_ARITHMETICS(pos) }; ////////////////////////////// #define BLOCK_ADAPTOR_OPERATORS(two_to_one_dim_array_adaptor_base) \ \ template \ inline two_to_one_dim_array_adaptor_base& operator ++ ( \ two_to_one_dim_array_adaptor_base& a) \ { \ a.pos++; \ return a; \ } \ \ template \ inline two_to_one_dim_array_adaptor_base operator ++ ( \ two_to_one_dim_array_adaptor_base& a, int) \ { \ two_to_one_dim_array_adaptor_base tmp = a; \ a.pos++; \ return tmp; \ } \ \ template \ inline two_to_one_dim_array_adaptor_base& operator -- ( \ two_to_one_dim_array_adaptor_base& a) \ { \ a.pos--; \ return a; \ } \ \ template \ inline two_to_one_dim_array_adaptor_base operator -- ( \ two_to_one_dim_array_adaptor_base& a, int) \ { \ two_to_one_dim_array_adaptor_base tmp = a; \ a.pos--; \ return tmp; \ } \ \ template \ inline two_to_one_dim_array_adaptor_base& operator -= ( \ two_to_one_dim_array_adaptor_base& a, \ typename two_to_one_dim_array_adaptor_base::_pos_type off) \ { \ a.pos -= off; \ return a; \ } \ \ template \ inline two_to_one_dim_array_adaptor_base& operator += ( \ two_to_one_dim_array_adaptor_base& a, \ typename two_to_one_dim_array_adaptor_base::_pos_type off) \ { \ a.pos += off; \ return a; \ } \ \ template \ inline two_to_one_dim_array_adaptor_base operator + ( \ const two_to_one_dim_array_adaptor_base& a, \ typename two_to_one_dim_array_adaptor_base::_pos_type off) \ { \ return two_to_one_dim_array_adaptor_base(a.array, a.pos + off); \ } \ \ template \ inline two_to_one_dim_array_adaptor_base operator + ( \ typename two_to_one_dim_array_adaptor_base::_pos_type off, \ const two_to_one_dim_array_adaptor_base& a) \ { \ return two_to_one_dim_array_adaptor_base(a.array, a.pos + off); \ } \ \ template \ inline two_to_one_dim_array_adaptor_base operator - ( \ const two_to_one_dim_array_adaptor_base& a, \ typename two_to_one_dim_array_adaptor_base::_pos_type off) \ { \ return two_to_one_dim_array_adaptor_base(a.array, a.pos - off); \ } #if 0 ////////////////////////// template > struct two2one_dim_array_row_adapter : public two2one_dim_array_adapter_base { typedef OneDimArrayType one_dim_array_type; typedef DataType data_type; typedef DimSize dim_type; typedef PosType pos_type; typedef two2one_dim_array_row_adapter self_type; typedef two2one_dim_array_adapter_base base_type; using base_type::array; using base_type::pos; two2one_dim_array_row_adapter() { } two2one_dim_array_row_adapter(one_dim_array_type* a, pos_type p) : two2one_dim_array_adapter_base(a, p) { } two2one_dim_array_row_adapter(const two2one_dim_array_row_adapter& a) : two2one_dim_array_adapter_base(a) { } data_type& operator * () { return array[(pos).get_block()][(pos).get_offset()]; } data_type* operator -> () const { return &(array[(pos).get_block()][(pos).get_offset()]); } data_type& operator [] (pos_type n) { n += pos; return array[(n) / dim_size][(n) % dim_size]; } const data_type& operator [] (pos_type n) const { n += pos; return array[(n) / dim_size][(n) % dim_size]; } STXXL_ADAPTOR_ARITHMETICS(pos) }; template > struct two2one_dim_array_column_adapter : public two2one_dim_array_adapter_base { typedef two2one_dim_array_column_adapter self_type; using two2one_dim_array_adapter_base::pos; using two2one_dim_array_adapter_base::array; two2one_dim_array_column_adapter(one_dim_array_type* a, pos_type p) : two2one_dim_array_adapter_base(a, p) { } two2one_dim_array_column_adapter(const self_type& a) : two2one_dim_array_adapter_base(a) { } data_type& operator * () { return array[(pos).get_offset()][(pos).get_block()]; } data_type* operator -> () const { return &(array[(pos).get_offset()][(pos).get_block()]); } const data_type& operator [] (pos_type n) const { n += pos; return array[(n) % dim_size][(n) / dim_size]; } data_type& operator [] (pos_type n) { n += pos; return array[(n) % dim_size][(n) / dim_size]; } STXXL_ADAPTOR_ARITHMETICS(pos) }; #endif template class array_of_sequences_iterator : public std::iterator { public: typedef ArrayType array_type; typedef ValueType value_type; protected: unsigned_type pos; unsigned_type offset; array_type* arrays; array_type* base; value_type* base_element; //! \invariant block * modulo + offset = pos void set(unsigned_type pos) { this->pos = pos; offset = pos % modulo; base = arrays + pos / modulo; base_element = base->elem; } public: array_of_sequences_iterator() { this->arrays = NULL; set(0); } array_of_sequences_iterator(array_type* arrays) { this->arrays = arrays; set(0); } array_of_sequences_iterator(array_type* arrays, unsigned_type pos) { this->arrays = arrays; set(pos); } void operator = (unsigned_type pos) { set(pos); } //pre-increment operator array_of_sequences_iterator& operator ++ () { ++pos; ++offset; if (offset == modulo) { offset = 0; ++base; base_element = base->elem; } return *this; } //post-increment operator array_of_sequences_iterator operator ++ (int) { array_of_sequences_iterator former(*this); operator ++ (); return former; } //pre-increment operator array_of_sequences_iterator& operator -- () { --pos; if (offset == 0) { offset = modulo; --base; base_element = base->elem; } --offset; return *this; } //post-increment operator array_of_sequences_iterator operator -- (int) { array_of_sequences_iterator former(*this); operator -- (); return former; } array_of_sequences_iterator& operator += (unsigned_type addend) { set(pos + addend); return *this; } array_of_sequences_iterator& operator -= (unsigned_type addend) { set(pos - addend); return *this; } array_of_sequences_iterator operator + (unsigned_type addend) const { return array_of_sequences_iterator(arrays, pos + addend); } array_of_sequences_iterator operator - (unsigned_type subtrahend) const { return array_of_sequences_iterator(arrays, pos - subtrahend); } unsigned_type operator - (const array_of_sequences_iterator& subtrahend) const { return pos - subtrahend.pos; } bool operator == (const array_of_sequences_iterator& aoai) const { return pos == aoai.pos; } bool operator != (const array_of_sequences_iterator& aoai) const { return pos != aoai.pos; } bool operator < (const array_of_sequences_iterator& aoai) const { return pos < aoai.pos; } bool operator <= (const array_of_sequences_iterator& aoai) const { return pos <= aoai.pos; } bool operator > (const array_of_sequences_iterator& aoai) const { return pos > aoai.pos; } bool operator >= (const array_of_sequences_iterator& aoai) const { return pos >= aoai.pos; } const value_type& operator * () const { return base_element[offset]; } value_type& operator * () { return base_element[offset]; } const value_type& operator -> () const { return &(base_element[offset]); } value_type& operator -> () { return &(base_element[offset]); } const value_type& operator [] (unsigned_type index) const { return arrays[index / modulo][index % modulo]; } value_type& operator [] (unsigned_type index) { return arrays[index / modulo][index % modulo]; } }; namespace helper { template class element_iterator_generator { }; // default case for blocks with fillers or other data: use array_of_sequences_iterator template class element_iterator_generator { typedef BlockType block_type; typedef typename block_type::value_type value_type; typedef SizeType size_type; public: typedef array_of_sequences_iterator iterator; iterator operator () (block_type* blocks, SizeType offset) const { return iterator(blocks, offset); } }; // special case for completely filled blocks: use trivial pointers template class element_iterator_generator { typedef BlockType block_type; typedef typename block_type::value_type value_type; typedef SizeType size_type; public: typedef value_type* iterator; iterator operator () (block_type* blocks, SizeType offset) const { return blocks[0].elem + offset; } }; } // namespace helper template struct element_iterator_traits { typedef typename helper::element_iterator_generator< BlockType, SizeType, BlockType::has_only_data >::iterator element_iterator; }; template inline typename element_iterator_traits::element_iterator make_element_iterator(BlockType* blocks, SizeType offset) { helper::element_iterator_generator< BlockType, SizeType, BlockType::has_only_data > iter_gen; return iter_gen(blocks, offset); } //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_ADAPTOR_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/block_alloc.h000644 001411 000144 00000013643 12414452316 021452 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/block_alloc.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2007 Roman Dementiev * Copyright (C) 2007-2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BLOCK_ALLOC_HEADER #define STXXL_MNG_BLOCK_ALLOC_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \defgroup alloc Allocation Functors //! \ingroup mnglayer //! Standard allocation strategies encapsulated in functors. //! \{ //! Example disk allocation scheme functor. //! \remarks model of \b allocation_strategy concept struct basic_allocation_strategy { basic_allocation_strategy(int disks_begin, int disks_end); basic_allocation_strategy(); int operator () (int i) const; static const char * name(); }; //! Striping disk allocation scheme functor. //! \remarks model of \b allocation_strategy concept struct striping { unsigned_type begin, diff; public: striping(unsigned_type b, unsigned_type e) : begin(b), diff(e - b) { } striping() : begin(0) { diff = config::get_instance()->disks_number(); } unsigned_type operator () (unsigned_type i) const { return begin + i % diff; } static const char * name() { return "striping"; } }; //! Fully randomized disk allocation scheme functor. //! \remarks model of \b allocation_strategy concept struct FR : public striping { private: typedef random_number rnd_type; rnd_type rnd; public: FR(unsigned_type b, unsigned_type e) : striping(b, e) { } FR() : striping() { } unsigned_type operator () (unsigned_type /*i*/) const { return begin + rnd(rnd_type::value_type(diff)); } static const char * name() { return "fully randomized striping"; } }; //! Simple randomized disk allocation scheme functor. //! \remarks model of \b allocation_strategy concept struct SR : public striping { private: unsigned_type offset; typedef random_number rnd_type; void init() { rnd_type rnd; offset = rnd(rnd_type::value_type(diff)); } public: SR(unsigned_type b, unsigned_type e) : striping(b, e) { init(); } SR() : striping() { init(); } unsigned_type operator () (unsigned_type i) const { return begin + (i + offset) % diff; } static const char * name() { return "simple randomized striping"; } }; //! Randomized cycling disk allocation scheme functor. //! \remarks model of \b allocation_strategy concept struct RC : public striping { private: std::vector perm; void init() { for (unsigned_type i = 0; i < diff; i++) perm[i] = i; stxxl::random_number rnd; std::random_shuffle(perm.begin(), perm.end(), rnd _STXXL_FORCE_SEQUENTIAL); } public: RC(unsigned_type b, unsigned_type e) : striping(b, e), perm(diff) { init(); } RC() : striping(), perm(diff) { init(); } unsigned_type operator () (unsigned_type i) const { return begin + perm[i % diff]; } static const char * name() { return "randomized cycling striping"; } }; struct RC_disk : public RC { RC_disk(unsigned_type b, unsigned_type e) : RC(b, e) { } RC_disk() : RC(config::get_instance()->regular_disk_range().first, config::get_instance()->regular_disk_range().second) { } static const char * name() { return "Randomized cycling striping on regular disks"; } }; struct RC_flash : public RC { RC_flash(unsigned_type b, unsigned_type e) : RC(b, e) { } RC_flash() : RC(config::get_instance()->flash_range().first, config::get_instance()->flash_range().second) { } static const char * name() { return "Randomized cycling striping on flash devices"; } }; //! 'Single disk' disk allocation scheme functor. //! \remarks model of \b allocation_strategy concept struct single_disk { unsigned_type disk; single_disk(unsigned_type d, unsigned_type = 0) : disk(d) { } single_disk() : disk(0) { } unsigned_type operator () (unsigned_type /*i*/) const { return disk; } static const char * name() { return "single disk"; } }; //! Allocator functor adaptor. //! //! Gives offset to disk number sequence defined in constructor template struct offset_allocator { BaseAllocator base; int_type offset; //! Creates functor based on instance of \c BaseAllocator functor //! with offset \c offset_. //! \param offset_ offset //! \param base_ used to create a copy offset_allocator(int_type offset_, const BaseAllocator& base_) : base(base_), offset(offset_) { } //! Creates functor based on instance of \c BaseAllocator functor. //! \param base_ used to create a copy offset_allocator(const BaseAllocator& base_) : base(base_), offset(0) { } //! Creates functor based on default \c BaseAllocator functor. offset_allocator() : offset(0) { } unsigned_type operator () (unsigned_type i) const { return base(offset + i); } int_type get_offset() const { return offset; } void set_offset(int_type i) { offset = i; } }; #ifndef STXXL_DEFAULT_ALLOC_STRATEGY #define STXXL_DEFAULT_ALLOC_STRATEGY stxxl::RC #endif //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BLOCK_ALLOC_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/disk_allocator.h000644 001411 000144 00000015677 12414452316 022211 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/disk_allocator.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2007 Johannes Singler * Copyright (C) 2009, 2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_DISK_ALLOCATOR_HEADER #define STXXL_MNG_DISK_ALLOCATOR_HEADER #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \ingroup mnglayer //! \{ class disk_allocator : private noncopyable { typedef std::pair place; struct first_fit : public std::binary_function { bool operator () ( const place& entry, const stxxl::int64 size) const { return (entry.second >= size); } }; typedef std::map sortseq; stxxl::mutex mutex; sortseq free_space; stxxl::int64 free_bytes; stxxl::int64 disk_bytes; stxxl::int64 cfg_bytes; stxxl::file* storage; bool autogrow; void dump() const; void deallocation_error( stxxl::int64 block_pos, stxxl::int64 block_size, const sortseq::iterator& pred, const sortseq::iterator& succ) const; // expects the mutex to be locked to prevent concurrent access void add_free_region(stxxl::int64 block_pos, stxxl::int64 block_size); // expects the mutex to be locked to prevent concurrent access void grow_file(stxxl::int64 extend_bytes) { if (!extend_bytes) return; storage->set_size(disk_bytes + extend_bytes); add_free_region(disk_bytes, extend_bytes); disk_bytes += extend_bytes; } public: disk_allocator(stxxl::file* storage, const disk_config& cfg) : free_bytes(0), disk_bytes(0), cfg_bytes(cfg.size), storage(storage), autogrow(cfg.autogrow) { // initial growth to configured file size grow_file(cfg.size); } ~disk_allocator() { if (disk_bytes > cfg_bytes) { // reduce to original size storage->set_size(cfg_bytes); } } inline int64 get_free_bytes() const { return free_bytes; } inline int64 get_used_bytes() const { return disk_bytes - free_bytes; } inline int64 get_total_bytes() const { return disk_bytes; } template void new_blocks(BIDArray& bids) { new_blocks(bids.begin(), bids.end()); } template void new_blocks(BID* begin, BID* end); #if 0 template void delete_blocks(const BIDArray& bids) { for (unsigned i = 0; i < bids.size(); ++i) delete_block(bids[i]); } #endif template void delete_block(const BID& bid) { scoped_mutex_lock lock(mutex); STXXL_VERBOSE2("disk_allocator::delete_block<" << BlockSize << ">(pos=" << bid.offset << ", size=" << bid.size << "), free:" << free_bytes << " total:" << disk_bytes); add_free_region(bid.offset, bid.size); } }; template void disk_allocator::new_blocks(BID* begin, BID* end) { stxxl::int64 requested_size = 0; for (typename BIDArray::iterator cur = begin; cur != end; ++cur) { STXXL_VERBOSE2("Asking for a block with size: " << (cur->size)); requested_size += cur->size; } scoped_mutex_lock lock(mutex); STXXL_VERBOSE2("disk_allocator::new_blocks, BlockSize = " << BlockSize << ", free:" << free_bytes << " total:" << disk_bytes << ", blocks: " << (end - begin) << " begin: " << static_cast(begin) << " end: " << static_cast(end) << ", requested_size=" << requested_size); if (free_bytes < requested_size) { if (!autogrow) { STXXL_ERRMSG("External memory block allocation error: " << requested_size << " bytes requested, " << free_bytes << " bytes free. Trying to extend the external memory space..."); } grow_file(requested_size); } // dump(); sortseq::iterator space; space = std::find_if(free_space.begin(), free_space.end(), bind2nd(first_fit(), requested_size) _STXXL_FORCE_SEQUENTIAL); if (space == free_space.end() && requested_size == BlockSize) { assert(end - begin == 1); if (!autogrow) { STXXL_ERRMSG("Warning: Severe external memory space fragmentation!"); dump(); STXXL_ERRMSG("External memory block allocation error: " << requested_size << " bytes requested, " << free_bytes << " bytes free. Trying to extend the external memory space..."); } grow_file(BlockSize); space = std::find_if(free_space.begin(), free_space.end(), bind2nd(first_fit(), requested_size) _STXXL_FORCE_SEQUENTIAL); } if (space != free_space.end()) { stxxl::int64 region_pos = (*space).first; stxxl::int64 region_size = (*space).second; free_space.erase(space); if (region_size > requested_size) free_space[region_pos + requested_size] = region_size - requested_size; for (stxxl::int64 pos = region_pos; begin != end; ++begin) { begin->offset = pos; pos += begin->size; } free_bytes -= requested_size; //dump(); return; } // no contiguous region found STXXL_VERBOSE1("Warning, when allocating an external memory space, no contiguous region found"); STXXL_VERBOSE1("It might harm the performance"); assert(requested_size > BlockSize); assert(end - begin > 1); lock.unlock(); BID* middle = begin + ((end - begin) / 2); new_blocks(begin, middle); new_blocks(middle, end); } //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_DISK_ALLOCATOR_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/config.h000644 001411 000144 00000017430 12405375303 020451 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/config.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2005 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_CONFIG_HEADER #define STXXL_MNG_CONFIG_HEADER #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup mnglayer //! \{ //! Encapsulate the configuration of one "disk". The disk is actually a file //! I/O object which block_manager uses to read/write blocks. class disk_config { public: //! \name Basic Disk Configuration Parameters //! \{ //! the file path used by the io implementation std::string path; //! file size to initially allocate uint64 size; //! io implementation to access file std::string io_impl; //! \} public: //! default constructor disk_config(); //! initializing constructor, also parses fileio parameter disk_config(const std::string& path, uint64 size, const std::string& fileio); //! initializing constructor, parse full line as in config files disk_config(const std::string& line); //! parse a disk=\,\,\ options line into disk_config, //! throws std::runtime_error on parse errors. void parse_line(const std::string& line); //! parse the "io_impl" parameter into the optional parameter fields. void parse_fileio(); //! return formatted fileio name and optional configuration parameters std::string fileio_string() const; public: //! \name Optional Disk / File I/O Implementation Parameters //! \{ //! autogrow file if more disk space is needed, automatically set if size == 0. bool autogrow; //! delete file on program exit (default for autoconfigurated files) bool delete_on_exit; //! tristate variable: direct=0 -> force direct OFF, direct=1 -> try direct //! ON, if fails print warning and open without direct, direct=2 -> force //! direct ON, fail if unavailable. enum direct_type { DIRECT_OFF = 0, DIRECT_TRY = 1, DIRECT_ON = 2 } direct; //! marks flash drives (configuration entries with flash= instead of disk=) bool flash; //! select request queue for disk. Use different queues for files on //! different disks. queue=-1 -> default queue (one for each disk). int queue; //! the selected physical device id (e.g. for calculating prefetching //! sequences). If -1 then the device id is chosen automatically. unsigned int device_id; //! turned on by syscall fileio when the path points to a raw block device bool raw_device; //! unlink file immediately after opening (available on most Unix) bool unlink_on_open; //! desired queue length for linuxaio_file and linuxaio_queue int queue_length; //! \} }; //! Access point to disks properties. Since 1.4.0: no config files are read //! automatically! //! \remarks is a singleton class config : public singleton { friend class singleton; //! typedef of list of configured disks typedef std::vector disk_list_type; //! list of configured disks disk_list_type disks_list; //! In disks_list, flash devices come after all regular disks unsigned first_flash; //! Finished initializing config bool is_initialized; //! Constructor: this must be inlined to print the header version //! string. inline config() : is_initialized(false) { logger::get_instance(); STXXL_MSG(get_version_string_long()); print_library_version_mismatch(); } //! deletes autogrow files ~config(); //! Search several places for a config file. void find_config(); //! If disk list is empty, then search different locations for a disk //! configuration file, or load a default config if everything fails. void initialize(); public: //! \name Initialization Functions //! \{ //! Check that initialize() was called. //! \note This function need not be called by the user, block_manager will //! always call it. void check_initialized() { if (!is_initialized) initialize(); } //! Load disk configuration file. void load_config_file(const std::string& config_path); //! Load default configuration. void load_default_config(); //! Add a disk to the configuration list. //! //! \warning This function should only be used during initialization, as it //! has no effect after construction of block_manager. inline config & add_disk(const disk_config& cfg) { disks_list.push_back(cfg); return *this; } //! \} protected: //! \name Automatic Disk Enumeration Functions //! \{ //! static counter for automatic physical device enumeration unsigned int m_max_device_id; public: //! Returns automatic physical device id counter unsigned int get_max_device_id(); //! Returns next automatic physical device id counter unsigned int get_next_device_id(); //! Update the automatic physical device id counter void update_max_device_id(unsigned int devid); //! \} public: //! \name Query Functions //! \{ //! Returns number of disks available to user. //! \return number of disks inline size_t disks_number() { check_initialized(); return disks_list.size(); } //! Returns contiguous range of regular disks w/o flash devices in the array of all disks. //! \return range [begin, end) of regular disk indices inline std::pair regular_disk_range() const { assert(is_initialized); return std::pair(0, first_flash); } //! Returns contiguous range of flash devices in the array of all disks. //! \return range [begin, end) of flash device indices inline std::pair flash_range() const { assert(is_initialized); return std::pair(first_flash, (unsigned)disks_list.size()); } //! Returns mutable disk_config structure for additional disk parameters inline disk_config & disk(size_t disk) { check_initialized(); return disks_list[disk]; } //! Returns constant disk_config structure for additional disk parameters inline const disk_config & disk(size_t disk) const { assert(is_initialized); return disks_list[disk]; } //! Returns path of disks. //! \param disk disk's identifier //! \return string that contains the disk's path name inline const std::string & disk_path(size_t disk) const { assert(is_initialized); return disks_list[disk].path; } //! Returns disk size. //! \param disk disk's identifier //! \return disk size in bytes inline stxxl::uint64 disk_size(size_t disk) const { assert(is_initialized); return disks_list[disk].size; } //! Returns name of I/O implementation of particular disk. //! \param disk disk's identifier inline const std::string & disk_io_impl(size_t disk) const { assert(is_initialized); return disks_list[disk].io_impl; } //! Returns the total size over all disks uint64 total_size() const; //! \} }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_CONFIG_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/typed_block.h000644 001411 000144 00000026534 12411366426 021513 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/typed_block.h * * Constructs a typed_block object containing as many elements elements plus * some metadata as fits into the given block size. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_TYPED_BLOCK_HEADER #define STXXL_MNG_TYPED_BLOCK_HEADER #include #include #include #include #ifndef STXXL_VERBOSE_TYPED_BLOCK #define STXXL_VERBOSE_TYPED_BLOCK STXXL_VERBOSE2 #endif STXXL_BEGIN_NAMESPACE //! \addtogroup mnglayer //! \{ //! Block Manager Internals \internal namespace mng_local { //! \defgroup mnglayer_internals Internals //! \ingroup mnglayer //! Internals and support classes //! \{ template class filler_struct { typedef unsigned char byte_type; byte_type filler_array[Bytes]; public: filler_struct() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] filler_struct is constructed"); } }; template <> class filler_struct<0> { typedef unsigned char byte_type; public: filler_struct() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] filler_struct<> is constructed"); } }; //! Contains data elements for \c stxxl::typed_block , not intended for direct use. template class element_block { public: typedef Type type; typedef Type value_type; typedef Type& reference; typedef const Type& const_reference; typedef type* pointer; typedef pointer iterator; typedef const type* const_iterator; enum { size = Size //!< number of elements in the block }; //! Array of elements of type Type value_type elem[size]; element_block() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] element_block is constructed"); } //! An operator to access elements in the block reference operator [] (size_t i) { return elem[i]; } //! Returns \c iterator pointing to the first element. iterator begin() { return elem; } //! Returns \c const_iterator pointing to the first element. const_iterator begin() const { return elem; } //! Returns \c const_iterator pointing to the first element. const_iterator cbegin() const { return begin(); } //! Returns \c iterator pointing to the end element. iterator end() { return elem + size; } //! Returns \c const_iterator pointing to the end element. const_iterator end() const { return elem + size; } //! Returns \c const_iterator pointing to the end element. const_iterator cend() const { return end(); } }; //! Contains BID references for \c stxxl::typed_block , not intended for direct use. template class block_w_bids : public element_block { public: enum { raw_size = RawSize, nbids = NBids }; typedef BID bid_type; //! Array of BID references bid_type ref[nbids]; //! An operator to access bid references bid_type& operator () (size_t i) { return ref[i]; } block_w_bids() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_bids is constructed"); } }; template class block_w_bids : public element_block { public: enum { raw_size = RawSize, nbids = 0 }; typedef BID bid_type; block_w_bids() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_bids<> is constructed"); } }; //! Contains per block information for \c stxxl::typed_block , not intended for direct use. template class block_w_info : public block_w_bids)* NBids - sizeof(MetaInfoType)) / sizeof(Type)), RawSize, NBids> { public: //! Type of per block information element. typedef MetaInfoType info_type; //! Per block information element. info_type info; block_w_info() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_info is constructed"); } }; template class block_w_info : public block_w_bids)* NBids) / sizeof(Type)), RawSize, NBids> { public: typedef void info_type; block_w_info() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_info<> is constructed"); } }; //! Contains per block filler for \c stxxl::typed_block , not intended for direct use. template class add_filler : public BaseType { private: //! Per block filler element. filler_struct filler; public: add_filler() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] add_filler is constructed"); } }; template class add_filler : public BaseType { public: add_filler() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] add_filler<> is constructed"); } }; //! Helper to compute the size of the filler , not intended for direct use. template class expand_struct : public add_filler { }; //! \} } // namespace mng_local //! Block containing elements of fixed length. //! //! \tparam RawSize size of block in bytes //! \tparam Type type of block's records //! \tparam NRef number of block references (BIDs) that can be stored in the block (default is 0) //! \tparam MetaInfoType type of per block information (default is no information - void) //! //! The data array of type Type is contained in the parent class \c stxxl::element_block, see related information there. //! The BID array of references is contained in the parent class \c stxxl::block_w_bids, see related information there. //! The "per block information" is contained in the parent class \c stxxl::block_w_info, see related information there. //! \warning If \c RawSize > 2MB object(s) of this type can not be allocated on the stack (as a //! function variable for example), because Linux POSIX library limits the stack size for the //! main thread to (2MB - system page size) template class typed_block : public mng_local::expand_struct, RawSize> { typedef mng_local::expand_struct, RawSize> Base; public: typedef Type value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* pointer; typedef pointer iterator; typedef const value_type* const_pointer; typedef const_pointer const_iterator; enum constants { raw_size = RawSize, //!< size of block in bytes size = Base::size, //!< number of elements in block has_only_data = (raw_size == (size * sizeof(value_type))) //!< no meta info, bids or (non-empty) fillers included in the block, allows value_type array addressing across block boundaries }; typedef BID bid_type; typed_block() { STXXL_STATIC_ASSERT(sizeof(typed_block) == raw_size); STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] typed_block is constructed"); #if 0 assert(((long)this) % STXXL_BLOCK_ALIGN == 0); #endif } #if 0 typed_block(const typed_block& tb) { STXXL_STATIC_ASSERT(sizeof(typed_block) == raw_size); STXXL_MSG("[" << (void*)this << "] typed_block is copy constructed from [" << (void*)&tb << "]"); STXXL_UNUSED(tb); } #endif /*! Writes block to the disk(s). *! \param bid block identifier, points the file(disk) and position *! \param on_cmpl completion handler *! \return \c pointer_ptr object to track status I/O operation after the call */ request_ptr write(const bid_type& bid, completion_handler on_cmpl = completion_handler()) { STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:write " << FMT_BID(bid)); return bid.storage->awrite(this, bid.offset, raw_size, on_cmpl); } /*! Reads block from the disk(s). *! \param bid block identifier, points the file(disk) and position *! \param on_cmpl completion handler *! \return \c pointer_ptr object to track status I/O operation after the call */ request_ptr read(const bid_type& bid, completion_handler on_cmpl = completion_handler()) { STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:read " << FMT_BID(bid)); return bid.storage->aread(this, bid.offset, raw_size, on_cmpl); } static void* operator new (size_t bytes) { unsigned_type meta_info_size = bytes % raw_size; STXXL_VERBOSE_TYPED_BLOCK("typed::block operator new[]: bytes=" << bytes << ", meta_info_size=" << meta_info_size); void* result = aligned_alloc( bytes - meta_info_size, meta_info_size); #if STXXL_WITH_VALGRIND || STXXL_TYPED_BLOCK_INITIALIZE_ZERO memset(result, 0, bytes); #endif return result; } static void* operator new[] (size_t bytes) { unsigned_type meta_info_size = bytes % raw_size; STXXL_VERBOSE_TYPED_BLOCK("typed::block operator new[]: bytes=" << bytes << ", meta_info_size=" << meta_info_size); void* result = aligned_alloc( bytes - meta_info_size, meta_info_size); #if STXXL_WITH_VALGRIND || STXXL_TYPED_BLOCK_INITIALIZE_ZERO memset(result, 0, bytes); #endif return result; } static void* operator new (size_t /*bytes*/, void* ptr) // construct object in existing memory { return ptr; } static void operator delete (void* ptr) { aligned_dealloc(ptr); } static void operator delete[] (void* ptr) { aligned_dealloc(ptr); } static void operator delete (void*, void*) { } #if 1 // STRANGE: implementing destructor makes g++ allocate // additional 4 bytes in the beginning of every array // of this type !? makes aligning to 4K boundaries difficult // // http://www.cc.gatech.edu/grads/j/Seung.Won.Jun/tips/pl/node4.html : // "One interesting thing is the array allocator requires more memory // than the array size multiplied by the size of an element, by a // difference of delta for metadata a compiler needs. It happens to // be 8 bytes long in g++." ~typed_block() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] typed_block is destructed"); } #endif }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_TYPED_BLOCK_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/buf_ostream.h000644 001411 000144 00000007272 12414452316 021515 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/buf_ostream.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BUF_OSTREAM_HEADER #define STXXL_MNG_BUF_OSTREAM_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup schedlayer //! \{ //! Buffered output stream. //! //! Writes data records to the stream of blocks. //! \remark Writing performed in the background, i.e. with overlapping of I/O and computation template class buf_ostream : private noncopyable { public: typedef BlockType block_type; typedef BidIteratorType bid_iterator_type; protected: buffered_writer writer; bid_iterator_type current_bid; int_type current_elem; block_type* current_blk; public: typedef typename block_type::const_reference const_reference; typedef typename block_type::reference reference; typedef buf_ostream self_type; //! Constructs output stream object. //! \param first_bid \c bid_iterator pointing to the first block of the stream //! \param nbuffers number of buffers for internal use buf_ostream(bid_iterator_type first_bid, int_type nbuffers) : writer(nbuffers, nbuffers / 2), current_bid(first_bid), current_elem(0) { current_blk = writer.get_free_block(); } //! Output stream operator, writes out \c record. //! \param record const reference to block record type, containing a value of record to write to the stream //! \return reference to itself (stream object) self_type& operator << (const_reference record) { current_blk->elem[current_elem++] = record; if (UNLIKELY(current_elem >= block_type::size)) { current_elem = 0; current_blk = writer.write(current_blk, *(current_bid++)); } return *this; } //! Returns reference to the current record. //! \return reference to the current record reference current() { return current_blk->elem[current_elem]; } //! Returns reference to the current record. //! \return reference to the current record reference operator * () { return current_blk->elem[current_elem]; } //! Moves to the next record in the stream. //! \return reference to itself after the advance self_type& operator ++ () { ++current_elem; if (UNLIKELY(current_elem >= block_type::size)) { current_elem = 0; current_blk = writer.write(current_blk, *(current_bid++)); } return *this; } //! Fill current block with padding and flush self_type & fill(const_reference record) { while (current_elem != 0) { operator << (record); } return *this; } //! Force flush of current block, for finishing writing within a block. //! \warning Use with caution as the block may contain uninitialized data self_type & flush() { current_elem = 0; current_blk = writer.write(current_blk, *(current_bid++)); return *this; } //! Deallocates internal objects. ~buf_ostream() { assert(current_elem == 0); } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BUF_OSTREAM_HEADER stxxl-1.4.1/include/stxxl/bits/mng/buf_istream.h000644 001411 000144 00000010744 12422212055 021476 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/buf_istream.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BUF_ISTREAM_HEADER #define STXXL_MNG_BUF_ISTREAM_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup schedlayer //! \{ // a paranoid check #define BUF_ISTREAM_CHECK_END //! Buffered input stream. //! //! Reads data records from the stream of blocks. //! \remark Reading performed in the background, i.e. with overlapping of I/O and computation template class buf_istream : private noncopyable { public: typedef BlockType block_type; typedef BidIteratorType bid_iterator_type; private: buf_istream() { } protected: typedef block_prefetcher prefetcher_type; prefetcher_type* prefetcher; int_type current_elem; block_type* current_blk; int_type* prefetch_seq; #ifdef BUF_ISTREAM_CHECK_END bool not_finished; #endif public: typedef typename block_type::reference reference; typedef buf_istream self_type; //! Constructs input stream object. //! \param begin \c bid_iterator pointing to the first block of the stream //! \param end \c bid_iterator pointing to the ( \b last + 1 ) block of the stream //! \param nbuffers number of buffers for internal use buf_istream(bid_iterator_type begin, bid_iterator_type end, unsigned_type nbuffers) : current_elem(0) #ifdef BUF_ISTREAM_CHECK_END , not_finished(true) #endif { const unsigned_type ndisks = config::get_instance()->disks_number(); const unsigned_type mdevid = config::get_instance()->get_max_device_id(); const int_type seq_length = end - begin; prefetch_seq = new int_type[seq_length]; // obvious schedule //for(int_type i = 0; i < seq_length; ++i) // prefetch_seq[i] = i; // optimal schedule nbuffers = STXXL_MAX(2 * ndisks, unsigned_type(nbuffers - 1)); compute_prefetch_schedule(begin, end, prefetch_seq, nbuffers, mdevid); prefetcher = new prefetcher_type(begin, end, prefetch_seq, nbuffers); current_blk = prefetcher->pull_block(); } //! Input stream operator, reads in \c record. //! \param record reference to the block record type, //! contains value of the next record in the stream after the call of the operator //! \return reference to itself (stream object) self_type& operator >> (reference record) { #ifdef BUF_ISTREAM_CHECK_END assert(not_finished); #endif record = current_blk->elem[current_elem++]; if (UNLIKELY(current_elem >= block_type::size)) { current_elem = 0; #ifdef BUF_ISTREAM_CHECK_END not_finished = prefetcher->block_consumed(current_blk); #else prefetcher->block_consumed(current_blk); #endif } return (*this); } //! Returns reference to the current record in the stream. reference current() /* const */ { return current_blk->elem[current_elem]; } //! Returns reference to the current record in the stream. reference operator * () /* const */ { return current_blk->elem[current_elem]; } //! Moves to the next record in the stream. //! \return reference to itself after the advance self_type& operator ++ () { #ifdef BUF_ISTREAM_CHECK_END assert(not_finished); #endif current_elem++; if (UNLIKELY(current_elem >= block_type::size)) { current_elem = 0; #ifdef BUF_ISTREAM_CHECK_END not_finished = prefetcher->block_consumed(current_blk); #else prefetcher->block_consumed(current_blk); #endif } return *this; } //! Frees used internal objects. ~buf_istream() { delete prefetcher; delete[] prefetch_seq; } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BUF_ISTREAM_HEADER stxxl-1.4.1/include/stxxl/bits/mng/block_prefetcher.h000644 001411 000144 00000017405 12414452316 022507 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/block_prefetcher.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2009, 2010 Johannes Singler * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BLOCK_PREFETCHER_HEADER #define STXXL_MNG_BLOCK_PREFETCHER_HEADER #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup schedlayer //! \{ class set_switch_handler { onoff_switch& switch_; completion_handler on_compl; public: set_switch_handler(onoff_switch& _switch, const completion_handler& on_compl) : switch_(_switch), on_compl(on_compl) { } void operator () (request* req) { // call before setting switch to on, otherwise, user has no way to wait // for the completion handler to be executed on_compl(req); switch_.on(); } }; //! Encapsulates asynchronous prefetching engine. //! //! \c block_prefetcher overlaps I/Os with consumption of read data. //! Utilizes optimal asynchronous prefetch scheduling (by Peter Sanders et.al.) template class block_prefetcher : private noncopyable { public: typedef BlockType block_type; typedef BidIteratorType bid_iterator_type; typedef typename block_type::bid_type bid_type; protected: bid_iterator_type consume_seq_begin; bid_iterator_type consume_seq_end; unsigned_type seq_length; int_type* prefetch_seq; unsigned_type nextread; unsigned_type nextconsume; const int_type nreadblocks; block_type* read_buffers; request_ptr* read_reqs; bid_type* read_bids; onoff_switch* completed; int_type* pref_buffer; completion_handler do_after_fetch; block_type * wait(int_type iblock) { STXXL_VERBOSE1("block_prefetcher: waiting block " << iblock); { stats::scoped_wait_timer wait_timer(stats::WAIT_OP_READ); completed[iblock].wait_for_on(); } STXXL_VERBOSE1("block_prefetcher: finished waiting block " << iblock); int_type ibuffer = pref_buffer[iblock]; STXXL_VERBOSE1("block_prefetcher: returning buffer " << ibuffer); assert(ibuffer >= 0 && ibuffer < nreadblocks); return (read_buffers + ibuffer); } public: //! Constructs an object and immediately starts prefetching. //! \param _cons_begin \c bid_iterator pointing to the \c bid of the first block to be consumed //! \param _cons_end \c bid_iterator pointing to the \c bid of the ( \b last + 1 ) block of consumption sequence //! \param _pref_seq gives the prefetch order, is a pointer to the integer array that contains //! the indices of the blocks in the consumption sequence //! \param _prefetch_buf_size amount of prefetch buffers to use //! \param do_after_fetch unknown block_prefetcher( bid_iterator_type _cons_begin, bid_iterator_type _cons_end, int_type* _pref_seq, int_type _prefetch_buf_size, completion_handler do_after_fetch = completion_handler()) : consume_seq_begin(_cons_begin), consume_seq_end(_cons_end), seq_length(_cons_end - _cons_begin), prefetch_seq(_pref_seq), nextread(STXXL_MIN(unsigned_type(_prefetch_buf_size), seq_length)), nextconsume(0), nreadblocks(nextread), do_after_fetch(do_after_fetch) { STXXL_VERBOSE1("block_prefetcher: seq_length=" << seq_length); STXXL_VERBOSE1("block_prefetcher: _prefetch_buf_size=" << _prefetch_buf_size); assert(seq_length > 0); assert(_prefetch_buf_size > 0); int_type i; read_buffers = new block_type[nreadblocks]; read_reqs = new request_ptr[nreadblocks]; read_bids = new bid_type[nreadblocks]; pref_buffer = new int_type[seq_length]; std::fill(pref_buffer, pref_buffer + seq_length, -1); completed = new onoff_switch[seq_length]; for (i = 0; i < nreadblocks; ++i) { assert(prefetch_seq[i] < int_type(seq_length)); assert(prefetch_seq[i] >= 0); read_bids[i] = *(consume_seq_begin + prefetch_seq[i]); STXXL_VERBOSE1("block_prefetcher: reading block " << i << " prefetch_seq[" << i << "]=" << prefetch_seq[i] << " @ " << &read_buffers[i] << " @ " << read_bids[i]); read_reqs[i] = read_buffers[i].read( read_bids[i], set_switch_handler(*(completed + prefetch_seq[i]), do_after_fetch)); pref_buffer[prefetch_seq[i]] = i; } } //! Pulls next unconsumed block from the consumption sequence. //! \return Pointer to the already prefetched block from the internal buffer pool block_type * pull_block() { STXXL_VERBOSE1("block_prefetcher: pulling a block"); return wait(nextconsume++); } //! Exchanges buffers between prefetcher and application. //! \param buffer pointer to the consumed buffer. After call if return value is true \c buffer //! contains valid pointer to the next unconsumed prefetched buffer. //! \remark parameter \c buffer must be value returned by \c pull_block() or \c block_consumed() methods //! \return \c false if there are no blocks to prefetch left, \c true if consumption sequence is not emptied bool block_consumed(block_type*& buffer) { int_type ibuffer = buffer - read_buffers; STXXL_VERBOSE1("block_prefetcher: buffer " << ibuffer << " consumed"); if (read_reqs[ibuffer].valid()) read_reqs[ibuffer]->wait(); read_reqs[ibuffer] = NULL; if (nextread < seq_length) { assert(ibuffer >= 0 && ibuffer < nreadblocks); int_type next_2_prefetch = prefetch_seq[nextread++]; STXXL_VERBOSE1("block_prefetcher: prefetching block " << next_2_prefetch); assert((next_2_prefetch < int_type(seq_length)) && (next_2_prefetch >= 0)); assert(!completed[next_2_prefetch].is_on()); pref_buffer[next_2_prefetch] = ibuffer; read_bids[ibuffer] = *(consume_seq_begin + next_2_prefetch); read_reqs[ibuffer] = read_buffers[ibuffer].read( read_bids[ibuffer], set_switch_handler(*(completed + next_2_prefetch), do_after_fetch) ); } if (nextconsume >= seq_length) return false; buffer = wait(nextconsume++); return true; } //! No more consumable blocks available, but can't delete the prefetcher, //! because not all blocks may have been returned, yet. bool empty() const { return nextconsume >= seq_length; } //! Index of the next element in the consume sequence. unsigned_type pos() const { return nextconsume; } //! Frees used memory. ~block_prefetcher() { for (int_type i = 0; i < nreadblocks; ++i) if (read_reqs[i].valid()) read_reqs[i]->wait(); delete[] read_reqs; delete[] read_bids; delete[] completed; delete[] pref_buffer; delete[] read_buffers; } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BLOCK_PREFETCHER_HEADER stxxl-1.4.1/include/stxxl/bits/mng/prefetch_pool.h000644 001411 000144 00000023704 12405375303 022036 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/prefetch_pool.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003-2004 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_PREFETCH_POOL_HEADER #define STXXL_MNG_PREFETCH_POOL_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup schedlayer //! \{ //! Implements dynamically resizable prefetching pool. template class prefetch_pool : private noncopyable { public: typedef BlockType block_type; typedef typename block_type::bid_type bid_type; protected: struct bid_hash { size_t operator () (const bid_type& bid) const { size_t result = size_t(bid.storage) + size_t(bid.offset & 0xffffffff) + size_t(bid.offset >> 32); return result; } #if STXXL_MSVC bool operator () (const bid_type& a, const bid_type& b) const { return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset); } enum { // parameters for hash table bucket_size = 4, // 0 < bucket_size min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N }; #endif }; typedef std::pair busy_entry; typedef typename compat_hash_map::result hash_map_type; typedef typename std::list::iterator free_blocks_iterator; typedef typename hash_map_type::iterator busy_blocks_iterator; // contains free prefetch blocks std::list free_blocks; // blocks that are in reading or already read but not retrieved by user hash_map_type busy_blocks; unsigned_type free_blocks_size; public: //! Constructs pool. //! \param init_size initial number of blocks in the pool explicit prefetch_pool(unsigned_type init_size = 1) : free_blocks_size(init_size) { unsigned_type i = 0; for ( ; i < init_size; ++i) free_blocks.push_back(new block_type); } void swap(prefetch_pool& obj) { std::swap(free_blocks, obj.free_blocks); std::swap(busy_blocks, obj.busy_blocks); std::swap(free_blocks_size, obj.free_blocks_size); } //! Waits for completion of all ongoing read requests and frees memory. virtual ~prefetch_pool() { while (!free_blocks.empty()) { delete free_blocks.back(); free_blocks.pop_back(); } try { busy_blocks_iterator i2 = busy_blocks.begin(); for ( ; i2 != busy_blocks.end(); ++i2) { i2->second.second->wait(); delete i2->second.first; } } catch (...) { } } //! Returns number of owned blocks. unsigned_type size() const { return free_blocks_size + busy_blocks.size(); } //! Gives a hint for prefetching a block. //! \param bid address of a block to be prefetched //! \return \c true if there was a free block to do prefetch and prefetching //! was scheduled, \c false otherwise //! \note If there are no free blocks available (all blocks //! are already in reading or read but not retrieved by user calling \c read //! method) calling \c hint function has no effect bool hint(bid_type bid) { // if block is already hinted, no need to hint it again if (in_prefetching(bid)) { STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " was already cached"); return true; } if (free_blocks_size) // only if we have a free block { --free_blocks_size; block_type* block = free_blocks.back(); free_blocks.pop_back(); STXXL_VERBOSE2("prefetch_pool::hint bid=" << bid << " => prefetching"); request_ptr req = block->read(bid); busy_blocks[bid] = busy_entry(block, req); return true; } STXXL_VERBOSE2("prefetch_pool::hint bid=" << bid << " => no free blocks for prefetching"); return false; } bool hint(bid_type bid, write_pool& w_pool) { // if block is already hinted, no need to hint it again if (in_prefetching(bid)) { STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " was already cached"); return true; } if (free_blocks_size) // only if we have a free block { --free_blocks_size; block_type* block = free_blocks.back(); free_blocks.pop_back(); if (w_pool.has_request(bid)) { busy_entry wp_request = w_pool.steal_request(bid); STXXL_VERBOSE1("prefetch_pool::hint2 bid=" << bid << " was in write cache at " << wp_request.first); assert(wp_request.first != 0); w_pool.add(block); //in exchange busy_blocks[bid] = wp_request; return true; } STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " => prefetching"); request_ptr req = block->read(bid); busy_blocks[bid] = busy_entry(block, req); return true; } STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " => no free blocks for prefetching"); return false; } bool invalidate(bid_type bid) { busy_blocks_iterator cache_el = busy_blocks.find(bid); if (cache_el == busy_blocks.end()) return false; // cancel request if it is a read request, there might be // write requests 'stolen' from a write_pool that may not be canceled if (cache_el->second.second->get_type() == request::READ) cache_el->second.second->cancel(); // finish the request cache_el->second.second->wait(); ++free_blocks_size; free_blocks.push_back(cache_el->second.first); busy_blocks.erase(cache_el); return true; } bool in_prefetching(bid_type bid) { return (busy_blocks.find(bid) != busy_blocks.end()); } //! Reads block. If this block is cached block is not read but passed from the cache. //! \param block block object, where data to be read to. If block was cached \c block 's //! ownership goes to the pool and block from cache is returned in \c block value. //! \param bid address of the block //! \warning \c block parameter must be allocated dynamically using \c new . //! \return request pointer object of read operation request_ptr read(block_type*& block, bid_type bid) { busy_blocks_iterator cache_el = busy_blocks.find(bid); if (cache_el == busy_blocks.end()) { // not cached STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => no copy in cache, retrieving to " << block); return block->read(bid); } // cached STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => copy in cache exists"); ++free_blocks_size; free_blocks.push_back(block); block = cache_el->second.first; request_ptr result = cache_el->second.second; busy_blocks.erase(cache_el); return result; } request_ptr read(block_type*& block, bid_type bid, write_pool& w_pool) { // try cache busy_blocks_iterator cache_el = busy_blocks.find(bid); if (cache_el != busy_blocks.end()) { // cached STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => copy in cache exists"); ++free_blocks_size; free_blocks.push_back(block); block = cache_el->second.first; request_ptr result = cache_el->second.second; busy_blocks.erase(cache_el); return result; } // try w_pool cache if (w_pool.has_request(bid)) { busy_entry wp_request = w_pool.steal_request(bid); STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " was in write cache at " << wp_request.first); assert(wp_request.first != 0); w_pool.add(block); //in exchange block = wp_request.first; return wp_request.second; } // not cached STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => no copy in cache, retrieving to " << block); return block->read(bid); } //! Resizes size of the pool. //! \param new_size desired size of the pool. If some //! blocks are used for prefetching, these blocks can't be freed. //! Only free blocks (not in prefetching) can be freed by reducing //! the size of the pool calling this method. //! \return new size of the pool unsigned_type resize(unsigned_type new_size) { int_type diff = int_type(new_size) - int_type(size()); if (diff > 0) { free_blocks_size += diff; while (--diff >= 0) free_blocks.push_back(new block_type); return size(); } while (diff < 0 && free_blocks_size > 0) { ++diff; --free_blocks_size; delete free_blocks.back(); free_blocks.pop_back(); } return size(); } }; //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::prefetch_pool& a, stxxl::prefetch_pool& b) { a.swap(b); } } // namespace std #endif // !STXXL_MNG_PREFETCH_POOL_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/buf_istream_reverse.h000644 001411 000144 00000012230 12414452316 023230 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/buf_istream_reverse.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BUF_ISTREAM_REVERSE_HEADER #define STXXL_MNG_BUF_ISTREAM_REVERSE_HEADER #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup schedlayer //! \{ // a paranoid check #define BUF_ISTREAM_CHECK_END //! Buffered input stream, reading the items in the blocks in reverse order. //! //! Reads data records from the stream of blocks in reverse order. //! \remark Reading performed in the background, i.e. with overlapping of I/O and computation template class buf_istream_reverse : private noncopyable { public: typedef BlockType block_type; typedef BidIteratorType bid_iterator_type; //-tb note that we redefine the BID type here, because there is no way to //-derive it from BidIteratorType (which is usually just a POD pointer). typedef BIDArray bid_vector_type; private: buf_istream_reverse() { } protected: typedef block_prefetcher prefetcher_type; prefetcher_type* prefetcher; int_type current_elem; block_type* current_blk; int_type* prefetch_seq; #ifdef BUF_ISTREAM_CHECK_END bool not_finished; #endif bid_vector_type bids_; public: typedef typename block_type::reference reference; typedef buf_istream_reverse self_type; //! Constructs input stream object, reading [first,last) blocks in reverse. //! \param begin \c bid_iterator pointing to the first block of the stream //! \param end \c bid_iterator pointing to the ( \b last + 1 ) block of the stream //! \param nbuffers number of buffers for internal use buf_istream_reverse(bid_iterator_type begin, bid_iterator_type end, int_type nbuffers) : current_elem(0), #ifdef BUF_ISTREAM_CHECK_END not_finished(true), #endif bids_(end - begin) { // copy list of bids in reverse std::reverse_copy(begin, end, bids_.begin()); // calculate prefetch sequence const unsigned_type ndisks = config::get_instance()->disks_number(); const unsigned_type mdevid = config::get_instance()->get_max_device_id(); prefetch_seq = new int_type[bids_.size()]; // optimal schedule nbuffers = STXXL_MAX(2 * ndisks, unsigned_type(nbuffers - 1)); compute_prefetch_schedule(bids_.begin(), bids_.end(), prefetch_seq, nbuffers, mdevid); // create stream prefetcher prefetcher = new prefetcher_type(bids_.begin(), bids_.end(), prefetch_seq, nbuffers); // fetch block: last in sequence current_blk = prefetcher->pull_block(); current_elem = block_type::size - 1; } //! Input stream operator, reads in \c record. //! \param record reference to the block record type, //! contains value of the next record in the stream after the call of the operator //! \return reference to itself (stream object) self_type& operator >> (reference record) { #ifdef BUF_ISTREAM_CHECK_END assert(not_finished); #endif record = current_blk->elem[current_elem--]; if (UNLIKELY(current_elem < 0)) { current_elem = block_type::size - 1; #ifdef BUF_ISTREAM_CHECK_END not_finished = prefetcher->block_consumed(current_blk); #else prefetcher->block_consumed(current_blk); #endif } return (*this); } //! Returns reference to the current record in the stream. reference current() /* const */ { return current_blk->elem[current_elem]; } //! Returns reference to the current record in the stream. reference operator * () /* const */ { return current_blk->elem[current_elem]; } //! Moves to the _previous_ record in the stream. //! \return reference to itself after the advance self_type& operator ++ () { #ifdef BUF_ISTREAM_CHECK_END assert(not_finished); #endif current_elem--; if (UNLIKELY(current_elem < 0)) { current_elem = block_type::size - 1; #ifdef BUF_ISTREAM_CHECK_END not_finished = prefetcher->block_consumed(current_blk); #else prefetcher->block_consumed(current_blk); #endif } return *this; } //! Frees used internal objects. ~buf_istream_reverse() { delete prefetcher; delete[] prefetch_seq; } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BUF_ISTREAM_REVERSE_HEADER stxxl-1.4.1/include/stxxl/bits/mng/bid.h000644 001411 000144 00000010305 12414452316 017734 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/bid.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BID_HEADER #define STXXL_MNG_BID_HEADER #include #include #include #include #include #include #ifndef STXXL_VERBOSE_BLOCK_LIFE_CYCLE #define STXXL_VERBOSE_BLOCK_LIFE_CYCLE STXXL_VERBOSE2 #endif #define FMT_BID(_bid_) "[" << (_bid_).storage->get_allocator_id() << "]0x" << std::hex << std::setfill('0') << std::setw(8) << (_bid_).offset << "/0x" << std::setw(8) << (_bid_).size STXXL_BEGIN_NAMESPACE //! \addtogroup mnglayer //! \{ //! Block identifier class. //! //! Stores block identity, given by file and offset within the file template struct BID { enum { size = Size, //!< Block size t_size = Size //!< Blocks size, given by the parameter }; file* storage; //!< pointer to the file of the block stxxl::int64 offset; //!< offset within the file of the block BID() : storage(NULL), offset(0) { } bool valid() const { return storage != NULL; } BID(file* s, stxxl::int64 o) : storage(s), offset(o) { } BID(const BID& obj) : storage(obj.storage), offset(obj.offset) { } template explicit BID(const BID& obj) : storage(obj.storage), offset(obj.offset) { } template BID& operator = (const BID& obj) { storage = obj.storage; offset = obj.offset; return *this; } bool is_managed() const { return storage->get_allocator_id() != file::NO_ALLOCATOR; } }; //! Specialization of block identifier class (BID) for variable size block size. //! //! Stores block identity, given by file, offset within the file, and size of the block template <> struct BID<0> { file* storage; //!< pointer to the file of the block stxxl::int64 offset; //!< offset within the file of the block unsigned size; //!< size of the block in bytes enum { t_size = 0 //!< Blocks size, given by the parameter }; BID() : storage(NULL), offset(0), size(0) { } BID(file* f, stxxl::int64 o, unsigned s) : storage(f), offset(o), size(s) { } bool valid() const { return (storage != NULL); } }; template bool operator == (const BID& a, const BID& b) { return (a.storage == b.storage) && (a.offset == b.offset) && (a.size == b.size); } template bool operator != (const BID& a, const BID& b) { return (a.storage != b.storage) || (a.offset != b.offset) || (a.size != b.size); } template std::ostream& operator << (std::ostream& s, const BID& bid) { // [0x12345678|0]0x00100000/0x00010000 // [file ptr|file id]offset/size std::ios state(NULL); state.copyfmt(s); s << "[" << bid.storage << "|"; if (bid.storage) s << bid.storage->get_allocator_id(); else s << "?"; s << "]0x" << std::hex << std::setfill('0') << std::setw(8) << bid.offset << "/0x" << std::setw(8) << bid.size << std::dec; s.copyfmt(state); return s; } template class BIDArray : public simple_vector > { public: BIDArray() : simple_vector >() { } BIDArray(unsigned_type size) : simple_vector >(size) { } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BID_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/block_manager.h000644 001411 000144 00000021275 12414452316 021772 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/block_manager.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2007 Roman Dementiev * Copyright (C) 2007, 2009 Johannes Singler * Copyright (C) 2008-2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BLOCK_MANAGER_HEADER #define STXXL_MNG_BLOCK_MANAGER_HEADER #include #include #include #include #include #include #include #include #include #include #if STXXL_MSVC #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE #ifndef STXXL_MNG_COUNT_ALLOCATION #define STXXL_MNG_COUNT_ALLOCATION 1 #endif // STXXL_MNG_COUNT_ALLOCATION //! \addtogroup mnglayer //! \{ //! Block manager class. //! //! Manages allocation and deallocation of blocks in multiple/single disk setting //! \remarks is a singleton class block_manager : public singleton { friend class singleton; disk_allocator** disk_allocators; file** disk_files; size_t ndisks; block_manager(); #if STXXL_MNG_COUNT_ALLOCATION //! total requested allocation in bytes uint64 m_total_allocation; //! currently allocated bytes uint64 m_current_allocation; //! maximum number of bytes allocated during program run. uint64 m_maximum_allocation; #endif // STXXL_MNG_COUNT_ALLOCATION protected: template void new_blocks_int( const unsigned_type nblocks, const DiskAssignFunctor& functor, unsigned_type offset, BIDIteratorClass out); public: //! return total number of bytes available in all disks uint64 get_total_bytes() const; //! Return total number of free disk allocations uint64 get_free_bytes() const; //! Allocates new blocks. //! //! Allocates new blocks according to the strategy //! given by \b functor and stores block identifiers //! to the range [ \b bidbegin, \b bidend) //! Allocation will be lined up with previous partial allocations //! of \b offset blocks. //! \param functor object of model of \b allocation_strategy concept //! \param bidbegin bidirectional BID iterator object //! \param bidend bidirectional BID iterator object //! \param offset advance for \b functor to line up partial allocations template void new_blocks( const DiskAssignFunctor& functor, BIDIteratorClass bidbegin, BIDIteratorClass bidend, unsigned_type offset = 0) { typedef typename std::iterator_traits::value_type bid_type; new_blocks_int(std::distance(bidbegin, bidend), functor, offset, bidbegin); } //! Allocates new blocks according to the strategy //! given by \b functor and stores block identifiers //! to the output iterator \b out //! Allocation will be lined up with previous partial allocations //! of \b offset blocks. //! \param nblocks the number of blocks to allocate //! \param functor object of model of \b allocation_strategy concept //! \param out iterator object of OutputIterator concept //! \param offset advance for \b functor to line up partial allocations //! //! The \c BlockType template parameter defines the type of block to allocate template void new_blocks( const unsigned_type nblocks, const DiskAssignFunctor& functor, BIDIteratorClass out, unsigned_type offset = 0) { typedef typename BlockType::bid_type bid_type; new_blocks_int(nblocks, functor, offset, out); } //! Allocates a new block according to the strategy //! given by \b functor and stores the block identifier //! to bid. //! Allocation will be lined up with previous partial allocations //! of \b offset blocks. //! \param functor object of model of \b allocation_strategy concept //! \param bid BID to store the block identifier //! \param offset advance for \b functor to line up partial allocations template void new_block(const DiskAssignFunctor& functor, BID& bid, unsigned_type offset = 0) { new_blocks_int >(1, functor, offset, &bid); } //! Deallocates blocks. //! //! Deallocates blocks in the range [ \b bidbegin, \b bidend) //! \param bidbegin iterator object of \b bid_iterator concept //! \param bidend iterator object of \b bid_iterator concept template void delete_blocks(const BIDIteratorClass& bidbegin, const BIDIteratorClass& bidend); //! Deallocates a block. //! \param bid block identifier template void delete_block(const BID& bid); ~block_manager(); #if STXXL_MNG_COUNT_ALLOCATION //! return total requested allocation in bytes uint64 get_total_allocation() const { return m_total_allocation; } //! return currently allocated bytes uint64 get_current_allocation() const { return m_current_allocation; } //! return maximum number of bytes allocated during program run. uint64 get_maximum_allocation() const { return m_maximum_allocation; } #endif // STXXL_MNG_COUNT_ALLOCATION }; template void block_manager::new_blocks_int( const unsigned_type nblocks, const DiskAssignFunctor& functor, unsigned_type offset, OutputIterator out) { typedef BIDType bid_type; typedef BIDArray bid_array_type; simple_vector bl(ndisks); simple_vector disk_bids(ndisks); simple_vector disk_ptrs(nblocks); bl.memzero(); for (unsigned_type i = 0; i < nblocks; ++i) { unsigned_type disk = functor(offset + i); disk_ptrs[i] = disk_files[disk]; bl[disk]++; } for (unsigned_type i = 0; i < ndisks; ++i) { if (bl[i]) { disk_bids[i].resize(bl[i]); disk_allocators[i]->new_blocks(disk_bids[i]); } } bl.memzero(); OutputIterator it = out; for (unsigned_type i = 0; i != nblocks; ++it, ++i) { const int disk = disk_ptrs[i]->get_allocator_id(); bid_type bid(disk_ptrs[i], disk_bids[disk][bl[disk]++].offset); *it = bid; STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:new " << FMT_BID(bid)); } #if STXXL_MNG_COUNT_ALLOCATION m_total_allocation += nblocks * BIDType::size; m_current_allocation += nblocks * BIDType::size; m_maximum_allocation = STXXL_MAX(m_maximum_allocation, m_current_allocation); #endif // STXXL_MNG_COUNT_ALLOCATION } template void block_manager::delete_block(const BID& bid) { // do not uncomment it //assert(bid.storage->get_allocator_id() < config::get_instance()->disks_number()); if (!bid.is_managed()) return; // self managed disk STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:delete " << FMT_BID(bid)); assert(bid.storage->get_allocator_id() >= 0); disk_allocators[bid.storage->get_allocator_id()]->delete_block(bid); disk_files[bid.storage->get_allocator_id()]->discard(bid.offset, bid.size); #if STXXL_MNG_COUNT_ALLOCATION m_current_allocation -= BlockSize; #endif // STXXL_MNG_COUNT_ALLOCATION } template void block_manager::delete_blocks( const BIDIteratorClass& bidbegin, const BIDIteratorClass& bidend) { for (BIDIteratorClass it = bidbegin; it != bidend; it++) { delete_block(*it); } } // in bytes #ifndef STXXL_DEFAULT_BLOCK_SIZE #define STXXL_DEFAULT_BLOCK_SIZE(type) (2 * 1024 * 1024) // use traits #endif //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BLOCK_MANAGER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/buf_writer.h000644 001411 000144 00000016353 12414452316 021357 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/buf_writer.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BUF_WRITER_HEADER #define STXXL_MNG_BUF_WRITER_HEADER #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \defgroup schedlayer Block Scheduling Sublayer //! \ingroup mnglayer //! Group of classes which help in scheduling //! sequences of read and write requests //! via prefetching and buffered writing //! \{ //! Encapsulates asynchronous buffered block writing engine. //! //! \c buffered_writer overlaps I/Os with filling of output buffer. template class buffered_writer : private noncopyable { typedef BlockType block_type; typedef typename block_type::bid_type bid_type; protected: const unsigned_type nwriteblocks; block_type* write_buffers; bid_type* write_bids; request_ptr* write_reqs; const unsigned_type writebatchsize; std::vector free_write_blocks; // contains free write blocks std::vector busy_write_blocks; // blocks that are in writing, notice that if block is not in free_ // an not in busy then block is not yet filled struct batch_entry { stxxl::int64 offset; int_type ibuffer; batch_entry(stxxl::int64 o, int_type b) : offset(o), ibuffer(b) { } }; struct batch_entry_cmp { bool operator () (const batch_entry& a, const batch_entry& b) const { return (a.offset > b.offset); } }; typedef std::priority_queue, batch_entry_cmp> batch_type; batch_type batch_write_blocks; // sorted sequence of blocks to write public: //! Constructs an object. //! \param write_buf_size number of write buffers to use //! \param write_batch_size number of blocks to accumulate in //! order to flush write requests (bulk buffered writing) buffered_writer(unsigned_type write_buf_size, unsigned_type write_batch_size) : nwriteblocks((write_buf_size > 2) ? write_buf_size : 2), writebatchsize(write_batch_size ? write_batch_size : 1) { write_buffers = new block_type[nwriteblocks]; write_reqs = new request_ptr[nwriteblocks]; write_bids = new bid_type[nwriteblocks]; for (unsigned_type i = 0; i < nwriteblocks; i++) free_write_blocks.push_back(i); disk_queues::get_instance()->set_priority_op(request_queue::WRITE); } //! Returns free block from the internal buffer pool. //! \return pointer to the block from the internal buffer pool block_type * get_free_block() { int_type ibuffer; for (std::vector::iterator it = busy_write_blocks.begin(); it != busy_write_blocks.end(); ++it) { if (write_reqs[ibuffer = (*it)]->poll()) { busy_write_blocks.erase(it); free_write_blocks.push_back(ibuffer); break; } } if (UNLIKELY(free_write_blocks.empty())) { int_type size = busy_write_blocks.size(); request_ptr* reqs = new request_ptr[size]; int_type i = 0; for ( ; i < size; ++i) { reqs[i] = write_reqs[busy_write_blocks[i]]; } int_type completed = wait_any(reqs, size); int_type completed_global = busy_write_blocks[completed]; delete[] reqs; busy_write_blocks.erase(busy_write_blocks.begin() + completed); return (write_buffers + completed_global); } ibuffer = free_write_blocks.back(); free_write_blocks.pop_back(); return (write_buffers + ibuffer); } //! Submits block for writing. //! \param filled_block pointer to the block //! \remark parameter \c filled_block must be value returned by \c get_free_block() or \c write() methods //! \param bid block identifier, a place to write data of the \c filled_block //! \return pointer to the new free block from the pool block_type * write(block_type* filled_block, const bid_type& bid) // writes filled_block and returns a new block { if (batch_write_blocks.size() >= writebatchsize) { // flush batch while (!batch_write_blocks.empty()) { int_type ibuffer = batch_write_blocks.top().ibuffer; batch_write_blocks.pop(); if (write_reqs[ibuffer].valid()) write_reqs[ibuffer]->wait(); write_reqs[ibuffer] = write_buffers[ibuffer].write(write_bids[ibuffer]); busy_write_blocks.push_back(ibuffer); } } // STXXL_MSG("Adding write request to batch"); int_type ibuffer = filled_block - write_buffers; write_bids[ibuffer] = bid; batch_write_blocks.push(batch_entry(bid.offset, ibuffer)); return get_free_block(); } //! Flushes not yet written buffers. void flush() { int_type ibuffer; while (!batch_write_blocks.empty()) { ibuffer = batch_write_blocks.top().ibuffer; batch_write_blocks.pop(); if (write_reqs[ibuffer].valid()) write_reqs[ibuffer]->wait(); write_reqs[ibuffer] = write_buffers[ibuffer].write(write_bids[ibuffer]); busy_write_blocks.push_back(ibuffer); } for (std::vector::const_iterator it = busy_write_blocks.begin(); it != busy_write_blocks.end(); it++) { ibuffer = *it; write_reqs[ibuffer]->wait(); } assert(batch_write_blocks.empty()); free_write_blocks.clear(); busy_write_blocks.clear(); for (unsigned_type i = 0; i < nwriteblocks; i++) free_write_blocks.push_back(i); } //! Flushes not yet written buffers and frees used memory. ~buffered_writer() { int_type ibuffer; while (!batch_write_blocks.empty()) { ibuffer = batch_write_blocks.top().ibuffer; batch_write_blocks.pop(); if (write_reqs[ibuffer].valid()) write_reqs[ibuffer]->wait(); write_reqs[ibuffer] = write_buffers[ibuffer].write(write_bids[ibuffer]); busy_write_blocks.push_back(ibuffer); } for (std::vector::const_iterator it = busy_write_blocks.begin(); it != busy_write_blocks.end(); it++) { ibuffer = *it; write_reqs[ibuffer]->wait(); } delete[] write_reqs; delete[] write_buffers; delete[] write_bids; } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_MNG_BUF_WRITER_HEADER stxxl-1.4.1/include/stxxl/bits/mng/block_alloc_interleaved.h000644 001411 000144 00000010167 12405375303 024032 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/block_alloc_interleaved.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2003 Roman Dementiev * Copyright (C) 2007-2009, 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BLOCK_ALLOC_INTERLEAVED_HEADER #define STXXL_MNG_BLOCK_ALLOC_INTERLEAVED_HEADER #include #include #include STXXL_BEGIN_NAMESPACE #define CHECK_RUN_BOUNDS(pos) struct interleaved_striping { protected: int_type nruns; unsigned_type begindisk; unsigned_type diff; interleaved_striping(int_type nruns, unsigned_type begindisk, unsigned_type diff) : nruns(nruns), begindisk(begindisk), diff(diff) { } public: interleaved_striping(int_type _nruns, const striping& strategy) : nruns(_nruns), begindisk(strategy.begin), diff(strategy.diff) { } unsigned_type operator () (unsigned_type i) const { return begindisk + (i / nruns) % diff; } }; struct interleaved_FR : public interleaved_striping { typedef random_number rnd_type; rnd_type rnd; interleaved_FR(int_type _nruns, const FR& strategy) : interleaved_striping(_nruns, strategy.begin, strategy.diff) { } unsigned_type operator () (unsigned_type /*i*/) const { return begindisk + rnd(rnd_type::value_type(diff)); } }; struct interleaved_SR : public interleaved_striping { typedef random_number rnd_type; std::vector offsets; interleaved_SR(int_type _nruns, const SR& strategy) : interleaved_striping(_nruns, strategy.begin, strategy.diff) { rnd_type rnd; for (int_type i = 0; i < nruns; i++) offsets.push_back(rnd(rnd_type::value_type(diff))); } unsigned_type operator () (unsigned_type i) const { return begindisk + (i / nruns + offsets[i % nruns]) % diff; } }; struct interleaved_RC : public interleaved_striping { std::vector > perms; interleaved_RC(int_type _nruns, const RC& strategy) : interleaved_striping(_nruns, strategy.begin, strategy.diff), perms(nruns, std::vector(diff)) { for (int_type i = 0; i < nruns; i++) { for (unsigned_type j = 0; j < diff; j++) perms[i][j] = j; random_number rnd; std::random_shuffle(perms[i].begin(), perms[i].end(), rnd _STXXL_FORCE_SEQUENTIAL); } } unsigned_type operator () (unsigned_type i) const { return begindisk + perms[i % nruns][(i / nruns) % diff]; } }; struct first_disk_only : public interleaved_striping { first_disk_only(int_type _nruns, const single_disk& strategy) : interleaved_striping(_nruns, strategy.disk, 1) { } unsigned_type operator () (unsigned_type) const { return begindisk; } }; template struct interleaved_alloc_traits { }; template <> struct interleaved_alloc_traits { typedef interleaved_striping strategy; }; template <> struct interleaved_alloc_traits { typedef interleaved_FR strategy; }; template <> struct interleaved_alloc_traits { typedef interleaved_SR strategy; }; template <> struct interleaved_alloc_traits { typedef interleaved_RC strategy; }; template <> struct interleaved_alloc_traits { // FIXME! HACK! typedef interleaved_RC strategy; }; template <> struct interleaved_alloc_traits { // FIXME! HACK! typedef interleaved_RC strategy; }; template <> struct interleaved_alloc_traits { typedef first_disk_only strategy; }; STXXL_END_NAMESPACE #endif // !STXXL_MNG_BLOCK_ALLOC_INTERLEAVED_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/read_write_pool.h000644 001411 000144 00000012267 12414452316 022365 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/read_write_pool.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_READ_WRITE_POOL_HEADER #define STXXL_MNG_READ_WRITE_POOL_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup schedlayer //! \{ //! Implements dynamically resizable buffered writing and prefetched reading pool. template class read_write_pool : private noncopyable { public: typedef BlockType block_type; typedef typename block_type::bid_type bid_type; typedef unsigned_type size_type; protected: typedef write_pool write_pool_type; typedef prefetch_pool prefetch_pool_type; write_pool_type* w_pool; prefetch_pool_type* p_pool; bool delete_pools; public: //! Constructs pool. //! \param init_size_prefetch initial number of blocks in the prefetch pool //! \param init_size_write initial number of blocks in the write pool explicit read_write_pool(size_type init_size_prefetch = 1, size_type init_size_write = 1) : delete_pools(true) { w_pool = new write_pool_type(init_size_write); p_pool = new prefetch_pool_type(init_size_prefetch); } STXXL_DEPRECATED(read_write_pool(prefetch_pool_type& p_pool, write_pool_type& w_pool)) : w_pool(&w_pool), p_pool(&p_pool), delete_pools(false) { } void swap(read_write_pool& obj) { std::swap(w_pool, obj.w_pool); std::swap(p_pool, obj.p_pool); std::swap(delete_pools, obj.delete_pools); } //! Waits for completion of all ongoing requests and frees memory. ~read_write_pool() { if (delete_pools) { delete w_pool; delete p_pool; } } //! Returns number of blocks owned by the write_pool. size_type size_write() const { return w_pool->size(); } //! Returns number of blocks owned by the prefetch_pool. size_type size_prefetch() const { return p_pool->size(); } //! Resizes size of the pool. //! \param new_size new size of the pool after the call void resize_write(size_type new_size) { w_pool->resize(new_size); } //! Resizes size of the pool. //! \param new_size new size of the pool after the call void resize_prefetch(size_type new_size) { p_pool->resize(new_size); } // WRITE POOL METHODS //! Passes a block to the pool for writing. //! \param block block to write. Ownership of the block goes to the pool. //! \c block must be allocated dynamically with using \c new . //! \param bid location, where to write //! \warning \c block must be allocated dynamically with using \c new . //! \return request object of the write operation request_ptr write(block_type*& block, bid_type bid) { request_ptr result = w_pool->write(block, bid); // if there is a copy of this block in the prefetch pool, // it is now a stale copy, so invalidate it and re-hint the block if (p_pool->invalidate(bid)) p_pool->hint(bid, *w_pool); return result; } //! Take out a block from the pool. //! \return pointer to the block. Ownership of the block goes to the caller. block_type * steal() { return w_pool->steal(); } void add(block_type*& block) { w_pool->add(block); } // PREFETCH POOL METHODS //! Gives a hint for prefetching a block. //! \param bid address of a block to be prefetched //! \return \c true if there was a free block to do prefetch and prefetching //! was scheduled, \c false otherwise //! \note If there are no free blocks available (all blocks //! are already in reading or read but not retrieved by user calling \c read //! method) calling \c hint function has no effect bool hint(bid_type bid) { return p_pool->hint(bid, *w_pool); } bool invalidate(bid_type bid) { return p_pool->invalidate(bid); } //! Reads block. If this block is cached block is not read but passed from the cache. //! \param block block object, where data to be read to. If block was cached \c block 's //! ownership goes to the pool and block from cache is returned in \c block value. //! \param bid address of the block //! \warning \c block parameter must be allocated dynamically using \c new . //! \return request pointer object of read operation request_ptr read(block_type*& block, bid_type bid) { return p_pool->read(block, bid, *w_pool); } }; //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::read_write_pool& a, stxxl::read_write_pool& b) { a.swap(b); } } // namespace std #endif // !STXXL_MNG_READ_WRITE_POOL_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/write_pool.h000644 001411 000144 00000022235 12414452316 021366 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/write_pool.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003-2004 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_WRITE_POOL_HEADER #define STXXL_MNG_WRITE_POOL_HEADER #include #include #include #include #include #define STXXL_VERBOSE_WPOOL(msg) STXXL_VERBOSE1("write_pool[" << static_cast(this) << "]" << msg) STXXL_BEGIN_NAMESPACE //! \addtogroup schedlayer //! \{ //! Implements dynamically resizable buffered writing pool. template class write_pool : private noncopyable { public: typedef BlockType block_type; typedef typename block_type::bid_type bid_type; // a hack to make wait_any work with busy_entry type struct busy_entry { block_type* block; request_ptr req; bid_type bid; busy_entry() : block(NULL) { } busy_entry(const busy_entry& a) : block(a.block), req(a.req), bid(a.bid) { } busy_entry(block_type*& bl, request_ptr& r, bid_type& bi) : block(bl), req(r), bid(bi) { } operator request_ptr () { return req; } }; typedef typename std::list::iterator free_blocks_iterator; typedef typename std::list::iterator busy_blocks_iterator; protected: // contains free write blocks std::list free_blocks; // blocks that are in writing std::list busy_blocks; public: //! Constructs pool. //! \param init_size initial number of blocks in the pool explicit write_pool(unsigned_type init_size = 1) { for (unsigned_type i = 0; i < init_size; ++i) { free_blocks.push_back(new block_type); STXXL_VERBOSE_WPOOL(" create block=" << free_blocks.back()); } } void swap(write_pool& obj) { std::swap(free_blocks, obj.free_blocks); std::swap(busy_blocks, obj.busy_blocks); } //! Waits for completion of all ongoing write requests and frees memory. ~write_pool() { STXXL_VERBOSE_WPOOL("::~write_pool free_blocks.size()=" << free_blocks.size() << " busy_blocks.size()=" << busy_blocks.size()); while (!free_blocks.empty()) { STXXL_VERBOSE_WPOOL(" delete free block=" << free_blocks.back()); delete free_blocks.back(); free_blocks.pop_back(); } try { for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) { i2->req->wait(); if (free_blocks.empty()) STXXL_VERBOSE_WPOOL(" delete busy block=(empty)"); else STXXL_VERBOSE_WPOOL(" delete busy block=" << free_blocks.back()); delete i2->block; } } catch (...) { } } //! Returns number of owned blocks. unsigned_type size() const { return free_blocks.size() + busy_blocks.size(); } //! Passes a block to the pool for writing. //! \param block block to write. Ownership of the block goes to the pool. //! \c block must be allocated dynamically with using \c new . //! \param bid location, where to write //! \warning \c block must be allocated dynamically with using \c new . //! \return request object of the write operation request_ptr write(block_type*& block, bid_type bid) { STXXL_VERBOSE_WPOOL("::write: " << block << " @ " << bid); for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) { if (i2->bid == bid) { assert(i2->block != block); STXXL_VERBOSE_WPOOL("WAW dependency"); // try to cancel the obsolete request i2->req->cancel(); // invalidate the bid of the stale write request, // prevents prefetch_pool from stealing a stale block i2->bid.storage = 0; } } request_ptr result = block->write(bid); busy_blocks.push_back(busy_entry(block, result, bid)); block = NULL; // prevent caller from using the block any further return result; } //! Take out a block from the pool. //! \return pointer to the block. Ownership of the block goes to the caller. block_type * steal() { assert(size() > 0); if (!free_blocks.empty()) { block_type* p = free_blocks.back(); STXXL_VERBOSE_WPOOL("::steal : " << free_blocks.size() << " free blocks available, serve block=" << p); free_blocks.pop_back(); return p; } STXXL_VERBOSE_WPOOL("::steal : all " << busy_blocks.size() << " are busy"); busy_blocks_iterator completed = wait_any(busy_blocks.begin(), busy_blocks.end()); assert(completed != busy_blocks.end()); // we got something reasonable from wait_any assert(completed->req->poll()); // and it is *really* completed block_type* p = completed->block; busy_blocks.erase(completed); check_all_busy(); // for debug STXXL_VERBOSE_WPOOL(" serve block=" << p); return p; } // deprecated name for the steal() STXXL_DEPRECATED(block_type * get()) { return steal(); } //! Resizes size of the pool. //! \param new_size new size of the pool after the call void resize(unsigned_type new_size) { int_type diff = int_type(new_size) - int_type(size()); if (diff > 0) { while (--diff >= 0) { free_blocks.push_back(new block_type); STXXL_VERBOSE_WPOOL(" create block=" << free_blocks.back()); } return; } while (++diff <= 0) delete steal(); } STXXL_DEPRECATED(request_ptr get_request(bid_type bid)) { busy_blocks_iterator i2 = busy_blocks.begin(); for ( ; i2 != busy_blocks.end(); ++i2) { if (i2->bid == bid) return i2->req; } return request_ptr(); } bool has_request(bid_type bid) { for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) { if (i2->bid == bid) return true; } return false; } STXXL_DEPRECATED(block_type * steal(bid_type bid)) { busy_blocks_iterator i2 = busy_blocks.begin(); for ( ; i2 != busy_blocks.end(); ++i2) { if (i2->bid == bid) { block_type* p = i2->block; i2->req->wait(); busy_blocks.erase(i2); return p; } } return NULL; } // returns a block and a (potentially unfinished) I/O request associated with it std::pair steal_request(bid_type bid) { for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) { if (i2->bid == bid) { // remove busy block from list, request has not yet been waited for! block_type* blk = i2->block; request_ptr req = i2->req; busy_blocks.erase(i2); STXXL_VERBOSE_WPOOL("::steal_request block=" << blk); // hand over block and (unfinished) request to caller return std::pair(blk, req); } } STXXL_VERBOSE_WPOOL("::steal_request NOT FOUND"); // not matching request found, return a dummy return std::pair((block_type*)NULL, request_ptr()); } void add(block_type*& block) { STXXL_VERBOSE_WPOOL("::add " << block); free_blocks.push_back(block); block = NULL; // prevent caller from using the block any further } protected: void check_all_busy() { busy_blocks_iterator cur = busy_blocks.begin(); int_type cnt = 0; while (cur != busy_blocks.end()) { if (cur->req->poll()) { free_blocks.push_back(cur->block); cur = busy_blocks.erase(cur); ++cnt; continue; } ++cur; } STXXL_VERBOSE_WPOOL("::check_all_busy : " << cnt << " are completed out of " << busy_blocks.size() + cnt << " busy blocks"); } }; //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::write_pool& a, stxxl::write_pool& b) { a.swap(b); } } // namespace std #endif // !STXXL_MNG_WRITE_POOL_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/mng/block_scheduler.h000644 001411 000144 00000230614 12411366426 022340 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/mng/block_scheduler.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MNG_BLOCK_SCHEDULER_HEADER #define STXXL_MNG_BLOCK_SCHEDULER_HEADER #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! Virtualization of a block of data. //! Holds information for allocating and swapping. To use in cooperation with block_scheduler. //! //! A swappable_block can be uninitialized, i.e. it holds no data. //! When access is required, is has to be acquired first, and released afterwards, so it can be swapped in and out as required. //! If the stored data is no longer needed, it can get uninitialized, freeing both internal and external memory. //! \tparam ValueType type of contained objects (POD with no references to internal memory). //! \tparam BlockSize Number of objects in one block. //! BlockSize*sizeof(ValueType) must be divisible by 4096. template class swappable_block { protected: static const unsigned_type raw_block_size = BlockSize * sizeof(ValueType); public: typedef typed_block internal_block_type; typedef typename internal_block_type::bid_type external_block_type; protected: external_block_type external_data; //!external_data.valid if no associated space on disk internal_block_type* internal_data; //NULL if there is no internal memory reserved bool dirty; int_type reference_count; static unsigned_type disk_allocation_offset; void get_external_block() { block_manager::get_instance()->new_block(striping(), external_data, ++disk_allocation_offset); } void free_external_block() { block_manager::get_instance()->delete_block(external_data); external_data = external_block_type(); // make invalid } public: //! Create in uninitialized state. swappable_block() : external_data() /*!valid*/, internal_data(0), dirty(false), reference_count(0) { } ~swappable_block() { } //! If it has an internal_block. The internal_block implicitly holds valid data. bool is_internal() const { return (internal_data != NULL); } //! If the external_block does not hold valid data. bool is_dirty() const { return dirty; } //! If it has an external_block. bool has_external_block() const { return external_data.valid(); } //! If it has an external_block that holds valid data. bool is_external() const { return has_external_block() && ! is_dirty(); } //! If it is acquired. bool is_acquired() const { return reference_count > 0; } //! If it holds an internal_block but does not need it. bool is_evictable() const { return ! is_acquired() && is_internal(); } //! If it has some valid data (in- or external). bool is_initialized() const { return is_internal() || is_external(); } //! Invalidate external data if true. //! \return is_dirty() bool make_dirty_if(const bool make_dirty) { assert(is_acquired()); return dirty = make_dirty || dirty; } //! Acquire the block, i.e. add a reference. Has to be internal. //! \return A reference to the data-block. internal_block_type & acquire() { assert(is_internal()); ++reference_count; return *internal_data; } //! Release the block, i.e. subduct a reference. Has to be acquired. void release() { assert(is_acquired()); --reference_count; } //! Get a reference to the data-block. Has to be acquired. const internal_block_type & get_internal_block() const { assert(is_acquired()); return *internal_data; } //! Get a reference to the data-block. Has to be acquired. internal_block_type & get_internal_block() { assert(is_acquired()); return *internal_data; } //! Fill block with default data, is supposed to be overwritten by subclass. Has to be internal. void fill_default() { } //! Read asyncronusly from external_block to internal_block. Has to be internal and have an external_block. //! \return A request pointer to the I/O. request_ptr read_async(completion_handler on_cmpl = completion_handler()) { assert(is_internal()); assert(has_external_block()); #ifdef RW_VERBOSE STXXL_MSG("reading block"); #endif dirty = false; return internal_data->read(external_data, on_cmpl); } //! Read synchronously from external_block to internal_block. Has to be internal and have an external_block. void read_sync() { read_async()->wait(); } //! Write asyncronusly from internal_block to external_block if necessary. //! \return A request pointer to the I/O, an invalid request pointer if not necessary. request_ptr clean_async(completion_handler on_cmpl = completion_handler()) { if (! is_dirty()) return request_ptr(); if (! has_external_block()) get_external_block(); #ifdef RW_VERBOSE STXXL_MSG("writing block"); #endif dirty = false; return internal_data->write(external_data, on_cmpl); } //! Write synchronously from internal_block to external_block if necessary. void clean_sync() { request_ptr rp = clean_async(); if (rp.valid()) rp->wait(); } //! Attach an internal_block, making the block internal. Has to be not internal. void attach_internal_block(internal_block_type* iblock) { assert(! is_internal()); internal_data = iblock; } //! Detach the internal_block. Writes to external_block if necessary. Has to be evictable. //! \return A pointer to the internal_block. internal_block_type * detach_internal_block() { assert(is_evictable()); clean_sync(); internal_block_type* iblock = internal_data; internal_data = 0; return iblock; } //! Bring the block in uninitialized state, freeing external and internal memory. //! Returns a pointer to the internal_block, NULL if it had none. //! \return A pointer to the freed internal_block, NULL if it had none. internal_block_type * deinitialize() { assert(! is_acquired()); dirty = false; // free external_block (so that it becomes invalid and the disk-space can be used again) if (has_external_block()) free_external_block(); // free internal_block internal_block_type* iblock = internal_data; internal_data = 0; return iblock; } //! Set the external_block that holds the swappable_block's data. The block gets initialized with it. //! \param eblock The external_block holding initial data. void initialize(external_block_type eblock) { assert(! is_initialized()); external_data = eblock; } //! Extract the swappable_blocks data in an external_block. The block gets uninitialized. //! \return The external_block that holds the swappable_block's data. external_block_type extract_external_block() { assert(! is_internal()); external_block_type eblock = external_data; external_data = external_block_type(); return eblock; } }; template unsigned_type swappable_block::disk_allocation_offset = 0; template class block_scheduler_algorithm; template class block_scheduler_algorithm_online_lru; //! Schedules swapping of blocks and provides blocks for temporary storage. //! //! In simple mode, it tries to save I/Os through caching only. //! In simulation mode, it records access patterns into a prediction sequence. //! The prediction sequence can then be used for prefetching in the (offline) execute mode. //! This will only work for algorithms with deterministic, data oblivious access patterns. //! In simulation mode, no I/O is performed; the data provided is accessible but undefined. //! In execute mode, it does caching, prefetching, and possibly other optimizations. //! \tparam SwappableBlockType Type of swappable_blocks to manage. Can be some specialized subclass. template class block_scheduler : private noncopyable { protected: // tuning-parameter: To acquire blocks, internal memory has to be allocated. // This constant limits the number of internal_blocks allocated at once. static const int_type max_internal_blocks_alloc_at_once; typedef int_type time_type; public: typedef typename SwappableBlockType::internal_block_type internal_block_type; typedef typename SwappableBlockType::external_block_type external_block_type; typedef typename std::vector::size_type swappable_block_identifier_type; /*/! Mode the block scheduler currently works in enum mode { online, //serve requests immediately, without any prediction, LRU caching simulation, //record prediction sequence only, do not serve requests, (returned blocks must not be accessed) offline_lfd, //serve requests based on prediction sequence, using longest-forward-distance caching offline_lfd_prefetch //serve requests based on prediction sequence, using longest-forward-distance caching, and prefetching };*/ public: // -------- prediction_sequence ------- enum block_scheduler_operation { op_acquire, op_acquire_uninitialized, op_release, op_release_dirty, op_deinitialize, op_initialize, op_extract_external_block }; struct prediction_sequence_element { block_scheduler_operation op; swappable_block_identifier_type id; time_type time; prediction_sequence_element(block_scheduler_operation op, swappable_block_identifier_type id, int_type time) : op(op), id(id), time(time) { } }; typedef std::list prediction_sequence_type; // ---- end prediction_sequence ------- protected: template friend class block_scheduler_algorithm; const int_type max_internal_blocks; int_type remaining_internal_blocks; //! Stores pointers to arrays of internal_blocks. Used to deallocate them only. std::stack internal_blocks_blocks; //! holds free internal_blocks with attributes reset. std::stack free_internal_blocks; //! temporary blocks that will not be needed after algorithm termination. mutable std::vector swappable_blocks; //! holds indices of free swappable_blocks with attributes reset. std::priority_queue, std::greater > free_swappable_blocks; block_manager* bm; block_scheduler_algorithm* algo; //! Get an internal_block from the freelist or a newly allocated one if available. //! \return Pointer to the internal_block. NULL if none available. internal_block_type * get_free_internal_block() { if (! free_internal_blocks.empty()) { // => there are internal_blocks in the free-list internal_block_type* iblock = free_internal_blocks.top(); free_internal_blocks.pop(); return iblock; } else if (remaining_internal_blocks > 0) { // => more internal_blocks can be allocated int_type num_blocks = std::min(max_internal_blocks_alloc_at_once, remaining_internal_blocks); remaining_internal_blocks -= num_blocks; internal_block_type* iblocks = new internal_block_type[num_blocks]; internal_blocks_blocks.push(iblocks); for (int_type i = num_blocks - 1; i > 0; --i) free_internal_blocks.push(iblocks + i); return iblocks; } else { // => no internal_block available return 0; } } //! Return an internal_block to the freelist. void return_free_internal_block(internal_block_type* iblock) { free_internal_blocks.push(iblock); } public: //! Create a block_scheduler with empty prediction sequence in simple mode. //! \param max_internal_memory Amount of internal memory (in bytes) the scheduler is allowed to use for acquiring, prefetching and caching. explicit block_scheduler(const int_type max_internal_memory) : max_internal_blocks(div_ceil(max_internal_memory, sizeof(internal_block_type))), remaining_internal_blocks(max_internal_blocks), bm(block_manager::get_instance()), algo(0) { algo = new block_scheduler_algorithm_online_lru(*this); } ~block_scheduler() { delete algo; int_type num_freed_internal_blocks = 0; if (free_swappable_blocks.size() != swappable_blocks.size()) { // => not all swappable_blocks are free, at least deinitialize them STXXL_ERRMSG("not all swappable_blocks are free, those not acquired will be deinitialized"); // evictable_blocks would suffice for (typename std::vector::iterator it = swappable_blocks.begin(); it != swappable_blocks.end(); ++it) { if (! it->is_acquired() && it->deinitialize()) // count internal_blocks that get freed num_freed_internal_blocks++; } } if (int_type nlost = (max_internal_blocks - remaining_internal_blocks) - (free_internal_blocks.size() + num_freed_internal_blocks)) { STXXL_ERRMSG(nlost << " internal_blocks are lost. They will get deallocated."); } while (! internal_blocks_blocks.empty()) { delete[] internal_blocks_blocks.top(); internal_blocks_blocks.pop(); } } //! Acquire the given block. //! Has to be in pairs with release. Pairs may be nested and interleaved. //! \return Reference to the block's data. //! param sbid Swappable block to acquire. internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) { return algo->acquire(sbid, uninitialized); } //! Release the given block. //! Has to be in pairs with acquire. Pairs may be nested and interleaved. //! \param sbid Swappable block to release. //! \param dirty If the data has been changed, invalidating possible data in external storage. void release(const swappable_block_identifier_type sbid, const bool dirty) { algo->release(sbid, dirty); } //! Drop all data in the given block, freeing in- and external memory. void deinitialize(const swappable_block_identifier_type sbid) { algo->deinitialize(sbid); } //! Initialize the swappable_block with the given external_block. //! //! It will use the the external_block for swapping and take care about //! it's deallocation. Has to be uninitialized. //! \param sbid identifier to the swappable_block //! \param eblock external_block a.k.a. bid void initialize(const swappable_block_identifier_type sbid, external_block_type eblock) { algo->initialize(sbid, eblock); } //! Deinitialize the swappable_block and return it's contents in an external_block. //! //! \param sbid identifier to the swappable_block //! \return external_block a.k.a. bid external_block_type extract_external_block(const swappable_block_identifier_type sbid) { return algo->extract_external_block(sbid); } //! check if the swappable_block is initialized. //! \param sbid identifier to the swappable_block //! \return if the swappable_block is initialized bool is_initialized(const swappable_block_identifier_type sbid) const { return algo->is_initialized(sbid); } //! Record a timestep in the prediction sequence to seperate consecutive //! acquire rsp. release-operations. Has an effect only in simulation mode. void explicit_timestep() { algo->explicit_timestep(); } //! Get a const reference to given block's data. Block has to be already acquired. //! \param sbid Swappable block to access. internal_block_type & get_internal_block(const swappable_block_identifier_type sbid) const { return swappable_blocks[sbid].get_internal_block(); } //! Allocate an uninitialized swappable_block. //! \return An identifier of the block. swappable_block_identifier_type allocate_swappable_block() { swappable_block_identifier_type sbid; if (free_swappable_blocks.empty()) { // create new swappable_block sbid = swappable_blocks.size(); swappable_blocks.resize(sbid + 1); algo->swappable_blocks_resize(sbid + 1); } else { // take swappable_block from freelist sbid = free_swappable_blocks.top(); free_swappable_blocks.pop(); } return sbid; } //! Free given no longer used temporary swappable_block. //! \param sbid Temporary swappable_block to free. void free_swappable_block(const swappable_block_identifier_type sbid) { deinitialize(sbid); free_swappable_blocks.push(sbid); } //! Returns if simulation mode is on, i.e. if a prediction sequence is being recorded. //! \return If simulation mode is on. bool is_simulating() const { return algo->is_simulating(); } //! Switch the used algorithm, e.g. to simulation etc.. //! \param new_algo Pointer to the new algorithm object. Has to be instantiated to the block scheduler (or the old algorithm object). //! \return Pointer to the old algorithm object. block_scheduler_algorithm * switch_algorithm_to(block_scheduler_algorithm* new_algo) { assert(&new_algo->bs == this); block_scheduler_algorithm* old_algo = algo; algo = new_algo; return old_algo; } //! Return the current algorithm. block_scheduler_algorithm * get_current_algorithm() const { return algo; } //! Get the prediction_sequence. //! \return reference to the prediction_sequence const prediction_sequence_type & get_prediction_sequence() const { return algo->get_prediction_sequence(); } void flush() { std::vector requests; while (! algo->evictable_blocks_empty()) { swappable_block_identifier_type sbid = algo->evictable_blocks_pop(); request_ptr rq = swappable_blocks[sbid].clean_async(); if (rq.valid()) requests.push_back(rq); return_free_internal_block(swappable_blocks[sbid].detach_internal_block()); } for (typename std::vector::reverse_iterator it = requests.rbegin(); it != requests.rend(); ++it) { (*it)->wait(); } } }; template const int_type block_scheduler::max_internal_blocks_alloc_at_once = 128; //! Interface of a block scheduling algorithm. template class block_scheduler_algorithm : private noncopyable { protected: typedef block_scheduler block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename block_scheduler_type::external_block_type external_block_type; typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; typedef typename block_scheduler_type::time_type time_type; public: block_scheduler_type& bs; protected: std::vector& swappable_blocks; prediction_sequence_type prediction_sequence; block_scheduler_algorithm * get_algorithm_from_block_scheduler() { return bs.algo; } //! Get an internal_block from the block_scheduler. //! \return Pointer to the internal_block. NULL if none available. internal_block_type * get_free_internal_block_from_block_scheduler() { return bs.get_free_internal_block(); } //! Return an internal_block to the block_scheduler. void return_free_internal_block_to_block_scheduler(internal_block_type* iblock) { bs.return_free_internal_block(iblock); } public: block_scheduler_algorithm(block_scheduler_type& bs) : bs(bs), swappable_blocks(bs.swappable_blocks) { } block_scheduler_algorithm(block_scheduler_algorithm* old) : bs(old->bs), swappable_blocks(bs.swappable_blocks) { } virtual ~block_scheduler_algorithm() { } virtual bool evictable_blocks_empty() = 0; virtual swappable_block_identifier_type evictable_blocks_pop() = 0; virtual void swappable_blocks_resize(swappable_block_identifier_type /*size*/) { } virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) = 0; virtual void release(swappable_block_identifier_type sbid, const bool dirty) = 0; virtual void deinitialize(swappable_block_identifier_type sbid) = 0; virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) = 0; virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) = 0; virtual bool is_initialized(const swappable_block_identifier_type sbid) const { return swappable_blocks[sbid].is_initialized(); } virtual void explicit_timestep() { } virtual bool is_simulating() const { return false; } virtual const prediction_sequence_type & get_prediction_sequence() const { return prediction_sequence; } }; //! Block scheduling algorithm caching via the least recently used policy (online). template class block_scheduler_algorithm_online_lru : public block_scheduler_algorithm { protected: typedef block_scheduler block_scheduler_type; typedef block_scheduler_algorithm block_scheduler_algorithm_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename block_scheduler_type::external_block_type external_block_type; typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; using block_scheduler_algorithm_type::bs; using block_scheduler_algorithm_type::swappable_blocks; using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. addressable_fifo_queue evictable_blocks; internal_block_type * get_free_internal_block() { // try to get a free internal_block if (internal_block_type * iblock = get_free_internal_block_from_block_scheduler()) return iblock; // evict block assert(! evictable_blocks.empty()); // fails it there is not enough memory available return swappable_blocks[evictable_blocks.pop()].detach_internal_block(); } void return_free_internal_block(internal_block_type* iblock) { return_free_internal_block_to_block_scheduler(iblock); } void init() { if (get_algorithm_from_block_scheduler()) while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); } public: block_scheduler_algorithm_online_lru(block_scheduler_type& bs) : block_scheduler_algorithm_type(bs) { init(); } block_scheduler_algorithm_online_lru(block_scheduler_algorithm_type* old) : block_scheduler_algorithm_type(old) { init(); } virtual ~block_scheduler_algorithm_online_lru() { if (! evictable_blocks.empty()) STXXL_ERRMSG("Destructing block_scheduler_algorithm_online that still holds evictable blocks. They get deinitialized."); while (! evictable_blocks.empty()) { SwappableBlockType& sblock = swappable_blocks[evictable_blocks.pop()]; if (internal_block_type * iblock = sblock.deinitialize()) return_free_internal_block(iblock); } } virtual bool evictable_blocks_empty() { return evictable_blocks.empty(); } virtual swappable_block_identifier_type evictable_blocks_pop() { return evictable_blocks.pop(); } virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) { SwappableBlockType& sblock = swappable_blocks[sbid]; /* acquired => internal -> increase reference count internal but not acquired -> remove from evictable_blocks, increase reference count not internal => uninitialized or external -> get internal_block, increase reference count uninitialized -> fill with default value external -> read */ if (sblock.is_internal()) { if (! sblock.is_acquired()) // not acquired yet -> remove from evictable_blocks evictable_blocks.erase(sbid); sblock.acquire(); } else if (sblock.is_initialized()) { // => external but not internal //get internal_block sblock.attach_internal_block(get_free_internal_block()); if (! uninitialized) //load block synchronously sblock.read_sync(); sblock.acquire(); } else { // => ! sblock.is_initialized() //get internal_block sblock.attach_internal_block(get_free_internal_block()); sblock.acquire(); //initialize new block if (! uninitialized) sblock.fill_default(); } return sblock.get_internal_block(); } virtual void release(swappable_block_identifier_type sbid, const bool dirty) { SwappableBlockType& sblock = swappable_blocks[sbid]; sblock.make_dirty_if(dirty); sblock.release(); if (! sblock.is_acquired()) { if (sblock.is_dirty() || sblock.is_external()) // => evictable, put in pq evictable_blocks.insert(sbid); else // => uninitialized, release internal block and put it in freelist return_free_internal_block(sblock.detach_internal_block()); } } virtual void deinitialize(swappable_block_identifier_type sbid) { SwappableBlockType& sblock = swappable_blocks[sbid]; if (sblock.is_evictable()) evictable_blocks.erase(sbid); if (internal_block_type * iblock = sblock.deinitialize()) return_free_internal_block(iblock); } virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) { SwappableBlockType& sblock = swappable_blocks[sbid]; sblock.initialize(eblock); } virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) { SwappableBlockType& sblock = swappable_blocks[sbid]; if (sblock.is_evictable()) evictable_blocks.erase(sbid); if (sblock.is_internal()) return_free_internal_block(sblock.detach_internal_block()); return sblock.extract_external_block(); } }; //! Pseudo block scheduling algorithm only recording the request sequence. template class block_scheduler_algorithm_simulation : public block_scheduler_algorithm { protected: typedef block_scheduler block_scheduler_type; typedef block_scheduler_algorithm block_scheduler_algorithm_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename block_scheduler_type::external_block_type external_block_type; typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; typedef typename block_scheduler_type::prediction_sequence_element prediction_sequence_element_type; typedef typename block_scheduler_algorithm_type::time_type time_type; using block_scheduler_algorithm_type::bs; using block_scheduler_algorithm_type::prediction_sequence; using block_scheduler_algorithm_type::swappable_blocks; using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. std::stack evictable_blocks; time_type time_count; bool last_op_release; std::vector reference_counts; internal_block_type dummy_block; void return_free_internal_block(internal_block_type* iblock) { return_free_internal_block_to_block_scheduler(iblock); } void init() { if (get_algorithm_from_block_scheduler()) while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) evictable_blocks.push(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); for (swappable_block_identifier_type i = 0; i < reference_counts.size(); ++i) reference_counts[i] = swappable_blocks[i].is_initialized(); } public: block_scheduler_algorithm_simulation(block_scheduler_type& bs) : block_scheduler_algorithm_type(bs), time_count(0), last_op_release(false), reference_counts(swappable_blocks.size()) { init(); } block_scheduler_algorithm_simulation(block_scheduler_algorithm_type* old) : block_scheduler_algorithm_type(old), time_count(0), last_op_release(false), reference_counts(swappable_blocks.size()) { init(); } virtual ~block_scheduler_algorithm_simulation() { if (! evictable_blocks.empty()) STXXL_ERRMSG("Destructing block_scheduler_algorithm_record_prediction_sequence that still holds evictable blocks. They get deinitialized."); while (! evictable_blocks.empty()) { SwappableBlockType& sblock = swappable_blocks[evictable_blocks.top()]; if (internal_block_type * iblock = sblock.deinitialize()) return_free_internal_block(iblock); evictable_blocks.pop(); } } virtual bool evictable_blocks_empty() { return evictable_blocks.empty(); } virtual swappable_block_identifier_type evictable_blocks_pop() { swappable_block_identifier_type sbid = evictable_blocks.top(); evictable_blocks.pop(); return sbid; } virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) { ++reference_counts[sbid]; last_op_release = false; if (uninitialized) prediction_sequence.push_back( prediction_sequence_element_type(block_scheduler_type::op_acquire_uninitialized, sbid, time_count) ); else prediction_sequence.push_back( prediction_sequence_element_type(block_scheduler_type::op_acquire, sbid, time_count) ); return dummy_block; } virtual void release(swappable_block_identifier_type sbid, const bool dirty) { --reference_counts[sbid] += dirty; time_count += ! last_op_release; last_op_release = true; if (dirty) prediction_sequence.push_back( prediction_sequence_element_type(block_scheduler_type::op_release_dirty, sbid, time_count) ); else prediction_sequence.push_back( prediction_sequence_element_type(block_scheduler_type::op_release, sbid, time_count) ); } virtual void deinitialize(swappable_block_identifier_type sbid) { reference_counts[sbid] = false; prediction_sequence.push_back( prediction_sequence_element_type(block_scheduler_type::op_deinitialize, sbid, time_count) ); } virtual void initialize(swappable_block_identifier_type sbid, external_block_type) { reference_counts[sbid] = true; prediction_sequence.push_back( prediction_sequence_element_type(block_scheduler_type::op_initialize, sbid, time_count) ); } virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) { reference_counts[sbid] = false; prediction_sequence.push_back( prediction_sequence_element_type(block_scheduler_type::op_extract_external_block, sbid, time_count) ); return external_block_type(); } virtual void swappable_blocks_resize(swappable_block_identifier_type size) { reference_counts.resize(size, 0); } virtual bool is_initialized(const swappable_block_identifier_type sbid) const { return reference_counts[sbid] > 0; } virtual void explicit_timestep() { ++time_count; } virtual bool is_simulating() const { return true; } }; //! Block scheduling algorithm caching via the longest forward distance policy (offline). template class block_scheduler_algorithm_offline_lfd : public block_scheduler_algorithm { protected: typedef block_scheduler block_scheduler_type; typedef block_scheduler_algorithm block_scheduler_algorithm_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename block_scheduler_type::external_block_type external_block_type; typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; typedef typename block_scheduler_algorithm_type::time_type time_type; typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; using block_scheduler_algorithm_type::bs; using block_scheduler_algorithm_type::swappable_blocks; using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; class priority { unsigned_type p; public: priority(const SwappableBlockType& sblock, const std::pair& t) { // p larger => evict earlier if (t.first) { // most significant: next use p = unsigned_type(t.second) << 2; // less significant: not dirty p |= unsigned_type(! sblock.is_dirty()) << 1; // less significant: has external_block p |= unsigned_type(sblock.has_external_block()) << 0; } else { // most significant: next use p = std::numeric_limits::max() << 2; // less significant: next operation: extract > accessed no more > deinitialize p |= unsigned_type(t.second) << 0; } } // less => evict earlier bool operator < (const priority& right) const { return p > right.p; } }; //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. addressable_priority_queue evictable_blocks; /*! * Stores for the sequence of releases extracted from the prediction_sequence: * (true, timestamp of the blocks next acquire) if it is acquired next * (false, 0) if it is deinitialized next * (false, 1) if it is not accessed any more * (false, 2) if it is extracted next * (false, 3) if it is initialized next */ std::deque > next_use; internal_block_type * get_free_internal_block() { // try to get a free internal_block if (internal_block_type * iblock = get_free_internal_block_from_block_scheduler()) return iblock; // evict block assert(! evictable_blocks.empty()); // fails it there is not enough memory available return swappable_blocks[evictable_blocks.pop()].detach_internal_block(); } void return_free_internal_block(internal_block_type* iblock) { return_free_internal_block_to_block_scheduler(iblock); } void init(block_scheduler_algorithm_type* old_algo) { std::vector > blocks_next_acquire(swappable_blocks.size(), std::make_pair(false, 1)); if (old_algo) { // precomputations for priorities: init next_acquires const prediction_sequence_type& ps = old_algo->get_prediction_sequence(); for (typename prediction_sequence_type::const_reverse_iterator it = ps.rbegin(); it != ps.rend(); ++it) { switch (it->op) { case (block_scheduler_type::op_acquire): case (block_scheduler_type::op_acquire_uninitialized): blocks_next_acquire[it->id] = std::make_pair(true, it->time); break; case (block_scheduler_type::op_release): case (block_scheduler_type::op_release_dirty): next_use.push_front(blocks_next_acquire[it->id]); break; case (block_scheduler_type::op_deinitialize): blocks_next_acquire[it->id] = std::make_pair(false, 0); break; case (block_scheduler_type::op_initialize): blocks_next_acquire[it->id] = std::make_pair(false, 3); break; case (block_scheduler_type::op_extract_external_block): blocks_next_acquire[it->id] = std::make_pair(false, 2); break; } } } if (get_algorithm_from_block_scheduler()) { while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) { // insert already evictable blocks with the right priority const swappable_block_identifier_type sbid = get_algorithm_from_block_scheduler()->evictable_blocks_pop(); evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], blocks_next_acquire[sbid])); } } } public: block_scheduler_algorithm_offline_lfd(block_scheduler_type& bs) : block_scheduler_algorithm_type(bs) { init(get_algorithm_from_block_scheduler()); } // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence block_scheduler_algorithm_offline_lfd(block_scheduler_algorithm_type* old) : block_scheduler_algorithm_type(old) { init(old); } virtual ~block_scheduler_algorithm_offline_lfd() { if (! evictable_blocks.empty()) STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lfd that still holds evictable blocks. They get deinitialized."); while (! evictable_blocks.empty()) { SwappableBlockType& sblock = swappable_blocks[evictable_blocks.pop()]; if (internal_block_type * iblock = sblock.deinitialize()) return_free_internal_block(iblock); } } virtual bool evictable_blocks_empty() { return evictable_blocks.empty(); } virtual swappable_block_identifier_type evictable_blocks_pop() { return evictable_blocks.pop(); } virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) { SwappableBlockType& sblock = swappable_blocks[sbid]; /* acquired => internal -> increase reference count internal but not acquired -> remove from evictable_blocks, increase reference count not intern => uninitialized or external -> get internal_block, increase reference count uninitialized -> fill with default value external -> read */ if (sblock.is_internal()) { if (! sblock.is_acquired()) // not acquired yet -> remove from evictable_blocks evictable_blocks.erase(sbid); sblock.acquire(); } else if (sblock.is_initialized()) { // => external but not internal //get internal_block sblock.attach_internal_block(get_free_internal_block()); if (! uninitialized) //load block synchronously sblock.read_sync(); sblock.acquire(); } else { // => ! sblock.is_initialized() //get internal_block sblock.attach_internal_block(get_free_internal_block()); sblock.acquire(); //initialize new block if (! uninitialized) sblock.fill_default(); } return sblock.get_internal_block(); } virtual void release(swappable_block_identifier_type sbid, const bool dirty) { if (next_use.empty()) { STXXL_ERRMSG("block_scheduler_algorithm_offline_lfd got release-request but prediction sequence ended. Switching to block_scheduler_algorithm_online."); // switch algorithm block_scheduler_algorithm_type* new_algo, * old_algo; new_algo = new block_scheduler_algorithm_online_lru(bs); old_algo = bs.switch_algorithm_to(new_algo); // redirect call new_algo->release(sbid, dirty); // delete self delete old_algo; return; } SwappableBlockType& sblock = swappable_blocks[sbid]; sblock.make_dirty_if(dirty); sblock.release(); if (! sblock.is_acquired()) { if (sblock.is_dirty() || sblock.is_external()) // => evictable, put in pq evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], next_use.front())); else // => uninitialized, release internal block and put it in freelist return_free_internal_block(sblock.detach_internal_block()); } next_use.pop_front(); } virtual void deinitialize(swappable_block_identifier_type sbid) { SwappableBlockType& sblock = swappable_blocks[sbid]; if (sblock.is_evictable()) evictable_blocks.erase(sbid); if (internal_block_type * iblock = sblock.deinitialize()) return_free_internal_block(iblock); } virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) { SwappableBlockType& sblock = swappable_blocks[sbid]; sblock.initialize(eblock); } virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) { SwappableBlockType& sblock = swappable_blocks[sbid]; if (sblock.is_evictable()) evictable_blocks.erase(sbid); if (sblock.is_internal()) return_free_internal_block(sblock.detach_internal_block()); return sblock.extract_external_block(); } }; //! Block scheduling algorithm caching via the least recently used policy //! (offline), and prefetching in addition. template class block_scheduler_algorithm_offline_lru_prefetching : public block_scheduler_algorithm { protected: struct scheduled_block_meta; struct write_read_request; typedef block_scheduler block_scheduler_type; typedef block_scheduler_algorithm block_scheduler_algorithm_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename block_scheduler_type::external_block_type external_block_type; typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; typedef typename block_scheduler_algorithm_type::time_type time_type; typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; typedef typename block_scheduler_type::block_scheduler_operation block_scheduler_operation; typedef typename std::vector::iterator swappable_blocks_iterator; typedef std::map scheduled_blocks_type; typedef typename scheduled_blocks_type::iterator scheduled_blocks_iterator; typedef typename scheduled_blocks_type::reference scheduled_blocks_reference; typedef std::map write_scheduled_blocks_type; typedef typename write_scheduled_blocks_type::iterator write_scheduled_blocks_iterator; typedef typename write_scheduled_blocks_type::reference write_scheduled_blocks_reference; using block_scheduler_algorithm_type::bs; using block_scheduler_algorithm_type::swappable_blocks; using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; using block_scheduler_algorithm_type::prediction_sequence; struct write_read_request { bool write_done_soon; // set by read_after_write, checked by schedule_read() bool shall_read; // checked by read_after_write, set by schedule_read() swappable_blocks_iterator block_to_start_read; // used by read_after_write, set by schedule_read() scheduled_blocks_iterator taker; // read_req set by read_after_write request_ptr write_req; // completes with read_after_write write_read_request() : write_done_soon(false), shall_read(false), block_to_start_read(), taker(), write_req(0) { } }; struct read_after_write { write_read_request* wrr; read_after_write(write_read_request* write_read_req) : wrr(write_read_req) { } void operator () (request*) { wrr->write_done_soon = true; if (wrr->shall_read) wrr->taker->second.read_req = wrr->block_to_start_read->read_async(); } }; struct scheduled_block_meta { internal_block_type* reserved_iblock; std::pair giver; request_ptr read_req; std::deque operations; // invariant: not empty; front: last scheduled operation, back: upcoming operation scheduled_block_meta(block_scheduler_operation op) : reserved_iblock(0), giver(false, 0), read_req(0), operations() { operations.push_front(op); } }; //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. std::set free_evictable_blocks; std::set scheduled_evictable_blocks; //! Holds not internal swappable_blocks, whose next access has already been scheduled. scheduled_blocks_type scheduled_blocks; //! Holds swappable_blocks, whose internal block has been taken away but the clean did not finish yet. write_scheduled_blocks_type write_scheduled_blocks; typename prediction_sequence_type::iterator next_op_to_schedule; //! Schedule an internal, possibly dirty swappable_block to write. //! //! The block becomes not dirty. if it was dirty, an entry in write_scheduled_blocks is made referencing the write_read_request. //! \param sbid block to write //! \return pointer to the write_read_request write_read_request * schedule_write(const swappable_block_identifier_type sbid) { SwappableBlockType& sblock = swappable_blocks[sbid]; write_read_request* wrr = new write_read_request; wrr->write_req = sblock.clean_async(read_after_write(wrr)); if (wrr->write_req.valid()) { bool t = write_scheduled_blocks.insert(std::make_pair(sbid, wrr)).second; STXXL_ASSERT(t); return wrr; } else { delete wrr; return 0; } } //! Try to interrupt a read scheduled in a write_read_request. //! //! side-effect: possibly erases entry from write_scheduled_blocks, so the iterator writing_block may become invalid //! \return if successful bool try_interrupt_read(const write_scheduled_blocks_iterator& writing_block) { // stop read writing_block->second->shall_read = false; // check if stopped if (! writing_block->second->write_done_soon) return true; // => possibly to late scheduled_blocks_reference taker = *writing_block->second->taker; // wait wait_on_write(writing_block); // check if read started if (taker.second.read_req.valid()) // => read started, to late return false; else // => just in time return true; } //! Schedule an internal and external block to read. //! //! If the giver is still writing, schedule read via its write_read_request. void schedule_read(scheduled_blocks_iterator block_to_read) { // first check if block_to_read is still writing. do not read before write finished // wait_on_write(block_to_read->first); write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->first); if (it != write_scheduled_blocks.end()) { scheduled_blocks_iterator other_block_to_read = it->second->taker; // check if scheduled to read if (it->second->shall_read) { if (try_interrupt_read(it)) { // => interrupted, swap internal_blocks std::swap(other_block_to_read->second.giver, block_to_read->second.giver); if (other_block_to_read->second.giver.first) { write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second); if (it != write_scheduled_blocks.end()) it->second->taker = other_block_to_read; else other_block_to_read->second.giver.first = false; } if (block_to_read->second.giver.first) { write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second); if (it != write_scheduled_blocks.end()) it->second->taker = block_to_read; else block_to_read->second.giver.first = false; } internal_block_type* tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block(); swappable_blocks[block_to_read->first].attach_internal_block( swappable_blocks[other_block_to_read->first].detach_internal_block()); swappable_blocks[other_block_to_read->first].attach_internal_block(tmp_iblock); // => this block has its internal_block back, no need to read // reschedule other schedule_read(other_block_to_read); return; } // else => read already started, but write done -> read this } else { // => no read scheduled, swap internal_blocks std::swap(other_block_to_read->second.giver, block_to_read->second.giver); if (other_block_to_read->second.giver.first) { write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second); if (it != write_scheduled_blocks.end()) it->second->taker = other_block_to_read; else other_block_to_read->second.giver.first = false; } if (block_to_read->second.giver.first) { write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second); if (it != write_scheduled_blocks.end()) it->second->taker = block_to_read; else block_to_read->second.giver.first = false; } internal_block_type* tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block(); swappable_blocks[block_to_read->first].attach_internal_block( other_block_to_read->second.reserved_iblock); other_block_to_read->second.reserved_iblock = tmp_iblock; // => this block has its internal_block back, no need to read return; } } // schedule block_to_read to read if (block_to_read->second.giver.first) { write_scheduled_blocks_iterator writing_block = write_scheduled_blocks.find(block_to_read->second.giver.second); if (writing_block != write_scheduled_blocks.end()) { // => there is a write scheduled // tell the completion handler that we want a read writing_block->second->block_to_start_read = swappable_blocks.begin() + block_to_read->first; writing_block->second->taker = block_to_read; writing_block->second->shall_read = true; // and check if it is not to late if (writing_block->second->write_done_soon) { // => the completion handler may have missed our wish to read // so wait for it to finish and check wait_on_write(writing_block); block_to_read->second.giver.first = false; if (block_to_read->second.read_req.valid()) // read scheduled return; } else // read scheduled return; } else block_to_read->second.giver.first = false; } // => read could not be scheduled through the completion handler block_to_read->second.read_req = swappable_blocks[block_to_read->first].read_async(); } //! Wait for the write to finish. //! //! side-effect: erases entry from write_scheduled_blocks void wait_on_write(const write_scheduled_blocks_iterator& writing_block) { writing_block->second->write_req->wait(); delete writing_block->second; write_scheduled_blocks.erase(writing_block); } //! Wait for the write to finish. //! //! side-effect: erases entry from write_scheduled_blocks void wait_on_write(const swappable_block_identifier_type& writing_block) { write_scheduled_blocks_iterator it = write_scheduled_blocks.find(writing_block); if (it != write_scheduled_blocks.end()) wait_on_write(it); } //! Wait for the write of the giver to finish. //! //! side-effect: erases entry from write_scheduled_blocks void wait_on_write(const scheduled_blocks_iterator& schedule_meta) { if (schedule_meta->second.giver.first) { wait_on_write(schedule_meta->second.giver.second); schedule_meta->second.giver.first = false; } } //! Wait for the read to finish. //! //! side-effect: erases entry for the write of the giver from write_scheduled_blocks void wait_on_read(const scheduled_blocks_iterator& schedule_meta) { wait_on_write(schedule_meta); if (schedule_meta->second.read_req.valid()) { schedule_meta->second.read_req->wait(); schedule_meta->second.read_req = 0; } } //! Wait for the write of the giver to finish and return reserved internal_block. //! //! side-effect: erases entry for the write of the giver from write_scheduled_blocks internal_block_type * get_ready_block(const scheduled_blocks_iterator& schedule_meta) { wait_on_write(schedule_meta); internal_block_type* r = schedule_meta->second.reserved_iblock; schedule_meta->second.reserved_iblock = 0; return r; } bool shall_keep_internal_block(const scheduled_blocks_iterator& schedule_meta, const bool ignore_first = true) const { // returns true iif there is an acquire or acquire_uninitialized scheduled or there is a deinitialize scheduled and the block is dirty for (typename std::deque::reverse_iterator rit = schedule_meta->second.operations.rbegin() + ignore_first; rit != schedule_meta->second.operations.rend(); ++rit) { switch (*rit) { case block_scheduler_type::op_acquire: case block_scheduler_type::op_acquire_uninitialized: return true; break; case block_scheduler_type::op_release: case block_scheduler_type::op_release_dirty: break; case block_scheduler_type::op_deinitialize: if (swappable_blocks[schedule_meta->first].is_dirty()) return true; break; case block_scheduler_type::op_initialize: case block_scheduler_type::op_extract_external_block: break; } } return false; } // assumes the current operation to be still in operations bool shall_be_cleaned(const scheduled_blocks_iterator& schedule_meta) const { // returns true iif there is an extract_external_block scheduled and no release_dirty, deinitialize or initialize before for (typename std::deque::reverse_iterator rit = schedule_meta->second.operations.rbegin() + 1; rit != schedule_meta->second.operations.rend(); ++rit) { switch (*rit) { case block_scheduler_type::op_acquire: case block_scheduler_type::op_acquire_uninitialized: case block_scheduler_type::op_release: break; case block_scheduler_type::op_release_dirty: case block_scheduler_type::op_deinitialize: case block_scheduler_type::op_initialize: return false; break; case block_scheduler_type::op_extract_external_block: return true; break; } } return false; } bool shall_be_read(const scheduled_blocks_iterator& schedule_meta, const bool ignore_first = true) const { // returns true iif there is an acquire scheduled next and the block is initialized return swappable_blocks[schedule_meta->first].is_initialized() && schedule_meta->second.operations.rbegin() + ignore_first != schedule_meta->second.operations.rend() && *(schedule_meta->second.operations.rbegin() + ignore_first) == block_scheduler_type::op_acquire; } void operation_done(scheduled_blocks_iterator& schedule_meta) { schedule_meta->second.operations.pop_back(); if (schedule_meta->second.operations.empty()) { assert(! schedule_meta->second.giver.first); scheduled_blocks.erase(schedule_meta); } } block_scheduler_algorithm_type * give_up(std::string err_msg = "detected some error in the prediction sequence") { STXXL_ERRMSG("block_scheduler_algorithm_offline_lru_prefetching: " << err_msg << ". Switching to block_scheduler_algorithm_online."); // switch algorithm block_scheduler_algorithm_type* new_algo = new block_scheduler_algorithm_online_lru(bs); // and delete self delete bs.switch_algorithm_to(new_algo); return new_algo; } void return_free_internal_block(internal_block_type* iblock) { return_free_internal_block_to_block_scheduler(iblock); } void schedule_next_operations() { while (next_op_to_schedule != prediction_sequence.end()) { // list operation in scheduled_blocks std::pair ins_res = scheduled_blocks.insert( std::make_pair(next_op_to_schedule->id, next_op_to_schedule->op)); scheduled_blocks_iterator schedule_meta = ins_res.first; if (! ins_res.second) schedule_meta->second.operations.push_front(next_op_to_schedule->op); SwappableBlockType& sblock = swappable_blocks[next_op_to_schedule->id]; // do appropriate preparations if (next_op_to_schedule->op == block_scheduler_type::op_acquire || next_op_to_schedule->op == block_scheduler_type::op_acquire_uninitialized) { if (sblock.is_internal()) { if (free_evictable_blocks.erase(next_op_to_schedule->id)) scheduled_evictable_blocks.insert(next_op_to_schedule->id); } else { if (! schedule_meta->second.reserved_iblock) { // => needs internal_block -> try to get one // -> try to get one from block_scheduler schedule_meta->second.reserved_iblock = get_free_internal_block_from_block_scheduler(); if (! schedule_meta->second.reserved_iblock) { // -> try to get one by evicting if (free_evictable_blocks.empty()) { // => can not schedule acquire // remove operation from scheduled_blocks if (ins_res.second) scheduled_blocks.erase(ins_res.first); else schedule_meta->second.operations.pop_front(); // stop scheduling return; } swappable_block_identifier_type giver = pop_begin(free_evictable_blocks); { assert(scheduled_blocks.find(giver) == scheduled_blocks.end() || !shall_keep_internal_block(scheduled_blocks.find(giver), false)); } write_read_request* wrr = schedule_write(giver); schedule_meta->second.giver.first = (wrr != NULL); schedule_meta->second.giver.second = giver; schedule_meta->second.reserved_iblock = swappable_blocks[giver].detach_internal_block(); if (wrr) wrr->taker = schedule_meta; } // read if desired if (shall_be_read(schedule_meta, false)) { // => there is no operation scheduled for this block before this acquire and it is initialized // -> start prefetching now sblock.attach_internal_block(schedule_meta->second.reserved_iblock); schedule_meta->second.reserved_iblock = 0; scheduled_evictable_blocks.insert(next_op_to_schedule->id); schedule_read(schedule_meta); } } } } else if (next_op_to_schedule->op == block_scheduler_type::op_deinitialize) { if (sblock.is_dirty()) if (free_evictable_blocks.erase(next_op_to_schedule->id)) scheduled_evictable_blocks.insert(next_op_to_schedule->id); } ++next_op_to_schedule; } for (typename std::set::iterator it = free_evictable_blocks.begin(); it != free_evictable_blocks.end(); ++it) { if (! write_scheduled_blocks.count(*it)) schedule_write(*it); } } void init(block_scheduler_algorithm_type* old_algo) { if (old_algo) // copy prediction sequence prediction_sequence = old_algo->get_prediction_sequence(); next_op_to_schedule = prediction_sequence.begin(); if (get_algorithm_from_block_scheduler()) while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) free_evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); schedule_next_operations(); } void deinit() { // TODO remove if (! scheduled_blocks.empty()) STXXL_MSG("deinit while scheduled_blocks not empty"); if (! scheduled_evictable_blocks.empty()) STXXL_MSG("deinit while scheduled_evictable_blocks not empty"); // empty scheduled_blocks free_evictable_blocks.insert(scheduled_evictable_blocks.begin(), scheduled_evictable_blocks.end()); //for (typename std::set::iterator it = scheduled_evictable_blocks.begin(); // it != scheduled_evictable_blocks.end(); ++it) // free_evictable_blocks.insert(*it); scheduled_evictable_blocks.clear(); while (! scheduled_blocks.empty()) { scheduled_blocks_iterator it = scheduled_blocks.begin(); wait_on_read(it); if (it->second.reserved_iblock) return_free_internal_block(it->second.reserved_iblock); scheduled_blocks.erase(it); } while (! write_scheduled_blocks.empty()) { write_scheduled_blocks_iterator it = write_scheduled_blocks.begin(); wait_on_write(it); } } public: block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_type& bs) : block_scheduler_algorithm_type(bs) { init(get_algorithm_from_block_scheduler()); } // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_algorithm_type* old) : block_scheduler_algorithm_type(old) { init(old); } virtual ~block_scheduler_algorithm_offline_lru_prefetching() { deinit(); if (! free_evictable_blocks.empty()) STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lru_prefetching that still holds evictable blocks. They get deinitialized."); while (! free_evictable_blocks.empty()) { SwappableBlockType& sblock = swappable_blocks[pop_begin(free_evictable_blocks)]; if (internal_block_type * iblock = sblock.deinitialize()) return_free_internal_block(iblock); } } virtual bool evictable_blocks_empty() { deinit(); return free_evictable_blocks.empty(); } virtual swappable_block_identifier_type evictable_blocks_pop() { return pop_begin(free_evictable_blocks); } virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) { assert(! prediction_sequence.empty()); assert(prediction_sequence.front().op == ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire)); assert(prediction_sequence.front().id == sbid); prediction_sequence.pop_front(); scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); assert(schedule_meta != scheduled_blocks.end()); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory) assert(schedule_meta->second.operations.back() == ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire)); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory) SwappableBlockType& sblock = swappable_blocks[sbid]; /* acquired => internal -> increase reference count internal but not acquired -> remove from scheduled_evictable_blocks, increase reference count not internal => uninitialized or external -> get internal_block, increase reference count uninitialized -> fill with default value external -> read */ if (sblock.is_internal()) { if (! sblock.is_acquired()) { // not acquired yet -> remove from scheduled_evictable_blocks size_t t = scheduled_evictable_blocks.erase(sbid); STXXL_ASSERT(t != 0); wait_on_read(schedule_meta); } sblock.acquire(); } else { assert(uninitialized || ! sblock.is_initialized()); // initialized blocks should be scheduled to read and thus internal //get internal_block sblock.attach_internal_block(get_ready_block(schedule_meta)); sblock.acquire(); //initialize new block if (! uninitialized) sblock.fill_default(); } operation_done(schedule_meta); return sblock.get_internal_block(); } virtual void release(swappable_block_identifier_type sbid, const bool dirty) { assert(! prediction_sequence.empty()); assert(prediction_sequence.front().op == ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release)); assert(prediction_sequence.front().id == sbid); prediction_sequence.pop_front(); scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); assert(schedule_meta != scheduled_blocks.end()); assert(schedule_meta->second.operations.back() == ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release)); SwappableBlockType& sblock = swappable_blocks[sbid]; sblock.make_dirty_if(dirty); sblock.release(); if (! sblock.is_acquired()) { if (sblock.is_dirty() || sblock.is_external()) { // => evictable if (shall_keep_internal_block(schedule_meta)) { // => swappable_block shall keep its internal_block scheduled_evictable_blocks.insert(sbid); if (shall_be_cleaned(schedule_meta)) schedule_write(sbid); } else { // give block to scheduler free_evictable_blocks.insert(sbid); if (next_op_to_schedule != prediction_sequence.end()) schedule_next_operations(); else { if (! write_scheduled_blocks.count(sbid)) schedule_write(sbid); } } } else { // => uninitialized if (shall_keep_internal_block(schedule_meta)) // => swappable_block shall keep its internal_block schedule_meta->second.reserved_iblock = sblock.detach_internal_block(); else { // release internal block and give it to prefetcher return_free_internal_block(sblock.detach_internal_block()); if (next_op_to_schedule != prediction_sequence.end()) schedule_next_operations(); } } } operation_done(schedule_meta); } virtual void deinitialize(swappable_block_identifier_type sbid) { assert(! prediction_sequence.empty()); assert(prediction_sequence.front().op == block_scheduler_type::op_deinitialize); assert(prediction_sequence.front().id == sbid); prediction_sequence.pop_front(); scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); assert(schedule_meta != scheduled_blocks.end()); assert(schedule_meta->second.operations.back() == block_scheduler_type::op_deinitialize); SwappableBlockType& sblock = swappable_blocks[sbid]; if (sblock.is_evictable()) { size_t t; if (shall_keep_internal_block(schedule_meta, false)) { t = scheduled_evictable_blocks.erase(sbid); if (t == 0) { STXXL_ERRMSG("dirty block not scheduled on deinitialize"); t = free_evictable_blocks.erase(sbid); } } else t = free_evictable_blocks.erase(sbid); assert(t != 0); } if (internal_block_type * iblock = sblock.deinitialize()) { if (shall_keep_internal_block(schedule_meta)) // => swappable_block shall keep its internal_block schedule_meta->second.reserved_iblock = iblock; else { // release internal block and give it to prefetcher return_free_internal_block(iblock); if (next_op_to_schedule != prediction_sequence.end()) schedule_next_operations(); } } operation_done(schedule_meta); } virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) { assert(! prediction_sequence.empty()); assert(prediction_sequence.front().op == block_scheduler_type::op_initialize); assert(prediction_sequence.front().id == sbid); prediction_sequence.pop_front(); scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); assert(schedule_meta != scheduled_blocks.end()); assert(schedule_meta->second.operations.back() == block_scheduler_type::op_initialize); SwappableBlockType& sblock = swappable_blocks[sbid]; sblock.initialize(eblock); if (shall_be_read(schedule_meta)) { sblock.attach_internal_block(schedule_meta->second.reserved_iblock); schedule_meta->second.reserved_iblock = 0; scheduled_evictable_blocks.insert(sbid); schedule_read(schedule_meta); } operation_done(schedule_meta); } virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) { assert(! prediction_sequence.empty()); assert(prediction_sequence.front().op == block_scheduler_type::op_extract_external_block); assert(prediction_sequence.front().id == sbid); prediction_sequence.pop_front(); scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); assert(schedule_meta != scheduled_blocks.end()); assert(schedule_meta->second.operations.back() == block_scheduler_type::op_extract_external_block); SwappableBlockType& sblock = swappable_blocks[sbid]; wait_on_write(sbid); operation_done(schedule_meta); return sblock.extract_external_block(); } }; STXXL_END_NAMESPACE #endif // !STXXL_MNG_BLOCK_SCHEDULER_HEADER stxxl-1.4.1/include/stxxl/bits/verbose.h000644 001411 000144 00000020310 12405375303 020057 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/verbose.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005-2006 Roman Dementiev * Copyright (C) 2007-2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_VERBOSE_HEADER #define STXXL_VERBOSE_HEADER #include #include #include #include #include #define _STXXL_PRNT_COUT (1 << 0) #define _STXXL_PRNT_CERR (1 << 1) #define _STXXL_PRNT_LOG (1 << 2) #define _STXXL_PRNT_ERRLOG (1 << 3) #define _STXXL_PRNT_ADDNEWLINE (1 << 16) #define _STXXL_PRNT_TIMESTAMP (1 << 17) #define _STXXL_PRNT_THREAD_ID (1 << 18) #define _STXXL_PRINT_FLAGS_DEFAULT (_STXXL_PRNT_COUT | _STXXL_PRNT_LOG) #define _STXXL_PRINT_FLAGS_ERROR (_STXXL_PRNT_CERR | _STXXL_PRNT_ERRLOG) #define _STXXL_PRINT_FLAGS_VERBOSE (_STXXL_PRINT_FLAGS_DEFAULT | _STXXL_PRNT_TIMESTAMP | _STXXL_PRNT_THREAD_ID) STXXL_BEGIN_NAMESPACE void print_msg(const char* label, const std::string& msg, unsigned flags); STXXL_END_NAMESPACE #define _STXXL_PRINT(label, message, flags) \ do { \ std::ostringstream str_; \ str_ << message; \ stxxl::print_msg(label, str_.str(), flags | _STXXL_PRNT_ADDNEWLINE); \ } while (false) #define _STXXL_NOT_VERBOSE(message) \ do { \ if (0) { \ std::ostringstream str_; \ str_ << message; \ } \ } while (false) #ifdef STXXL_FORCE_VERBOSE_LEVEL #undef STXXL_VERBOSE_LEVEL #define STXXL_VERBOSE_LEVEL STXXL_FORCE_VERBOSE_LEVEL #endif #ifdef STXXL_DEFAULT_VERBOSE_LEVEL #ifndef STXXL_VERBOSE_LEVEL #define STXXL_VERBOSE_LEVEL STXXL_DEFAULT_VERBOSE_LEVEL #endif #endif #ifndef STXXL_VERBOSE_LEVEL #define STXXL_VERBOSE_LEVEL -1 #endif #if STXXL_VERBOSE_LEVEL > -10 #define STXXL_MSG(x) _STXXL_PRINT("STXXL-MSG", x, _STXXL_PRINT_FLAGS_DEFAULT) #else // Please do not report STXXL problems with STXXL_MSG disabled! #define STXXL_MSG(x) _STXXL_NOT_VERBOSE(x) #endif #if STXXL_VERBOSE_LEVEL > -100 #define STXXL_ERRMSG(x) _STXXL_PRINT("STXXL-ERRMSG", x, _STXXL_PRINT_FLAGS_ERROR) #else // Please do not report STXXL problems with STXXL_ERRMSG disabled! #define STXXL_ERRMSG(x) _STXXL_NOT_VERBOSE(x) #endif // STXXL_VERBOSE0 should be used for current debugging activity only, // and afterwards be replaced by STXXL_VERBOSE1 or higher. // Code that actively uses STXXL_VERBOSE0 should never get into a release. #if STXXL_VERBOSE_LEVEL > -1 #define STXXL_VERBOSE0(x) _STXXL_PRINT("STXXL-VERBOSE0", x, _STXXL_PRINT_FLAGS_VERBOSE) #else #define STXXL_VERBOSE0(x) _STXXL_NOT_VERBOSE(x) #endif #if STXXL_VERBOSE_LEVEL > 0 #define STXXL_VERBOSE1(x) _STXXL_PRINT("STXXL-VERBOSE1", x, _STXXL_PRINT_FLAGS_VERBOSE) #else #define STXXL_VERBOSE1(x) _STXXL_NOT_VERBOSE(x) #endif #define STXXL_VERBOSE(x) STXXL_VERBOSE1(x) #if STXXL_VERBOSE_LEVEL > 1 #define STXXL_VERBOSE2(x) _STXXL_PRINT("STXXL-VERBOSE2", x, _STXXL_PRINT_FLAGS_VERBOSE) #else #define STXXL_VERBOSE2(x) _STXXL_NOT_VERBOSE(x) #endif #if STXXL_VERBOSE_LEVEL > 2 #define STXXL_VERBOSE3(x) _STXXL_PRINT("STXXL-VERBOSE3", x, _STXXL_PRINT_FLAGS_VERBOSE) #else #define STXXL_VERBOSE3(x) _STXXL_NOT_VERBOSE(x) #endif // STXXL_VERBOSE[0123]_THIS prefixes "[0xaddress]" and then calls the version // without _THIS. #define STXXL_VERBOSE0_THIS(x) \ STXXL_VERBOSE0("[" << static_cast(this) << "] " << x) #define STXXL_VERBOSE1_THIS(x) \ STXXL_VERBOSE1("[" << static_cast(this) << "] " << x) #define STXXL_VERBOSE2_THIS(x) \ STXXL_VERBOSE2("[" << static_cast(this) << "] " << x) #define STXXL_VERBOSE3_THIS(x) \ STXXL_VERBOSE3("[" << static_cast(this) << "] " << x) // STXXL_CHECK is an assertion macro for unit tests, which contrarily to // assert() also works in release builds. These macros should ONLY be used in // UNIT TESTS, not in the library source. Use usual assert() there. #define STXXL_CHECK(condition) \ do { \ if (!(condition)) { \ _STXXL_PRINT("STXXL-CHECK", \ #condition " - FAILED @ " __FILE__ ":" << __LINE__, \ _STXXL_PRINT_FLAGS_ERROR); abort(); \ } \ } while (0) #define STXXL_CHECK2(condition, text) \ do { \ if (!(condition)) { \ _STXXL_PRINT("STXXL-CHECK", \ text << " - " #condition " - FAILED @ " __FILE__ ":" << __LINE__, \ _STXXL_PRINT_FLAGS_ERROR); abort(); \ } \ } while (0) // STXXL_ASSERT is an assertion macro almost identical to assert(). The only // difference is that with NDEBUG defined, the _code_ itself still exists. This // silences warnings about unused variables and typedefs in release builds. #ifdef NDEBUG #define STXXL_ASSERT(condition) \ do { if (0) { \ if (!(condition)) { \ _STXXL_PRINT("STXXL-ASSERT", \ #condition " - FAILED @ " __FILE__ ":" << __LINE__, \ _STXXL_PRINT_FLAGS_ERROR); abort(); \ } \ } \ } while (0) #else #define STXXL_ASSERT(condition) \ do { if (1) { \ if (!(condition)) { \ _STXXL_PRINT("STXXL-ASSERT", \ #condition " - FAILED @ " __FILE__ ":" << __LINE__, \ _STXXL_PRINT_FLAGS_ERROR); abort(); \ } \ } \ } while (0) #endif // STXXL_CHECK_THROW is an assertion macro for unit tests, which checks that // the enclosed code throws an exception. #define STXXL_CHECK_THROW(code, exception_type) \ do { \ bool t_ = false; try { code; } \ catch (const exception_type&) { t_ = true; } \ if (t_) break; \ _STXXL_PRINT("STXXL-CHECK-THROW", \ #code " - NO EXCEPTION " #exception_type \ " @ " __FILE__ ":" << __LINE__, \ _STXXL_PRINT_FLAGS_ERROR); \ abort(); \ } while (0) //////////////////////////////////////////////////////////////////////////// #endif // !STXXL_VERBOSE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/stream/unique.h000644 001411 000144 00000006303 12411366426 021224 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/stream/unique.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003-2005 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_STREAM_UNIQUE_HEADER #define STXXL_STREAM_UNIQUE_HEADER #include STXXL_BEGIN_NAMESPACE //! Stream package subnamespace. namespace stream { //////////////////////////////////////////////////////////////////////// // UNIQUE // //////////////////////////////////////////////////////////////////////// struct dummy_cmp_unique { }; //! Equivalent to std::unique algorithms. //! //! Removes consecutive duplicates from the stream. //! Uses BinaryPredicate to compare elements of the stream template class unique { Input& input; BinaryPredicate binary_pred; typename Input::value_type current; public: //! Standard stream typedef. typedef typename Input::value_type value_type; unique(Input& input_, BinaryPredicate binary_pred_) : input(input_), binary_pred(binary_pred_) { if (!input.empty()) current = *input; } //! Standard stream method. unique& operator ++ () { value_type old_value = current; ++input; while (!input.empty() && (binary_pred(current = *input, old_value))) ++input; return *this; } //! Standard stream method. const value_type& operator * () const { return current; } //! Standard stream method. const value_type* operator -> () const { return ¤t; } //! Standard stream method. bool empty() const { return input.empty(); } }; //! Equivalent to std::unique algorithms. //! //! Removes consecutive duplicates from the stream. template class unique { Input& input; typename Input::value_type current; public: //! Standard stream typedef. typedef typename Input::value_type value_type; unique(Input& input_) : input(input_) { if (!input.empty()) current = *input; } //! Standard stream method. unique& operator ++ () { value_type old_value = current; ++input; while (!input.empty() && ((current = *input) == old_value)) ++input; return *this; } //! Standard stream method. const value_type& operator * () const { return current; } //! Standard stream method. const value_type* operator -> () const { return ¤t; } //! Standard stream method. bool empty() const { return input.empty(); } }; //! \} } // namespace stream STXXL_END_NAMESPACE #endif // !STXXL_STREAM_UNIQUE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/stream/sort_stream.h000644 001411 000144 00000160765 12422212055 022263 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/stream/sort_stream.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2005 Roman Dementiev * Copyright (C) 2006-2008 Johannes Singler * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_STREAM_SORT_STREAM_HEADER #define STXXL_STREAM_SORT_STREAM_HEADER #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE namespace stream { //! \addtogroup streampack Stream Package //! \{ //////////////////////////////////////////////////////////////////////// // CREATE RUNS // //////////////////////////////////////////////////////////////////////// //! Forms sorted runs of data from a stream. //! //! \tparam Input type of the input stream //! \tparam CompareType type of comparison object used for sorting the runs //! \tparam BlockSize size of blocks used to store the runs (in bytes) //! \tparam AllocStr functor that defines allocation strategy for the runs template < class Input, class CompareType, unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(typename Input::value_type), class AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY> class basic_runs_creator : private noncopyable { public: typedef Input input_type; typedef CompareType cmp_type; static const unsigned block_size = BlockSize; typedef AllocStr allocation_strategy_type; public: typedef typename Input::value_type value_type; typedef typed_block block_type; typedef sort_helper::trigger_entry trigger_entry_type; typedef sorted_runs sorted_runs_data_type; typedef typename sorted_runs_data_type::run_type run_type; typedef counting_ptr sorted_runs_type; typedef typename element_iterator_traits::element_iterator element_iterator; protected: //! reference to the input stream Input& m_input; //! comparator used to sort block groups CompareType m_cmp; private: //! stores the result (sorted runs) as smart pointer sorted_runs_type m_result; //! memory for internal use in blocks unsigned_type m_memsize; //! true iff result is already computed (used in 'result()' method) bool m_result_computed; //! Fetch data from input into blocks[first_idx,last_idx). unsigned_type fetch(block_type* blocks, unsigned_type first_idx, unsigned_type last_idx) { element_iterator output = make_element_iterator(blocks, first_idx); unsigned_type curr_idx = first_idx; while (!m_input.empty() && curr_idx != last_idx) { *output = *m_input; ++m_input; ++output; ++curr_idx; } return curr_idx; } //! fill the rest of the block with max values void fill_with_max_value(block_type* blocks, unsigned_type num_blocks, unsigned_type first_idx) { unsigned_type last_idx = num_blocks * block_type::size; if (first_idx < last_idx) { element_iterator curr = make_element_iterator(blocks, first_idx); while (first_idx != last_idx) { *curr = m_cmp.max_value(); ++curr; ++first_idx; } } } //! Sort a specific run, contained in a sequences of blocks. void sort_run(block_type* run, unsigned_type elements) { check_sort_settings(); potentially_parallel::sort(make_element_iterator(run, 0), make_element_iterator(run, elements), m_cmp); } void compute_result(); public: //! Create the object. //! \param input input stream //! \param cmp comparator object //! \param memory_to_use memory amount that is allowed to used by the //! sorter in bytes basic_runs_creator(Input& input, CompareType cmp, unsigned_type memory_to_use) : m_input(input), m_cmp(cmp), m_result(new sorted_runs_data_type), m_memsize(memory_to_use / BlockSize / sort_memory_usage_factor()), m_result_computed(false) { sort_helper::verify_sentinel_strict_weak_ordering(cmp); if (!(2 * BlockSize * sort_memory_usage_factor() <= memory_to_use)) { throw bad_parameter("stxxl::runs_creator<>:runs_creator(): " "INSUFFICIENT MEMORY provided, " "please increase parameter 'memory_to_use'"); } assert(m_memsize > 0); } //! Returns the sorted runs object. //! \return Sorted runs object. The result is computed lazily, i.e. on the first call //! \remark Returned object is intended to be used by \c runs_merger object as input sorted_runs_type & result() { if (!m_result_computed) { compute_result(); m_result_computed = true; #ifdef STXXL_PRINT_STAT_AFTER_RF STXXL_MSG(*stats::get_instance()); #endif //STXXL_PRINT_STAT_AFTER_RF } return m_result; } }; //! Finish the results, i. e. create all runs. //! //! This is the main routine of this class. template void basic_runs_creator::compute_result() { unsigned_type i = 0; unsigned_type m2 = m_memsize / 2; const unsigned_type el_in_run = m2 * block_type::size; // # el in a run STXXL_VERBOSE1("basic_runs_creator::compute_result m2=" << m2); unsigned_type blocks1_length = 0, blocks2_length = 0; block_type* Blocks1 = NULL; #ifndef STXXL_SMALL_INPUT_PSORT_OPT Blocks1 = new block_type[m2 * 2]; #else // push input element into small_run vector in result until it is full while (!input.empty() && blocks1_length != block_type::size) { m_result->small_run.push_back(*input); ++input; ++blocks1_length; } if (blocks1_length == block_type::size && !input.empty()) { Blocks1 = new block_type[m2 * 2]; std::copy(m_result->small_run.begin(), m_result->small_run.end(), Blocks1[0].begin()); m_result->small_run.clear(); } else { STXXL_VERBOSE1("basic_runs_creator: Small input optimization, input length: " << blocks1_length); m_result->elements = blocks1_length; check_sort_settings(); potentially_parallel::sort(m_result->small_run.begin(), m_result->small_run.end(), cmp); return; } #endif //STXXL_SMALL_INPUT_PSORT_OPT // the first block may be there already, now fetch until memsize is filled. blocks1_length = fetch(Blocks1, blocks1_length, el_in_run); // sort first run sort_run(Blocks1, blocks1_length); if (blocks1_length <= block_type::size && m_input.empty()) { // small input, do not flush it on the disk(s) STXXL_VERBOSE1("basic_runs_creator: Small input optimization, input length: " << blocks1_length); assert(m_result->small_run.empty()); m_result->small_run.assign(Blocks1[0].begin(), Blocks1[0].begin() + blocks1_length); m_result->elements = blocks1_length; delete[] Blocks1; return; } block_type* Blocks2 = Blocks1 + m2; block_manager* bm = block_manager::get_instance(); request_ptr* write_reqs = new request_ptr[m2]; run_type run; unsigned_type cur_run_size = div_ceil(blocks1_length, block_type::size); // in blocks run.resize(cur_run_size); bm->new_blocks(AllocStr(), make_bid_iterator(run.begin()), make_bid_iterator(run.end())); disk_queues::get_instance()->set_priority_op(request_queue::WRITE); // fill the rest of the last block with max values fill_with_max_value(Blocks1, cur_run_size, blocks1_length); for (i = 0; i < cur_run_size; ++i) { run[i].value = Blocks1[i][0]; write_reqs[i] = Blocks1[i].write(run[i].bid); } m_result->runs.push_back(run); m_result->runs_sizes.push_back(blocks1_length); m_result->elements += blocks1_length; if (m_input.empty()) { // return wait_all(write_reqs, write_reqs + cur_run_size); delete[] write_reqs; delete[] Blocks1; return; } STXXL_VERBOSE1("Filling the second part of the allocated blocks"); blocks2_length = fetch(Blocks2, 0, el_in_run); if (m_input.empty()) { // optimization if the whole set fits into both halves // (re)sort internally and return blocks2_length += el_in_run; sort_run(Blocks1, blocks2_length); // sort first an second run together wait_all(write_reqs, write_reqs + cur_run_size); bm->delete_blocks(make_bid_iterator(run.begin()), make_bid_iterator(run.end())); cur_run_size = div_ceil(blocks2_length, block_type::size); run.resize(cur_run_size); bm->new_blocks(AllocStr(), make_bid_iterator(run.begin()), make_bid_iterator(run.end())); // fill the rest of the last block with max values fill_with_max_value(Blocks1, cur_run_size, blocks2_length); assert(cur_run_size > m2); for (i = 0; i < m2; ++i) { run[i].value = Blocks1[i][0]; write_reqs[i]->wait(); write_reqs[i] = Blocks1[i].write(run[i].bid); } request_ptr* write_reqs1 = new request_ptr[cur_run_size - m2]; for ( ; i < cur_run_size; ++i) { run[i].value = Blocks1[i][0]; write_reqs1[i - m2] = Blocks1[i].write(run[i].bid); } m_result->runs[0] = run; m_result->runs_sizes[0] = blocks2_length; m_result->elements = blocks2_length; wait_all(write_reqs, write_reqs + m2); delete[] write_reqs; wait_all(write_reqs1, write_reqs1 + cur_run_size - m2); delete[] write_reqs1; delete[] Blocks1; return; } // more than 2 runs can be filled, i. e. the general case sort_run(Blocks2, blocks2_length); cur_run_size = div_ceil(blocks2_length, block_type::size); // in blocks run.resize(cur_run_size); bm->new_blocks(AllocStr(), make_bid_iterator(run.begin()), make_bid_iterator(run.end())); for (i = 0; i < cur_run_size; ++i) { run[i].value = Blocks2[i][0]; write_reqs[i]->wait(); write_reqs[i] = Blocks2[i].write(run[i].bid); } assert((blocks2_length % el_in_run) == 0); m_result->add_run(run, blocks2_length); while (!m_input.empty()) { blocks1_length = fetch(Blocks1, 0, el_in_run); sort_run(Blocks1, blocks1_length); cur_run_size = div_ceil(blocks1_length, block_type::size); // in blocks run.resize(cur_run_size); bm->new_blocks(AllocStr(), make_bid_iterator(run.begin()), make_bid_iterator(run.end())); // fill the rest of the last block with max values (occurs only on the last run) fill_with_max_value(Blocks1, cur_run_size, blocks1_length); for (i = 0; i < cur_run_size; ++i) { run[i].value = Blocks1[i][0]; write_reqs[i]->wait(); write_reqs[i] = Blocks1[i].write(run[i].bid); } m_result->add_run(run, blocks1_length); std::swap(Blocks1, Blocks2); std::swap(blocks1_length, blocks2_length); } wait_all(write_reqs, write_reqs + m2); delete[] write_reqs; delete[] ((Blocks1 < Blocks2) ? Blocks1 : Blocks2); } //! Forms sorted runs of data from a stream. //! //! \tparam Input type of the input stream //! \tparam CompareType type of omparison object used for sorting the runs //! \tparam BlockSize size of blocks used to store the runs //! \tparam AllocStr functor that defines allocation strategy for the runs template < class Input, class CompareType, unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(typename Input::value_type), class AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY > class runs_creator : public basic_runs_creator { private: typedef basic_runs_creator base; public: typedef typename base::cmp_type cmp_type; typedef typename base::value_type value_type; typedef typename base::block_type block_type; typedef typename base::sorted_runs_data_type sorted_runs_data_type; typedef typename base::sorted_runs_type sorted_runs_type; public: //! Creates the object. //! \param input input stream //! \param cmp comparator object //! \param memory_to_use memory amount that is allowed to used by the //! sorter in bytes runs_creator(Input& input, CompareType cmp, unsigned_type memory_to_use) : base(input, cmp, memory_to_use) { } }; //! Input strategy for \c runs_creator class. //! //! This strategy together with \c runs_creator class //! allows to create sorted runs //! data structure usable for \c runs_merger //! pushing elements into the sorter //! (using runs_creator::push()) template struct use_push { typedef ValueType value_type; }; //! Forms sorted runs of elements passed in push() method. //! //! A specialization of \c runs_creator that //! allows to create sorted runs //! data structure usable for \c runs_merger from //! elements passed in sorted push() method.
    //! \tparam ValueType type of values (parameter for \c use_push strategy) //! \tparam CompareType type of comparison object used for sorting the runs //! \tparam BlockSize size of blocks used to store the runs //! \tparam AllocStr functor that defines allocation strategy for the runs template < class ValueType, class CompareType, unsigned BlockSize, class AllocStr > class runs_creator< use_push, CompareType, BlockSize, AllocStr >: private noncopyable { public: typedef CompareType cmp_type; typedef ValueType value_type; typedef typed_block block_type; typedef sort_helper::trigger_entry trigger_entry_type; typedef sorted_runs sorted_runs_data_type; typedef counting_ptr sorted_runs_type; typedef sorted_runs_type result_type; typedef typename element_iterator_traits::element_iterator element_iterator; private: //! comparator object to sort runs CompareType m_cmp; typedef typename sorted_runs_data_type::run_type run_type; //! stores the result (sorted runs) in a reference counted object sorted_runs_type m_result; //! memory size in bytes to use const unsigned_type m_memory_to_use; //! memory size in numberr of blocks for internal use const unsigned_type m_memsize; //! m_memsize / 2 const unsigned_type m_m2; //! true after the result() method was called for the first time bool m_result_computed; //! total number of elements in a run const unsigned_type m_el_in_run; //! current number of elements in the run m_blocks1 internal_size_type m_cur_el; //! accumulation buffer of size m_m2 blocks, half the available memory size block_type* m_blocks1; //! accumulation buffer that is currently being written to disk block_type* m_blocks2; //! reference to write requests transporting the last accumulation buffer //! to disk request_ptr* m_write_reqs; //! run object containing block ids of the run being written to disk run_type run; protected: //! fill the rest of the block with max values void fill_with_max_value(block_type* blocks, unsigned_type num_blocks, unsigned_type first_idx) { unsigned_type last_idx = num_blocks * block_type::size; if (first_idx < last_idx) { element_iterator curr = make_element_iterator(blocks, first_idx); while (first_idx != last_idx) { *curr = m_cmp.max_value(); ++curr; ++first_idx; } } } //! Sort a specific run, contained in a sequences of blocks. void sort_run(block_type* run, unsigned_type elements) { check_sort_settings(); potentially_parallel::sort(make_element_iterator(run, 0), make_element_iterator(run, elements), m_cmp); } void compute_result() { if (m_cur_el == 0) return; sort_run(m_blocks1, m_cur_el); if (m_cur_el <= block_type::size && m_result->elements == 0) { // small input, do not flush it on the disk(s) STXXL_VERBOSE1("runs_creator(use_push): Small input optimization, input length: " << m_cur_el); m_result->small_run.assign(m_blocks1[0].begin(), m_blocks1[0].begin() + m_cur_el); m_result->elements = m_cur_el; return; } const unsigned_type cur_run_size = div_ceil(m_cur_el, block_type::size); // in blocks run.resize(cur_run_size); block_manager* bm = block_manager::get_instance(); bm->new_blocks(AllocStr(), make_bid_iterator(run.begin()), make_bid_iterator(run.end())); disk_queues::get_instance()->set_priority_op(request_queue::WRITE); // fill the rest of the last block with max values fill_with_max_value(m_blocks1, cur_run_size, m_cur_el); unsigned_type i = 0; for ( ; i < cur_run_size; ++i) { run[i].value = m_blocks1[i][0]; if (m_write_reqs[i].get()) m_write_reqs[i]->wait(); m_write_reqs[i] = m_blocks1[i].write(run[i].bid); } m_result->add_run(run, m_cur_el); for (i = 0; i < m_m2; ++i) { if (m_write_reqs[i].get()) m_write_reqs[i]->wait(); } } public: //! Creates the object. //! \param cmp comparator object //! \param memory_to_use memory amount that is allowed to used by the sorter in bytes runs_creator(CompareType cmp, unsigned_type memory_to_use) : m_cmp(cmp), m_memory_to_use(memory_to_use), m_memsize(memory_to_use / BlockSize / sort_memory_usage_factor()), m_m2(m_memsize / 2), m_el_in_run(m_m2 * block_type::size), m_blocks1(NULL), m_blocks2(NULL), m_write_reqs(NULL) { sort_helper::verify_sentinel_strict_weak_ordering(m_cmp); if (!(2 * BlockSize * sort_memory_usage_factor() <= m_memory_to_use)) { throw bad_parameter("stxxl::runs_creator<>:runs_creator(): " "INSUFFICIENT MEMORY provided, " "please increase parameter 'memory_to_use'"); } assert(m_m2 > 0); allocate(); } ~runs_creator() { m_result_computed = 1; deallocate(); } //! Clear current state and remove all items. void clear() { if (!m_result) m_result = new sorted_runs_data_type; else m_result->clear(); m_result_computed = false; m_cur_el = 0; for (unsigned_type i = 0; i < m_m2; ++i) { if (m_write_reqs[i].get()) m_write_reqs[i]->cancel(); } } //! Allocates input buffers and clears result. void allocate() { if (!m_blocks1) { m_blocks1 = new block_type[m_m2 * 2]; m_blocks2 = m_blocks1 + m_m2; m_write_reqs = new request_ptr[m_m2]; } clear(); } //! Deallocates input buffers but not the current result. void deallocate() { result(); // finishes result if (m_blocks1) { delete[] ((m_blocks1 < m_blocks2) ? m_blocks1 : m_blocks2); m_blocks1 = m_blocks2 = NULL; delete[] m_write_reqs; m_write_reqs = NULL; } } //! Adds new element to the sorter. //! \param val value to be added void push(const value_type& val) { assert(m_result_computed == false); if (LIKELY(m_cur_el < m_el_in_run)) { m_blocks1[m_cur_el / block_type::size][m_cur_el % block_type::size] = val; ++m_cur_el; return; } assert(m_el_in_run == m_cur_el); m_cur_el = 0; // sort and store m_blocks1 sort_run(m_blocks1, m_el_in_run); const unsigned_type cur_run_blocks = div_ceil(m_el_in_run, block_type::size); // in blocks run.resize(cur_run_blocks); block_manager* bm = block_manager::get_instance(); bm->new_blocks(AllocStr(), make_bid_iterator(run.begin()), make_bid_iterator(run.end())); disk_queues::get_instance()->set_priority_op(request_queue::WRITE); for (unsigned_type i = 0; i < cur_run_blocks; ++i) { run[i].value = m_blocks1[i][0]; if (m_write_reqs[i].get()) m_write_reqs[i]->wait(); m_write_reqs[i] = m_blocks1[i].write(run[i].bid); } m_result->add_run(run, m_el_in_run); std::swap(m_blocks1, m_blocks2); push(val); } //! Returns the sorted runs object. //! \return Sorted runs object. //! \remark Returned object is intended to be used by \c runs_merger object as input sorted_runs_type & result() { if (!m_result_computed) { compute_result(); m_result_computed = true; #ifdef STXXL_PRINT_STAT_AFTER_RF STXXL_MSG(*stats::get_instance()); #endif //STXXL_PRINT_STAT_AFTER_RF } return m_result; } //! number of items currently inserted. external_size_type size() const { return m_result->elements + m_cur_el; } //! return comparator object. const cmp_type & cmp() const { return m_cmp; } //! return memory size used (in bytes). unsigned_type memory_used() const { return m_memory_to_use; } }; //! Input strategy for \c runs_creator class. //! //! This strategy together with \c runs_creator class //! allows to create sorted runs //! data structure usable for \c runs_merger from //! sequences of elements in sorted order template struct from_sorted_sequences { typedef ValueType value_type; }; //! Forms sorted runs of data taking elements in sorted order (element by element). //! //! A specialization of \c runs_creator that //! allows to create sorted runs //! data structure usable for \c runs_merger from //! sequences of elements in sorted order.
    //! \tparam ValueType type of values (parameter for \c from_sorted_sequences strategy) //! \tparam CompareType type of comparison object used for sorting the runs //! \tparam BlockSize size of blocks used to store the runs //! \tparam AllocStr functor that defines allocation strategy for the runs template < class ValueType, class CompareType, unsigned BlockSize, class AllocStr > class runs_creator< from_sorted_sequences, CompareType, BlockSize, AllocStr >: private noncopyable { public: typedef ValueType value_type; typedef typed_block block_type; typedef sort_helper::trigger_entry trigger_entry_type; typedef AllocStr alloc_strategy_type; public: typedef CompareType cmp_type; typedef sorted_runs sorted_runs_data_type; typedef counting_ptr sorted_runs_type; typedef sorted_runs_type result_type; private: typedef typename sorted_runs_data_type::run_type run_type; CompareType cmp; //! stores the result (sorted runs) sorted_runs_type result_; //! memory for internal use in blocks unsigned_type m_; buffered_writer writer; block_type* cur_block; unsigned_type offset; unsigned_type iblock; unsigned_type irun; //! needs to be reset after each run alloc_strategy_type alloc_strategy; public: //! Creates the object. //! \param c comparator object //! \param memory_to_use memory amount that is allowed to used by the sorter in bytes. //! Recommended value: 2 * block_size * D runs_creator(CompareType c, unsigned_type memory_to_use) : cmp(c), result_(new sorted_runs_data_type), m_(memory_to_use / BlockSize / sort_memory_usage_factor()), writer(m_, m_ / 2), cur_block(writer.get_free_block()), offset(0), iblock(0), irun(0) { sort_helper::verify_sentinel_strict_weak_ordering(cmp); assert(m_ > 0); if (!(2 * BlockSize * sort_memory_usage_factor() <= memory_to_use)) { throw bad_parameter("stxxl::runs_creator<>:runs_creator(): " "INSUFFICIENT MEMORY provided, " "please increase parameter 'memory_to_use'"); } } //! Adds new element to the current run. //! \param val value to be added to the current run void push(const value_type& val) { assert(offset < block_type::size); (*cur_block)[offset] = val; ++offset; if (offset == block_type::size) { // write current block block_manager* bm = block_manager::get_instance(); // allocate space for the block result_->runs.resize(irun + 1); result_->runs[irun].resize(iblock + 1); bm->new_blocks( alloc_strategy, make_bid_iterator(result_->runs[irun].begin() + iblock), make_bid_iterator(result_->runs[irun].end()), iblock ); result_->runs[irun][iblock].value = (*cur_block)[0]; // init trigger cur_block = writer.write(cur_block, result_->runs[irun][iblock].bid); ++iblock; offset = 0; } ++result_->elements; } //! Finishes current run and begins new one. void finish() { if (offset == 0 && iblock == 0) // current run is empty return; result_->runs_sizes.resize(irun + 1); result_->runs_sizes.back() = iblock * block_type::size + offset; if (offset) // if current block is partially filled { while (offset != block_type::size) { (*cur_block)[offset] = cmp.max_value(); ++offset; } offset = 0; block_manager* bm = block_manager::get_instance(); // allocate space for the block result_->runs.resize(irun + 1); result_->runs[irun].resize(iblock + 1); bm->new_blocks( alloc_strategy, make_bid_iterator(result_->runs[irun].begin() + iblock), make_bid_iterator(result_->runs[irun].end()), iblock ); result_->runs[irun][iblock].value = (*cur_block)[0]; // init trigger cur_block = writer.write(cur_block, result_->runs[irun][iblock].bid); } else { } alloc_strategy = alloc_strategy_type(); // reinitialize block allocator for the next run iblock = 0; ++irun; } //! Returns the sorted runs object. //! \return Sorted runs object //! \remark Returned object is intended to be used by \c runs_merger object as input sorted_runs_type & result() { finish(); writer.flush(); return result_; } }; //! Checker for the sorted runs object created by the \c runs_creator . //! \param sruns sorted runs object //! \param cmp comparison object used for checking the order of elements in runs //! \return \c true if runs are sorted, \c false otherwise template bool check_sorted_runs(const RunsType& sruns, CompareType cmp) { sort_helper::verify_sentinel_strict_weak_ordering(cmp); typedef typename RunsType::element_type::block_type block_type; STXXL_VERBOSE2("Elements: " << sruns->elements); unsigned_type nruns = sruns->runs.size(); STXXL_VERBOSE2("Runs: " << nruns); unsigned_type irun = 0; for (irun = 0; irun < nruns; ++irun) { const unsigned_type nblocks = sruns->runs[irun].size(); block_type* blocks = new block_type[nblocks]; request_ptr* reqs = new request_ptr[nblocks]; for (unsigned_type j = 0; j < nblocks; ++j) { reqs[j] = blocks[j].read(sruns->runs[irun][j].bid); } wait_all(reqs, reqs + nblocks); delete[] reqs; for (unsigned_type j = 0; j < nblocks; ++j) { if (cmp(blocks[j][0], sruns->runs[irun][j].value) || cmp(sruns->runs[irun][j].value, blocks[j][0])) //!= { STXXL_ERRMSG("check_sorted_runs wrong trigger in the run"); delete[] blocks; return false; } } if (!stxxl::is_sorted( make_element_iterator(blocks, 0), make_element_iterator(blocks, sruns->runs_sizes[irun]), cmp)) { STXXL_ERRMSG("check_sorted_runs wrong order in the run"); delete[] blocks; return false; } delete[] blocks; } STXXL_MSG("Checking runs finished successfully"); return true; } //////////////////////////////////////////////////////////////////////// // MERGE RUNS // //////////////////////////////////////////////////////////////////////// //! Merges sorted runs. //! //! \tparam RunsType type of the sorted runs, available as \c runs_creator::sorted_runs_type , //! \tparam CompareType type of comparison object used for merging //! \tparam AllocStr allocation strategy used to allocate the blocks for //! storing intermediate results if several merge passes are required template class basic_runs_merger : private noncopyable { public: typedef RunsType sorted_runs_type; typedef CompareType value_cmp; typedef AllocStr alloc_strategy; typedef typename sorted_runs_type::element_type sorted_runs_data_type; typedef typename sorted_runs_data_type::size_type size_type; typedef typename sorted_runs_data_type::run_type run_type; typedef typename sorted_runs_data_type::block_type block_type; typedef block_type out_block_type; typedef typename run_type::value_type trigger_entry_type; typedef block_prefetcher prefetcher_type; typedef run_cursor2 run_cursor_type; typedef sort_helper::run_cursor2_cmp run_cursor2_cmp_type; typedef loser_tree loser_tree_type; typedef stxxl::int64 diff_type; typedef std::pair sequence; typedef typename std::vector::size_type seqs_size_type; public: //! Standard stream typedef. typedef typename sorted_runs_data_type::value_type value_type; private: //! comparator object to sort runs value_cmp m_cmp; //! memory size in bytes to use unsigned_type m_memory_to_use; //! smart pointer to sorted_runs object sorted_runs_type m_sruns; //! items remaining in input size_type m_elements_remaining; //! memory buffer for merging from external streams out_block_type* m_buffer_block; //! pointer into current memory buffer: this is either m_buffer_block or the small_runs vector const value_type* m_current_ptr; //! pointer into current memory buffer: end after range of current values const value_type* m_current_end; //! sequence of block needed for merging run_type m_consume_seq; //! precalculated order of blocks in which they are prefetched int_type* m_prefetch_seq; //! prefetcher object prefetcher_type* m_prefetcher; //! loser tree used for native merging loser_tree_type* m_losers; #if STXXL_PARALLEL_MULTIWAY_MERGE std::vector* seqs; std::vector* buffers; diff_type num_currently_mergeable; #endif #if STXXL_CHECK_ORDER_IN_SORTS //! previous element to ensure the current output ordering value_type m_last_element; #endif //STXXL_CHECK_ORDER_IN_SORTS //////////////////////////////////////////////////////////////////// void merge_recursively(); void deallocate_prefetcher() { if (m_prefetcher) { delete m_losers; #if STXXL_PARALLEL_MULTIWAY_MERGE delete seqs; delete buffers; #endif delete m_prefetcher; delete[] m_prefetch_seq; m_prefetcher = NULL; } } void fill_buffer_block() { STXXL_VERBOSE1("fill_buffer_block"); if (do_parallel_merge()) { #if STXXL_PARALLEL_MULTIWAY_MERGE // begin of STL-style merging diff_type rest = out_block_type::size; // elements still to merge for this output block do // while rest > 0 and still elements available { if (num_currently_mergeable < rest) { if (!m_prefetcher || m_prefetcher->empty()) { // anything remaining is already in memory num_currently_mergeable = m_elements_remaining; } else { num_currently_mergeable = sort_helper::count_elements_less_equal( *seqs, m_consume_seq[m_prefetcher->pos()].value, m_cmp); } } diff_type output_size = STXXL_MIN(num_currently_mergeable, rest); // at most rest elements STXXL_VERBOSE1("before merge " << output_size); stxxl::parallel::multiway_merge((*seqs).begin(), (*seqs).end(), m_buffer_block->end() - rest, m_cmp, output_size); // sequence iterators are progressed appropriately rest -= output_size; num_currently_mergeable -= output_size; STXXL_VERBOSE1("after merge"); sort_helper::refill_or_remove_empty_sequences(*seqs, *buffers, *m_prefetcher); } while (rest > 0 && (*seqs).size() > 0); #if STXXL_CHECK_ORDER_IN_SORTS if (!stxxl::is_sorted(m_buffer_block->begin(), m_buffer_block->end(), cmp)) { for (value_type* i = m_buffer_block->begin() + 1; i != m_buffer_block->end(); ++i) if (cmp(*i, *(i - 1))) { STXXL_VERBOSE1("Error at position " << (i - m_buffer_block->begin())); } assert(false); } #endif //STXXL_CHECK_ORDER_IN_SORTS // end of STL-style merging #else STXXL_THROW_UNREACHABLE(); #endif //STXXL_PARALLEL_MULTIWAY_MERGE } else { // begin of native merging procedure m_losers->multi_merge(m_buffer_block->elem, m_buffer_block->elem + STXXL_MIN(out_block_type::size, m_elements_remaining)); // end of native merging procedure } STXXL_VERBOSE1("current block filled"); m_current_ptr = m_buffer_block->elem; m_current_end = m_buffer_block->elem + STXXL_MIN(out_block_type::size, m_elements_remaining); if (m_elements_remaining <= out_block_type::size) deallocate_prefetcher(); } public: //! Creates a runs merger object. //! \param c comparison object //! \param memory_to_use amount of memory available for the merger in bytes basic_runs_merger(value_cmp c, unsigned_type memory_to_use) : m_cmp(c), m_memory_to_use(memory_to_use), m_buffer_block(new out_block_type), m_prefetch_seq(NULL), m_prefetcher(NULL), m_losers(NULL) #if STXXL_PARALLEL_MULTIWAY_MERGE , seqs(NULL), buffers(NULL), num_currently_mergeable(0) #endif #if STXXL_CHECK_ORDER_IN_SORTS , m_last_element(m_cmp.min_value()) #endif //STXXL_CHECK_ORDER_IN_SORTS { sort_helper::verify_sentinel_strict_weak_ordering(m_cmp); } //! Set memory amount to use for the merger in bytes. void set_memory_to_use(unsigned_type memory_to_use) { m_memory_to_use = memory_to_use; } //! Initialize the runs merger object with a new round of sorted_runs. void initialize(const sorted_runs_type& sruns) { m_sruns = sruns; m_elements_remaining = m_sruns->elements; if (empty()) return; if (!m_sruns->small_run.empty()) { // we have a small input <= B, that is kept in the main memory STXXL_VERBOSE1("basic_runs_merger: small input optimization, input length: " << m_elements_remaining); assert(m_elements_remaining == size_type(m_sruns->small_run.size())); m_current_ptr = &m_sruns->small_run[0]; m_current_end = m_current_ptr + m_sruns->small_run.size(); return; } #if STXXL_CHECK_ORDER_IN_SORTS assert(check_sorted_runs(m_sruns, m_cmp)); #endif //STXXL_CHECK_ORDER_IN_SORTS // *** test whether recursive merging is necessary disk_queues::get_instance()->set_priority_op(request_queue::WRITE); int_type disks_number = config::get_instance()->disks_number(); unsigned_type min_prefetch_buffers = 2 * disks_number; unsigned_type input_buffers = (m_memory_to_use > sizeof(out_block_type) ? m_memory_to_use - sizeof(out_block_type) : 0) / block_type::raw_size; unsigned_type nruns = m_sruns->runs.size(); if (input_buffers < nruns + min_prefetch_buffers) { // can not merge runs in one pass. merge recursively: STXXL_WARNMSG_RECURSIVE_SORT("The implementation of sort requires more than one merge pass, therefore for a better"); STXXL_WARNMSG_RECURSIVE_SORT("efficiency decrease block size of run storage (a parameter of the run_creator)"); STXXL_WARNMSG_RECURSIVE_SORT("or increase the amount memory dedicated to the merger."); STXXL_WARNMSG_RECURSIVE_SORT("m=" << input_buffers << " nruns=" << nruns << " prefetch_blocks=" << min_prefetch_buffers); STXXL_WARNMSG_RECURSIVE_SORT("memory_to_use=" << m_memory_to_use << " bytes block_type::raw_size=" << block_type::raw_size << " bytes"); // check whether we have enough memory to merge recursively unsigned_type recursive_merge_buffers = m_memory_to_use / block_type::raw_size; if (recursive_merge_buffers < 2 * min_prefetch_buffers + 1 + 2) { // recursive merge uses min_prefetch_buffers for input buffering and min_prefetch_buffers output buffering // as well as 1 current output block and at least 2 input blocks STXXL_ERRMSG("There are only m=" << recursive_merge_buffers << " blocks available for recursive merging, but " << min_prefetch_buffers << "+" << min_prefetch_buffers << "+1 are needed read-ahead/write-back/output, and"); STXXL_ERRMSG("the merger requires memory to store at least two input blocks internally. Aborting."); throw bad_parameter("basic_runs_merger::sort(): INSUFFICIENT MEMORY provided, please increase parameter 'memory_to_use'"); } merge_recursively(); nruns = m_sruns->runs.size(); } assert(nruns + min_prefetch_buffers <= input_buffers); // *** Allocate prefetcher and merge data structure deallocate_prefetcher(); unsigned_type prefetch_seq_size = 0; for (unsigned_type i = 0; i < nruns; ++i) { prefetch_seq_size += m_sruns->runs[i].size(); } m_consume_seq.resize(prefetch_seq_size); m_prefetch_seq = new int_type[prefetch_seq_size]; typename run_type::iterator copy_start = m_consume_seq.begin(); for (unsigned_type i = 0; i < nruns; ++i) { copy_start = std::copy(m_sruns->runs[i].begin(), m_sruns->runs[i].end(), copy_start); } std::stable_sort(m_consume_seq.begin(), m_consume_seq.end(), sort_helper::trigger_entry_cmp(m_cmp) _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL); const unsigned_type n_prefetch_buffers = STXXL_MAX(min_prefetch_buffers, input_buffers - nruns); #if STXXL_SORT_OPTIMAL_PREFETCHING // heuristic const int_type n_opt_prefetch_buffers = min_prefetch_buffers + (3 * (n_prefetch_buffers - min_prefetch_buffers)) / 10; compute_prefetch_schedule( m_consume_seq, m_prefetch_seq, n_opt_prefetch_buffers, config::get_instance()->get_max_device_id()); #else for (unsigned_type i = 0; i < prefetch_seq_size; ++i) m_prefetch_seq[i] = i; #endif //STXXL_SORT_OPTIMAL_PREFETCHING m_prefetcher = new prefetcher_type( m_consume_seq.begin(), m_consume_seq.end(), m_prefetch_seq, STXXL_MIN(nruns + n_prefetch_buffers, prefetch_seq_size)); if (do_parallel_merge()) { #if STXXL_PARALLEL_MULTIWAY_MERGE // begin of STL-style merging seqs = new std::vector(nruns); buffers = new std::vector(nruns); for (unsigned_type i = 0; i < nruns; ++i) //initialize sequences { (*buffers)[i] = m_prefetcher->pull_block(); //get first block of each run (*seqs)[i] = std::make_pair((*buffers)[i]->begin(), (*buffers)[i]->end()); //this memory location stays the same, only the data is exchanged } // end of STL-style merging #else STXXL_THROW_UNREACHABLE(); #endif //STXXL_PARALLEL_MULTIWAY_MERGE } else { // begin of native merging procedure m_losers = new loser_tree_type(m_prefetcher, nruns, run_cursor2_cmp_type(m_cmp)); // end of native merging procedure } fill_buffer_block(); } //! Deallocate temporary structures freeing memory prior to next initialize(). void deallocate() { deallocate_prefetcher(); m_sruns = NULL; // release reference on result object } public: //! Standard stream method. bool empty() const { return (m_elements_remaining == 0); } //! Standard size method. size_type size() const { return m_elements_remaining; } //! Standard stream method. const value_type& operator * () const { assert(!empty()); return *m_current_ptr; } //! Standard stream method. const value_type* operator -> () const { return &(operator * ()); } //! Standard stream method. basic_runs_merger& operator ++ () // preincrement operator { assert(!empty()); assert(m_current_ptr != m_current_end); --m_elements_remaining; ++m_current_ptr; if (LIKELY(m_current_ptr == m_current_end && !empty())) { fill_buffer_block(); #if STXXL_CHECK_ORDER_IN_SORTS assert(stxxl::is_sorted(m_buffer_block->elem, m_buffer_block->elem + STXXL_MIN(m_elements_remaining, m_buffer_block->size), m_cmp)); #endif //STXXL_CHECK_ORDER_IN_SORTS } #if STXXL_CHECK_ORDER_IN_SORTS if (!empty()) { assert(!m_cmp(operator * (), m_last_element)); m_last_element = operator * (); } #endif //STXXL_CHECK_ORDER_IN_SORTS return *this; } //! Destructor. //! \remark Deallocates blocks of the input sorted runs object virtual ~basic_runs_merger() { deallocate_prefetcher(); delete m_buffer_block; } }; template void basic_runs_merger::merge_recursively() { block_manager* bm = block_manager::get_instance(); unsigned_type ndisks = config::get_instance()->disks_number(); unsigned_type nwrite_buffers = 2 * ndisks; unsigned_type memory_for_write_buffers = nwrite_buffers * sizeof(block_type); // memory consumption of the recursive merger (uses block_type as // out_block_type) unsigned_type recursive_merger_memory_prefetch_buffers = 2 * ndisks * sizeof(block_type); unsigned_type recursive_merger_memory_out_block = sizeof(block_type); unsigned_type memory_for_buffers = memory_for_write_buffers + recursive_merger_memory_prefetch_buffers + recursive_merger_memory_out_block; // maximum arity in the recursive merger unsigned_type max_arity = (m_memory_to_use > memory_for_buffers ? m_memory_to_use - memory_for_buffers : 0) / block_type::raw_size; unsigned_type nruns = m_sruns->runs.size(); const unsigned_type merge_factor = optimal_merge_factor(nruns, max_arity); assert(merge_factor > 1); assert(merge_factor <= max_arity); while (nruns > max_arity) { unsigned_type new_nruns = div_ceil(nruns, merge_factor); STXXL_MSG("Starting new merge phase: nruns: " << nruns << " opt_merge_factor: " << merge_factor << " max_arity: " << max_arity << " new_nruns: " << new_nruns); // construct new sorted_runs data object which will be swapped into // m_sruns sorted_runs_data_type new_runs; new_runs.runs.resize(new_nruns); new_runs.runs_sizes.resize(new_nruns); new_runs.elements = m_sruns->elements; // merge all runs from m_runs into news_runs unsigned_type runs_left = nruns; unsigned_type cur_out_run = 0; size_type elements_left = m_sruns->elements; while (runs_left > 0) { unsigned_type runs2merge = STXXL_MIN(runs_left, merge_factor); STXXL_MSG("Merging " << runs2merge << " runs"); if (runs2merge > 1) // non-trivial merge { // count the number of elements in the run size_type elements_in_new_run = 0; for (unsigned_type i = nruns - runs_left; i < (nruns - runs_left + runs2merge); ++i) { elements_in_new_run += m_sruns->runs_sizes[i]; } new_runs.runs_sizes[cur_out_run] = elements_in_new_run; // calculate blocks in run const unsigned_type blocks_in_new_run = (unsigned_type)div_ceil(elements_in_new_run, block_type::size); // allocate blocks for the new runs new_runs.runs[cur_out_run].resize(blocks_in_new_run); bm->new_blocks(alloc_strategy(), make_bid_iterator(new_runs.runs[cur_out_run].begin()), make_bid_iterator(new_runs.runs[cur_out_run].end())); // Construct temporary sorted_runs object as input into recursive merger. // This sorted_runs is copied a subset of the over-large set of runs, which // will be deallocated from external memory once the runs are merged. sorted_runs_type cur_runs = new sorted_runs_data_type; cur_runs->runs.resize(runs2merge); cur_runs->runs_sizes.resize(runs2merge); std::copy(m_sruns->runs.begin() + nruns - runs_left, m_sruns->runs.begin() + nruns - runs_left + runs2merge, cur_runs->runs.begin()); std::copy(m_sruns->runs_sizes.begin() + nruns - runs_left, m_sruns->runs_sizes.begin() + nruns - runs_left + runs2merge, cur_runs->runs_sizes.begin()); cur_runs->elements = elements_in_new_run; elements_left -= elements_in_new_run; // construct recursive merger basic_runs_merger merger(m_cmp, m_memory_to_use - memory_for_write_buffers); merger.initialize(cur_runs); { // make sure everything is being destroyed in right time buf_ostream out( new_runs.runs[cur_out_run].begin(), nwrite_buffers); size_type cnt = 0; const size_type cnt_max = cur_runs->elements; while (cnt != cnt_max) { *out = *merger; if ((cnt % block_type::size) == 0) // have to write the trigger value new_runs.runs[cur_out_run][(unsigned_type)(cnt / size_type(block_type::size))].value = *merger; ++cnt, ++out, ++merger; } assert(merger.empty()); while (cnt % block_type::size) { *out = m_cmp.max_value(); ++out, ++cnt; } } // deallocate merged runs by destroying cur_runs } else // runs2merge = 1 -> no merging needed { assert(cur_out_run + 1 == new_runs.runs.size()); elements_left -= m_sruns->runs_sizes.back(); // copy block identifiers into new sorted_runs object new_runs.runs.back() = m_sruns->runs.back(); new_runs.runs_sizes.back() = m_sruns->runs_sizes.back(); } runs_left -= runs2merge; ++cur_out_run; } assert(elements_left == 0); // clear bid vector of m_sruns to skip deallocation of blocks in // destructor m_sruns->runs.clear(); // replaces data in referenced counted object m_sruns end while (nruns // > max_arity) std::swap(nruns, new_nruns); m_sruns->swap(new_runs); } } //! Merges sorted runs. //! //! \tparam RunsType type of the sorted runs, available as \c runs_creator::sorted_runs_type , //! \tparam CompareType type of comparison object used for merging //! \tparam AllocStr allocation strategy used to allocate the blocks for //! storing intermediate results if several merge passes are required template class runs_merger : public basic_runs_merger { protected: typedef basic_runs_merger base; public: typedef RunsType sorted_runs_type; typedef typename base::value_cmp value_cmp; typedef typename base::value_cmp cmp_type; typedef typename base::block_type block_type; public: //! Creates a runs merger object. //! \param sruns input sorted runs object //! \param cmp comparison object //! \param memory_to_use amount of memory available for the merger in bytes runs_merger(sorted_runs_type& sruns, value_cmp cmp, unsigned_type memory_to_use) : base(cmp, memory_to_use) { this->initialize(sruns); } //! Creates a runs merger object without initializing a round of sorted_runs. //! \param cmp comparison object //! \param memory_to_use amount of memory available for the merger in bytes runs_merger(value_cmp cmp, unsigned_type memory_to_use) : base(cmp, memory_to_use) { } }; //////////////////////////////////////////////////////////////////////// // SORT // //////////////////////////////////////////////////////////////////////// //! Produces sorted stream from input stream. //! //! \tparam Input type of the input stream //! \tparam CompareType type of comparison object used for sorting the runs //! \tparam BlockSize size of blocks used to store the runs //! \tparam AllocStr functor that defines allocation strategy for the runs //! \remark Implemented as the composition of \c runs_creator and \c runs_merger . template < class Input, class CompareType, unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(typename Input::value_type), class AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY, class RunsCreatorType = runs_creator > class sort : public noncopyable { typedef RunsCreatorType runs_creator_type; typedef typename runs_creator_type::sorted_runs_type sorted_runs_type; typedef runs_merger runs_merger_type; runs_creator_type creator; runs_merger_type merger; public: //! Standard stream typedef. typedef typename Input::value_type value_type; //! Creates the object. //! \param in input stream //! \param c comparator object //! \param memory_to_use memory amount that is allowed to used by the sorter in bytes sort(Input& in, CompareType c, unsigned_type memory_to_use) : creator(in, c, memory_to_use), merger(creator.result(), c, memory_to_use) { sort_helper::verify_sentinel_strict_weak_ordering(c); } //! Creates the object. //! \param in input stream //! \param c comparator object //! \param m_memory_to_userc memory amount that is allowed to used by the runs creator in bytes //! \param m_memory_to_use memory amount that is allowed to used by the merger in bytes sort(Input& in, CompareType c, unsigned_type m_memory_to_userc, unsigned_type m_memory_to_use) : creator(in, c, m_memory_to_userc), merger(creator.result(), c, m_memory_to_use) { sort_helper::verify_sentinel_strict_weak_ordering(c); } //! Standard stream method. bool empty() const { return merger.empty(); } //! Standard stream method. const value_type& operator * () const { assert(!empty()); return *merger; } const value_type* operator -> () const { assert(!empty()); return merger.operator -> (); } //! Standard stream method. sort& operator ++ () { ++merger; return *this; } }; //! Computes sorted runs type from value type and block size. //! //! \tparam ValueType type of values ins sorted runs //! \tparam BlockSize size of blocks where sorted runs stored template < class ValueType, unsigned BlockSize > class compute_sorted_runs_type { typedef ValueType value_type; typedef BID bid_type; typedef sort_helper::trigger_entry trigger_entry_type; public: typedef sorted_runs > result; }; //! \} } // namespace stream //! \addtogroup stlalgo //! \{ //! Sorts range of any random access iterators externally. //! //! \param begin iterator pointing to the first element of the range //! \param end iterator pointing to the last+1 element of the range //! \param cmp comparison object //! \param MemSize memory to use for sorting (in bytes) //! \param AS allocation strategy //! //! The \c BlockSize template parameter defines the block size to use (in bytes) //! \warning Slower than External Iterator Sort template < unsigned BlockSize, class RandomAccessIterator, class CmpType, class AllocStr > void sort(RandomAccessIterator begin, RandomAccessIterator end, CmpType cmp, unsigned_type MemSize, AllocStr AS) { STXXL_UNUSED(AS); typedef typename stream::streamify_traits::stream_type InputType; InputType Input(begin, end); typedef stream::sort sorter_type; sorter_type Sort(Input, cmp, MemSize); stream::materialize(Sort, begin); } //! \} STXXL_END_NAMESPACE #endif // !STXXL_STREAM_SORT_STREAM_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/stream/stream.h000644 001411 000144 00000124665 12422212055 021213 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/stream/stream.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003-2005 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * Copyright (C) 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_STREAM_STREAM_HEADER #define STXXL_STREAM_STREAM_HEADER #include #include #include #include #include #include #include #ifndef STXXL_VERBOSE_MATERIALIZE #define STXXL_VERBOSE_MATERIALIZE STXXL_VERBOSE3 #endif STXXL_BEGIN_NAMESPACE //! Stream package subnamespace. namespace stream { //! \addtogroup streampack //! \{ //////////////////////////////////////////////////////////////////////// // STREAMIFY // //////////////////////////////////////////////////////////////////////// //! A model of stream that retrieves the data from an input iterator. //! For convenience use \c streamify function instead of direct instantiation //! of \c iterator2stream . template class iterator2stream { InputIterator m_current, m_end; public: //! Standard stream typedef. typedef typename std::iterator_traits::value_type value_type; iterator2stream(InputIterator begin, InputIterator end) : m_current(begin), m_end(end) { } iterator2stream(const iterator2stream& a) : m_current(a.m_current), m_end(a.m_end) { } //! Standard stream method. const value_type& operator * () const { return *m_current; } const value_type* operator -> () const { return &(*m_current); } //! Standard stream method. iterator2stream& operator ++ () { assert(m_end != m_current); ++m_current; return *this; } //! Standard stream method. bool empty() const { return (m_current == m_end); } }; //! Input iterator range to stream converter. //! \param begin iterator, pointing to the first value //! \param end iterator, pointing to the last + 1 position, i.e. beyond the range //! \return an instance of a stream object template iterator2stream streamify(InputIterator begin, InputIterator end) { return iterator2stream(begin, end); } //! Traits class of \c streamify function. template struct streamify_traits { //! return type (stream type) of \c streamify for \c InputIterator. typedef iterator2stream stream_type; }; //! A model of stream that retrieves data from an external \c stxxl::vector //! iterator. It is more efficient than generic \c iterator2stream thanks to //! use of overlapping For convenience use \c streamify function instead of //! direct instantiation of \c vector_iterator2stream . template class vector_iterator2stream { InputIterator m_current, m_end; typedef buf_istream buf_istream_type; typedef typename stxxl::compat_unique_ptr::result buf_istream_unique_ptr_type; mutable buf_istream_unique_ptr_type in; void delete_stream() { in.reset(); // delete object } public: typedef vector_iterator2stream self_type; //! Standard stream typedef. typedef typename std::iterator_traits::value_type value_type; vector_iterator2stream(InputIterator begin, InputIterator end, unsigned_type nbuffers = 0) : m_current(begin), m_end(end), in(static_cast(NULL)) { if (empty()) return; begin.flush(); // flush container typename InputIterator::bids_container_iterator end_iter = end.bid() + ((end.block_offset()) ? 1 : 0); if (end_iter - begin.bid() > 0) { in.reset(new buf_istream_type( begin.bid(), end_iter, nbuffers ? nbuffers : (2 * config::get_instance()->disks_number()) ) ); InputIterator cur = begin - begin.block_offset(); // skip the beginning of the block for ( ; cur != begin; ++cur) ++(*in); } } vector_iterator2stream(const self_type& a) : m_current(a.m_current), m_end(a.m_end), in(a.in.release()) { } //! Standard stream method. const value_type& operator * () const { return **in; } const value_type* operator -> () const { return &(**in); } //! Standard stream method. self_type& operator ++ () { assert(m_end != m_current); ++m_current; ++(*in); if (UNLIKELY(empty())) delete_stream(); return *this; } //! Standard stream method. bool empty() const { return (m_current == m_end); } virtual ~vector_iterator2stream() { delete_stream(); // not needed actually } }; //! Input external \c stxxl::vector iterator range to stream converter. //! It is more efficient than generic input iterator \c streamify thanks to use of overlapping //! \param begin iterator, pointing to the first value //! \param end iterator, pointing to the last + 1 position, i.e. beyond the range //! \param nbuffers number of blocks used for overlapped reading (0 is default, //! which equals to (2 * number_of_disks) //! \return an instance of a stream object template vector_iterator2stream< stxxl::vector_iterator > streamify( stxxl::vector_iterator begin, stxxl::vector_iterator end, unsigned_type nbuffers = 0) { STXXL_VERBOSE1("streamify for vector_iterator range is called"); return vector_iterator2stream< stxxl::vector_iterator >(begin, end, nbuffers); } template struct streamify_traits< stxxl::vector_iterator > { typedef vector_iterator2stream< stxxl::vector_iterator > stream_type; }; //! Input external \c stxxl::vector const iterator range to stream converter. //! It is more efficient than generic input iterator \c streamify thanks to use of overlapping //! \param begin const iterator, pointing to the first value //! \param end const iterator, pointing to the last + 1 position, i.e. beyond the range //! \param nbuffers number of blocks used for overlapped reading (0 is default, //! which equals to (2 * number_of_disks) //! \return an instance of a stream object template vector_iterator2stream< stxxl::const_vector_iterator > streamify( stxxl::const_vector_iterator begin, stxxl::const_vector_iterator end, unsigned_type nbuffers = 0) { STXXL_VERBOSE1("streamify for const_vector_iterator range is called"); return vector_iterator2stream< stxxl::const_vector_iterator >(begin, end, nbuffers); } template struct streamify_traits< stxxl::const_vector_iterator > { typedef vector_iterator2stream< stxxl::const_vector_iterator > stream_type; }; //! Version of \c iterator2stream. Switches between \c vector_iterator2stream and \c iterator2stream . //! //! small range switches between //! \c vector_iterator2stream and \c iterator2stream . //! iterator2stream is chosen if the input iterator range //! is small ( < B ) template class vector_iterator2stream_sr { vector_iterator2stream* vec_it_stream; iterator2stream* it_stream; typedef typename InputIterator::block_type block_type; public: typedef vector_iterator2stream_sr self_type; //! Standard stream typedef. typedef typename std::iterator_traits::value_type value_type; vector_iterator2stream_sr(InputIterator begin, InputIterator end, unsigned_type nbuffers = 0) { if (end - begin < block_type::size) { STXXL_VERBOSE1("vector_iterator2stream_sr::vector_iterator2stream_sr: Choosing iterator2stream"); it_stream = new iterator2stream(begin, end); vec_it_stream = NULL; } else { STXXL_VERBOSE1("vector_iterator2stream_sr::vector_iterator2stream_sr: Choosing vector_iterator2stream"); it_stream = NULL; vec_it_stream = new vector_iterator2stream(begin, end, nbuffers); } } vector_iterator2stream_sr(const self_type& a) : vec_it_stream(a.vec_it_stream), it_stream(a.it_stream) { } //! Standard stream method. const value_type& operator * () const { if (it_stream) return **it_stream; return **vec_it_stream; } const value_type* operator -> () const { if (it_stream) return &(**it_stream); return &(**vec_it_stream); } //! Standard stream method. self_type& operator ++ () { if (it_stream) ++(*it_stream); else ++(*vec_it_stream); return *this; } //! Standard stream method. bool empty() const { if (it_stream) return it_stream->empty(); return vec_it_stream->empty(); } virtual ~vector_iterator2stream_sr() { if (it_stream) delete it_stream; else delete vec_it_stream; } }; //! Version of \c streamify. Switches from \c vector_iterator2stream to \c //! iterator2stream for small ranges. template vector_iterator2stream_sr< stxxl::vector_iterator > streamify_sr( stxxl::vector_iterator begin, stxxl::vector_iterator end, unsigned_type nbuffers = 0) { STXXL_VERBOSE1("streamify_sr for vector_iterator range is called"); return vector_iterator2stream_sr< stxxl::vector_iterator >(begin, end, nbuffers); } //! Version of \c streamify. Switches from \c vector_iterator2stream to \c //! iterator2stream for small ranges. template vector_iterator2stream_sr< stxxl::const_vector_iterator > streamify_sr( stxxl::const_vector_iterator begin, stxxl::const_vector_iterator end, unsigned_type nbuffers = 0) { STXXL_VERBOSE1("streamify_sr for const_vector_iterator range is called"); return vector_iterator2stream_sr< stxxl::const_vector_iterator >(begin, end, nbuffers); } //////////////////////////////////////////////////////////////////////// // MATERIALIZE // //////////////////////////////////////////////////////////////////////// //! Stores consecutively stream content to an output iterator. //! \param in stream to be stored used as source //! \param out output iterator used as destination //! \return value of the output iterator after all increments, //! i.e. points to the first unwritten value //! \pre Output (range) is large enough to hold the all elements in the input stream template OutputIterator materialize(StreamAlgorithm& in, OutputIterator out) { STXXL_VERBOSE_MATERIALIZE(STXXL_PRETTY_FUNCTION_NAME); while (!in.empty()) { *out = *in; ++out; ++in; } return out; } //! Stores consecutively stream content to an output iterator range \b until end of the stream or end of the iterator range is reached. //! \param in stream to be stored used as source //! \param outbegin output iterator used as destination //! \param outend output end iterator, pointing beyond the output range //! \return value of the output iterator after all increments, //! i.e. points to the first unwritten value //! \pre Output range is large enough to hold the all elements in the input stream //! //! This function is useful when you do not know the length of the stream beforehand. template OutputIterator materialize(StreamAlgorithm& in, OutputIterator outbegin, OutputIterator outend) { STXXL_VERBOSE_MATERIALIZE(STXXL_PRETTY_FUNCTION_NAME); while ((!in.empty()) && outend != outbegin) { *outbegin = *in; ++outbegin; ++in; } return outbegin; } //! Stores consecutively stream content to an output \c stxxl::vector iterator \b until end of the stream or end of the iterator range is reached. //! \param in stream to be stored used as source //! \param outbegin output \c stxxl::vector iterator used as destination //! \param outend output end iterator, pointing beyond the output range //! \param nbuffers number of blocks used for overlapped writing (0 is default, //! which equals to (2 * number_of_disks) //! \return value of the output iterator after all increments, //! i.e. points to the first unwritten value //! \pre Output range is large enough to hold the all elements in the input stream //! //! This function is useful when you do not know the length of the stream beforehand. template stxxl::vector_iterator materialize(StreamAlgorithm& in, stxxl::vector_iterator outbegin, stxxl::vector_iterator outend, unsigned_type nbuffers = 0) { STXXL_VERBOSE_MATERIALIZE(STXXL_PRETTY_FUNCTION_NAME); typedef stxxl::vector_iterator ExtIterator; typedef stxxl::const_vector_iterator ConstExtIterator; typedef buf_ostream buf_ostream_type; while (outbegin.block_offset()) // go to the beginning of the block // of the external vector { if (in.empty() || outbegin == outend) return outbegin; *outbegin = *in; ++outbegin; ++in; } if (nbuffers == 0) nbuffers = 2 * config::get_instance()->disks_number(); outbegin.flush(); // flush container // create buffered write stream for blocks buf_ostream_type outstream(outbegin.bid(), nbuffers); assert(outbegin.block_offset() == 0); // delay calling block_externally_updated() until the block is // completely filled (and written out) in outstream ConstExtIterator prev_block = outbegin; while (!in.empty() && outend != outbegin) { if (outbegin.block_offset() == 0) { if (prev_block != outbegin) { prev_block.block_externally_updated(); prev_block = outbegin; } } *outstream = *in; ++outbegin; ++outstream; ++in; } ConstExtIterator const_out = outbegin; while (const_out.block_offset()) // filling the rest of the block { *outstream = *const_out; ++const_out; ++outstream; } if (prev_block != outbegin) prev_block.block_externally_updated(); outbegin.flush(); return outbegin; } //! Stores consecutively stream content to an output \c stxxl::vector iterator. //! \param in stream to be stored used as source //! \param out output \c stxxl::vector iterator used as destination //! \param nbuffers number of blocks used for overlapped writing (0 is default, //! which equals to (2 * number_of_disks) //! \return value of the output iterator after all increments, //! i.e. points to the first unwritten value //! \pre Output (range) is large enough to hold the all elements in the input stream template stxxl::vector_iterator materialize(StreamAlgorithm& in, stxxl::vector_iterator out, unsigned_type nbuffers = 0) { STXXL_VERBOSE_MATERIALIZE(STXXL_PRETTY_FUNCTION_NAME); typedef stxxl::vector_iterator ExtIterator; typedef stxxl::const_vector_iterator ConstExtIterator; typedef buf_ostream buf_ostream_type; // on the I/O complexity of "materialize": // crossing block boundary causes O(1) I/Os // if you stay in a block, then materialize function accesses only the cache of the // vector (only one block indeed), amortized complexity should apply here while (out.block_offset()) // go to the beginning of the block // of the external vector { if (in.empty()) return out; *out = *in; ++out; ++in; } if (nbuffers == 0) nbuffers = 2 * config::get_instance()->disks_number(); out.flush(); // flush container // create buffered write stream for blocks buf_ostream_type outstream(out.bid(), nbuffers); assert(out.block_offset() == 0); // delay calling block_externally_updated() until the block is // completely filled (and written out) in outstream ConstExtIterator prev_block = out; while (!in.empty()) { if (out.block_offset() == 0) { if (prev_block != out) { prev_block.block_externally_updated(); prev_block = out; } } // tells the vector that the block was modified *outstream = *in; ++out; ++outstream; ++in; } ConstExtIterator const_out = out; // copy over items remaining in block from vector. while (const_out.block_offset()) { *outstream = *const_out; // might cause I/Os for loading the page that ++const_out; // contains data beyond out ++outstream; } if (prev_block != out) prev_block.block_externally_updated(); out.flush(); return out; } //! Reads stream content and discards it. //! Useful where you do not need the processed stream anymore, //! but are just interested in side effects, or just for debugging. //! \param in input stream template void discard(StreamAlgorithm& in) { while (!in.empty()) { *in; ++in; } } //////////////////////////////////////////////////////////////////////// // GENERATE // //////////////////////////////////////////////////////////////////////// //! A model of stream that outputs data from an adaptable generator functor. //! For convenience use \c streamify function instead of direct instantiation //! of \c generator2stream . template class generator2stream { public: //! Standard stream typedef. typedef T value_type; private: Generator gen_; value_type m_current; public: generator2stream(Generator g) : gen_(g), m_current(gen_()) { } generator2stream(const generator2stream& a) : gen_(a.gen_), m_current(a.m_current) { } //! Standard stream method. const value_type& operator * () const { return m_current; } const value_type* operator -> () const { return &m_current; } //! Standard stream method. generator2stream& operator ++ () { m_current = gen_(); return *this; } //! Standard stream method. bool empty() const { return false; } }; //! Adaptable generator to stream converter. //! \param gen_ generator object //! \return an instance of a stream object template generator2stream streamify(Generator gen_) { return generator2stream(gen_); } //////////////////////////////////////////////////////////////////////// // TRANSFORM // //////////////////////////////////////////////////////////////////////// struct Stopper { }; //! Processes (up to) 6 input streams using given operation functor. //! //! \tparam Operation type of the operation (type of an //! adaptable functor that takes 6 parameters) //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \tparam Input4 type of the 4th input //! \tparam Input5 type of the 5th input //! \tparam Input6 type of the 6th input template class transform { Operation& op; Input1& i1; Input2& i2; Input3& i3; Input4& i4; Input5& i5; Input6& i6; public: //! Standard stream typedef. typedef typename Operation::value_type value_type; private: value_type current; public: //! Construction. transform(Operation& o, Input1& i1_, Input2& i2_, Input3& i3_, Input4& i4_, Input5& i5_, Input5& i6_) : op(o), i1(i1_), i2(i2_), i3(i3_), i4(i4_), i5(i5_), i6(i6_) { if (!empty()) current = op(*i1, *i2, *i3, *i4, *i5, *i6); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. transform& operator ++ () { ++i1; ++i2; ++i3; ++i4; ++i5; ++i6; if (!empty()) current = op(*i1, *i2, *i3, *i4, *i5, *i6); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty() || i4.empty() || i5.empty() || i6.empty(); } }; // Specializations //////////////////////////////////////////////////////////////////////// // TRANSFORM (1 input stream) // //////////////////////////////////////////////////////////////////////// //! Processes an input stream using given operation functor. //! //! \tparam Operation type of the operation (type of an //! adaptable functor that takes 1 parameter) //! \tparam Input1 type of the input //! \remark This is a specialization of \c transform . template class transform { Operation& op; Input1& i1; public: //! Standard stream typedef. typedef typename Operation::value_type value_type; private: value_type current; public: //! Construction. transform(Operation& o, Input1& i1_) : op(o), i1(i1_) { if (!empty()) current = op(*i1); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. transform& operator ++ () { ++i1; if (!empty()) current = op(*i1); return *this; } //! Standard stream method. bool empty() const { return i1.empty(); } }; //////////////////////////////////////////////////////////////////////// // TRANSFORM (2 input streams) // //////////////////////////////////////////////////////////////////////// //! Processes 2 input streams using given operation functor. //! //! \tparam Operation type of the operation (type of an //! adaptable functor that takes 2 parameters) //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \remark This is a specialization of \c transform . template class transform { Operation& op; Input1& i1; Input2& i2; public: //! Standard stream typedef. typedef typename Operation::value_type value_type; private: value_type current; public: //! Construction. transform(Operation& o, Input1& i1_, Input2& i2_) : op(o), i1(i1_), i2(i2_) { if (!empty()) current = op(*i1, *i2); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. transform& operator ++ () { ++i1; ++i2; if (!empty()) current = op(*i1, *i2); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty(); } }; //////////////////////////////////////////////////////////////////////// // TRANSFORM (3 input streams) // //////////////////////////////////////////////////////////////////////// //! Processes 3 input streams using given operation functor. //! //! \tparam Operation type of the operation (type of an //! adaptable functor that takes 3 parameters) //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \remark This is a specialization of \c transform . template class transform { Operation& op; Input1& i1; Input2& i2; Input3& i3; public: //! Standard stream typedef. typedef typename Operation::value_type value_type; private: value_type current; public: //! Construction. transform(Operation& o, Input1& i1_, Input2& i2_, Input3& i3_) : op(o), i1(i1_), i2(i2_), i3(i3_) { if (!empty()) current = op(*i1, *i2, *i3); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. transform& operator ++ () { ++i1; ++i2; ++i3; if (!empty()) current = op(*i1, *i2, *i3); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty(); } }; //////////////////////////////////////////////////////////////////////// // TRANSFORM (4 input streams) // //////////////////////////////////////////////////////////////////////// //! Processes 4 input streams using given operation functor. //! //! \tparam Operation type of the operation (type of an //! adaptable functor that takes 4 parameters) //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \tparam Input4 type of the 4th input //! \remark This is a specialization of \c transform . template class transform { Operation& op; Input1& i1; Input2& i2; Input3& i3; Input4& i4; public: //! Standard stream typedef. typedef typename Operation::value_type value_type; private: value_type current; public: //! Construction. transform(Operation& o, Input1& i1_, Input2& i2_, Input3& i3_, Input4& i4_) : op(o), i1(i1_), i2(i2_), i3(i3_), i4(i4_) { if (!empty()) current = op(*i1, *i2, *i3, *i4); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. transform& operator ++ () { ++i1; ++i2; ++i3; ++i4; if (!empty()) current = op(*i1, *i2, *i3, *i4); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty() || i4.empty(); } }; //////////////////////////////////////////////////////////////////////// // TRANSFORM (5 input streams) // //////////////////////////////////////////////////////////////////////// //! Processes 5 input streams using given operation functor. //! //! \tparam Operation type of the operation (type of an //! adaptable functor that takes 5 parameters) //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \tparam Input4 type of the 4th input //! \tparam Input5 type of the 5th input //! \remark This is a specialization of \c transform . template class transform { Operation& op; Input1& i1; Input2& i2; Input3& i3; Input4& i4; Input5& i5; public: //! Standard stream typedef. typedef typename Operation::value_type value_type; private: value_type current; public: //! Construction. transform(Operation& o, Input1& i1_, Input2& i2_, Input3& i3_, Input4& i4_, Input5& i5_) : op(o), i1(i1_), i2(i2_), i3(i3_), i4(i4_), i5(i5_) { if (!empty()) current = op(*i1, *i2, *i3, *i4, *i5); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. transform& operator ++ () { ++i1; ++i2; ++i3; ++i4; ++i5; if (!empty()) current = op(*i1, *i2, *i3, *i4, *i5); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty() || i4.empty() || i5.empty(); } }; //////////////////////////////////////////////////////////////////////// // MAKE TUPLE // //////////////////////////////////////////////////////////////////////// //! Creates stream of 6-tuples from 6 input streams. //! //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \tparam Input4 type of the 4th input //! \tparam Input5 type of the 5th input //! \tparam Input6 type of the 6th input template class make_tuple { Input1& i1; Input2& i2; Input3& i3; Input4& i4; Input5& i5; Input6& i6; public: //! Standard stream typedef. typedef typename stxxl::tuple< typename Input1::value_type, typename Input2::value_type, typename Input3::value_type, typename Input4::value_type, typename Input5::value_type, typename Input6::value_type > value_type; private: value_type current; public: //! Construction. make_tuple(Input1& i1_, Input2& i2_, Input3& i3_, Input4& i4_, Input5& i5_, Input6& i6_) : i1(i1_), i2(i2_), i3(i3_), i4(i4_), i5(i5_), i6(i6_) { if (!empty()) current = value_type(*i1, *i2, *i3, *i4, *i5, *i6); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. make_tuple& operator ++ () { ++i1; ++i2; ++i3; ++i4; ++i5; ++i6; if (!empty()) current = value_type(*i1, *i2, *i3, *i4, *i5, *i6); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty() || i4.empty() || i5.empty() || i6.empty(); } }; //! Creates stream of 2-tuples (pairs) from 2 input streams. //! //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \remark A specialization of \c make_tuple . template class make_tuple { Input1& i1; Input2& i2; public: //! Standard stream typedef. typedef typename stxxl::tuple< typename Input1::value_type, typename Input2::value_type > value_type; private: value_type current; public: //! Construction. make_tuple(Input1& i1_, Input2& i2_) : i1(i1_), i2(i2_) { if (!empty()) current = value_type(*i1, *i2); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. make_tuple& operator ++ () { ++i1; ++i2; if (!empty()) current = value_type(*i1, *i2); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty(); } }; //! Creates stream of 3-tuples from 3 input streams. //! //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \remark A specialization of \c make_tuple . template class make_tuple { Input1& i1; Input2& i2; Input3& i3; public: //! Standard stream typedef. typedef typename stxxl::tuple< typename Input1::value_type, typename Input2::value_type, typename Input3::value_type > value_type; private: value_type current; public: //! Construction. make_tuple(Input1& i1_, Input2& i2_, Input3& i3_) : i1(i1_), i2(i2_), i3(i3_) { if (!empty()) current = value_type(*i1, *i2, *i3); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. make_tuple& operator ++ () { ++i1; ++i2; ++i3; if (!empty()) current = value_type(*i1, *i2, *i3); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty(); } }; //! Creates stream of 4-tuples from 4 input streams. //! //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \tparam Input4 type of the 4th input //! \remark A specialization of \c make_tuple . template class make_tuple { Input1& i1; Input2& i2; Input3& i3; Input4& i4; public: //! Standard stream typedef. typedef typename stxxl::tuple< typename Input1::value_type, typename Input2::value_type, typename Input3::value_type, typename Input4::value_type > value_type; private: value_type current; public: //! Construction. make_tuple(Input1& i1_, Input2& i2_, Input3& i3_, Input4& i4_) : i1(i1_), i2(i2_), i3(i3_), i4(i4_) { if (!empty()) current = value_type(*i1, *i2, *i3, *i4); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. make_tuple& operator ++ () { ++i1; ++i2; ++i3; ++i4; if (!empty()) current = value_type(*i1, *i2, *i3, *i4); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty() || i4.empty(); } }; //! Creates stream of 5-tuples from 5 input streams. //! //! \tparam Input1 type of the 1st input //! \tparam Input2 type of the 2nd input //! \tparam Input3 type of the 3rd input //! \tparam Input4 type of the 4th input //! \tparam Input5 type of the 5th input //! \remark A specialization of \c make_tuple . template < class Input1, class Input2, class Input3, class Input4, class Input5 > class make_tuple { Input1& i1; Input2& i2; Input3& i3; Input4& i4; Input5& i5; public: //! Standard stream typedef. typedef typename stxxl::tuple< typename Input1::value_type, typename Input2::value_type, typename Input3::value_type, typename Input4::value_type, typename Input5::value_type > value_type; private: value_type current; public: //! Construction. make_tuple(Input1& i1_, Input2& i2_, Input3& i3_, Input4& i4_, Input5& i5_) : i1(i1_), i2(i2_), i3(i3_), i4(i4_), i5(i5_) { if (!empty()) current = value_type(*i1, *i2, *i3, *i4, *i5); } //! Standard stream method. const value_type& operator * () const { return current; } const value_type* operator -> () const { return ¤t; } //! Standard stream method. make_tuple& operator ++ () { ++i1; ++i2; ++i3; ++i4; ++i5; if (!empty()) current = value_type(*i1, *i2, *i3, *i4, *i5); return *this; } //! Standard stream method. bool empty() const { return i1.empty() || i2.empty() || i3.empty() || i4.empty() || i5.empty(); } }; //! \} } // namespace stream STXXL_END_NAMESPACE #include #include #endif // !STXXL_STREAM_STREAM_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/stream/choose.h000644 001411 000144 00000014654 12411366426 021206 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/stream/choose.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003-2005 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_STREAM_CHOOSE_HEADER #define STXXL_STREAM_CHOOSE_HEADER #include STXXL_BEGIN_NAMESPACE //! Stream package subnamespace. namespace stream { //////////////////////////////////////////////////////////////////////// // CHOOSE // //////////////////////////////////////////////////////////////////////// template class choose { }; //! Creates stream from a tuple stream taking the first component of each tuple. //! //! \tparam Input type of the input tuple stream //! //! \remark Tuple stream is a stream which \c value_type is \c stxxl::tuple . template class choose { Input& in; typedef typename Input::value_type tuple_type; public: //! Standard stream typedef. typedef typename tuple_type::first_type value_type; //! Construction. choose(Input& in_) : in(in_) { } //! Standard stream method. const value_type& operator * () const { return (*in).first; } const value_type* operator -> () const { return &(*in).first; } //! Standard stream method. choose& operator ++ () { ++in; return *this; } //! Standard stream method. bool empty() const { return in.empty(); } }; //! Creates stream from a tuple stream taking the second component of each tuple. //! //! \tparam Input type of the input tuple stream //! //! \remark Tuple stream is a stream which \c value_type is \c stxxl::tuple . template class choose { Input& in; typedef typename Input::value_type tuple_type; public: //! Standard stream typedef. typedef typename tuple_type::second_type value_type; //! Construction. choose(Input& in_) : in(in_) { } //! Standard stream method. const value_type& operator * () const { return (*in).second; } const value_type* operator -> () const { return &(*in).second; } //! Standard stream method. choose& operator ++ () { ++in; return *this; } //! Standard stream method. bool empty() const { return in.empty(); } }; //! Creates stream from a tuple stream taking the third component of each tuple. //! //! \tparam Input type of the input tuple stream //! //! \remark Tuple stream is a stream which \c value_type is \c stxxl::tuple . template class choose { Input& in; typedef typename Input::value_type tuple_type; public: //! Standard stream typedef. typedef typename tuple_type::third_type value_type; //! Construction. choose(Input& in_) : in(in_) { } //! Standard stream method. const value_type& operator * () const { return (*in).third; } const value_type* operator -> () const { return &(*in).third; } //! Standard stream method. choose& operator ++ () { ++in; return *this; } //! Standard stream method. bool empty() const { return in.empty(); } }; //! Creates stream from a tuple stream taking the fourth component of each tuple. //! //! \tparam Input type of the input tuple stream //! //! \remark Tuple stream is a stream which \c value_type is \c stxxl::tuple . template class choose { Input& in; typedef typename Input::value_type tuple_type; public: //! Standard stream typedef. typedef typename tuple_type::fourth_type value_type; //! Construction. choose(Input& in_) : in(in_) { } //! Standard stream method. const value_type& operator * () const { return (*in).fourth; } const value_type* operator -> () const { return &(*in).fourth; } //! Standard stream method. choose& operator ++ () { ++in; return *this; } //! Standard stream method. bool empty() const { return in.empty(); } }; //! Creates stream from a tuple stream taking the fifth component of each tuple. //! //! \tparam Input type of the input tuple stream //! //! \remark Tuple stream is a stream which \c value_type is \c stxxl::tuple . template class choose { Input& in; typedef typename Input::value_type tuple_type; public: //! Standard stream typedef. typedef typename tuple_type::fifth_type value_type; //! Construction. choose(Input& in_) : in(in_) { } //! Standard stream method. const value_type& operator * () const { return (*in).fifth; } const value_type* operator -> () const { return &(*in).fifth; } //! Standard stream method. choose& operator ++ () { ++in; return *this; } //! Standard stream method. bool empty() const { return in.empty(); } }; //! Creates stream from a tuple stream taking the sixth component of each tuple. //! //! \tparam Input type of the input tuple stream //! //! \remark Tuple stream is a stream which \c value_type is \c stxxl::tuple . template class choose { Input& in; typedef typename Input::value_type tuple_type; public: //! Standard stream typedef. typedef typename tuple_type::sixth_type value_type; //! Construction. choose(Input& in_) : in(in_) { } //! Standard stream method. const value_type& operator * () const { return (*in).sixth; } const value_type* operator -> () const { return &(*in).sixth; } //! Standard stream method. choose& operator ++ () { ++in; return *this; } //! Standard stream method. bool empty() const { return in.empty(); } }; //! \} } // namespace stream STXXL_END_NAMESPACE #include #endif // !STXXL_STREAM_CHOOSE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/stream/sorted_runs.h000644 001411 000144 00000007605 12411366426 022273 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/stream/sorted_runs.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2005 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_STREAM_SORTED_RUNS_HEADER #define STXXL_STREAM_SORTED_RUNS_HEADER #include #include #include #include #include STXXL_BEGIN_NAMESPACE namespace stream { //! \addtogroup streampack Stream Package //! \{ //////////////////////////////////////////////////////////////////////// // SORTED RUNS // //////////////////////////////////////////////////////////////////////// //! All sorted runs of a sort operation. template struct sorted_runs : private noncopyable, public counted_object { typedef TriggerEntryType trigger_entry_type; typedef typename trigger_entry_type::block_type block_type; //! may differ from trigger_entry_type::value_type typedef typename block_type::value_type value_type; typedef std::vector run_type; typedef std::vector small_run_type; typedef stxxl::external_size_type size_type; typedef typename std::vector::size_type run_index_type; typedef CompareType cmp_type; //! total number of elements in all runs size_type elements; //! vector of runs (containing vectors of block ids) std::vector runs; //! vector of the number of elements in each individual run std::vector runs_sizes; //! Small sort optimization: // if the input is small such that its total size is at most B // (block_type::size) then input is sorted internally and kept in the // array "small_run" small_run_type small_run; public: sorted_runs() : elements(0) { } ~sorted_runs() { deallocate_blocks(); } //! Clear the internal state of the object: release all runs and reset. void clear() { deallocate_blocks(); elements = 0; runs.clear(); runs_sizes.clear(); small_run.clear(); } //! Add a new run with given number of elements void add_run(const run_type& run, size_type run_size) { runs.push_back(run); runs_sizes.push_back(run_size); elements += run_size; } //! Swap contents with another object. This is used by the recursive //! merger to swap in a sorted_runs object with fewer runs. void swap(sorted_runs& b) { std::swap(elements, b.elements); std::swap(runs, b.runs); std::swap(runs_sizes, b.runs_sizes); std::swap(small_run, b.small_run); } private: //! Deallocates the blocks which the runs occupy. //! //! \remark There is no need in calling this method, the blocks are //! deallocated by the destructor. However, if you wish to reuse the //! object, then this function can be used to clear its state. void deallocate_blocks() { block_manager* bm = block_manager::get_instance(); for (unsigned_type i = 0; i < runs.size(); ++i) { bm->delete_blocks(make_bid_iterator(runs[i].begin()), make_bid_iterator(runs[i].end())); } } }; //! \} } // namespace stream STXXL_END_NAMESPACE #endif // !STXXL_STREAM_SORTED_RUNS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/syscall_file.h000644 001411 000144 00000003464 12405375303 021505 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/syscall_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_SYSCALL_FILE_HEADER #define STXXL_IO_SYSCALL_FILE_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Implementation of file based on UNIX syscalls. class syscall_file : public ufs_file_base, public disk_queued_file { public: //! Constructs file object. //! \param filename path of file //! \param mode open mode, see \c stxxl::file::open_modes //! \param queue_id disk queue identifier //! \param allocator_id linked disk_allocator //! \param device_id physical device identifier syscall_file( const std::string& filename, int mode, int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID) : file(device_id), ufs_file_base(filename, mode), disk_queued_file(queue_id, allocator_id) { } void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_SYSCALL_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/io/request.h000644 001411 000144 00000006111 12405375303 020514 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2013-2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_HEADER #define STXXL_IO_REQUEST_HEADER #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ #define STXXL_BLOCK_ALIGN 4096 class file; //! Request object encapsulating basic properties like file and offset. class request : virtual public request_interface, public atomic_counted_object { friend class linuxaio_queue; protected: completion_handler m_on_complete; compat_unique_ptr::result m_error; protected: file* m_file; void* m_buffer; offset_type m_offset; size_type m_bytes; request_type m_type; public: request(const completion_handler& on_compl, file* file, void* buffer, offset_type offset, size_type bytes, request_type type); virtual ~request(); file * get_file() const { return m_file; } void * get_buffer() const { return m_buffer; } offset_type get_offset() const { return m_offset; } size_type get_size() const { return m_bytes; } request_type get_type() const { return m_type; } void check_alignment() const; std::ostream & print(std::ostream& out) const; //! Inform the request object that an error occurred during the I/O //! execution. void error_occured(const char* msg) { m_error.reset(new stxxl::io_error(msg)); } //! Inform the request object that an error occurred during the I/O //! execution. void error_occured(const std::string& msg) { m_error.reset(new stxxl::io_error(msg)); } //! Rises an exception if there were error with the I/O. void check_errors() { if (m_error.get()) throw *(m_error.get()); } virtual const char * io_type() const; protected: void check_nref(bool after = false) { if (get_reference_count() < 2) check_nref_failed(after); } private: void check_nref_failed(bool after); }; inline std::ostream& operator << (std::ostream& out, const request& req) { return req.print(out); } //! A reference counting pointer for \c request. typedef counting_ptr request_ptr; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/request_queue_impl_worker.h000644 001411 000144 00000003755 12405375303 024345 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_queue_impl_worker.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_QUEUE_IMPL_WORKER_HEADER #define STXXL_IO_REQUEST_QUEUE_IMPL_WORKER_HEADER #include #if STXXL_STD_THREADS #include #elif STXXL_BOOST_THREADS #include #elif STXXL_POSIX_THREADS #include #else #error "Thread implementation not detected." #endif #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Implementation of request queue worker threads. Worker threads can be //! started by start_thread and stopped with stop_thread. The queue state is //! checked before termination and updated afterwards. class request_queue_impl_worker : public request_queue { protected: enum thread_state { NOT_RUNNING, RUNNING, TERMINATING, TERMINATED }; #if STXXL_STD_THREADS typedef std::thread* thread_type; #elif STXXL_BOOST_THREADS typedef boost::thread* thread_type; #else typedef pthread_t thread_type; #endif protected: void start_thread(void* (* worker)(void*), void* arg, thread_type& t, state& s); void stop_thread(thread_type& t, state& s, semaphore& sem); }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_QUEUE_IMPL_WORKER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/create_file.h000644 001411 000144 00000002463 12405153574 021300 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/create_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_CREATE_FILE_HEADER #define STXXL_IO_CREATE_FILE_HEADER #include #include #include STXXL_BEGIN_NAMESPACE //! create fileio object from io_impl string and a few parameters file * create_file(const std::string& io_impl, const std::string& filename, int options, int physical_device_id = file::DEFAULT_QUEUE, int disk_allocator_id = file::NO_ALLOCATOR); // prototype class disk_config; //! create fileio object from disk_config parameter file * create_file(disk_config& config, int mode, int disk_allocator_id = file::NO_ALLOCATOR); STXXL_END_NAMESPACE #endif // !STXXL_IO_CREATE_FILE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/linuxaio_request.h000644 001411 000144 00000004063 12405375303 022430 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/linuxaio_request.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_LINUXAIO_REQUEST_HEADER #define STXXL_IO_LINUXAIO_REQUEST_HEADER #include #if STXXL_HAVE_LINUXAIO_FILE #include #include #define STXXL_VERBOSE_LINUXAIO(msg) STXXL_VERBOSE2(msg) STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Request for an linuxaio_file. class linuxaio_request : public request_with_state { template friend class fileperblock_file; //! control block of async request iocb cb; void fill_control_block(); public: linuxaio_request( const completion_handler& on_cmpl, file* file, void* buffer, offset_type offset, size_type bytes, request_type type) : request_with_state(on_cmpl, file, buffer, offset, bytes, type) { assert(dynamic_cast(file)); STXXL_VERBOSE_LINUXAIO("linuxaio_request[" << this << "]" << " linuxaio_request" << "(file=" << file << " buffer=" << buffer << " offset=" << offset << " bytes=" << bytes << " type=" << type << ")"); } bool post(); bool cancel(); bool cancel_aio(); void completed(bool posted, bool canceled); void completed(bool canceled) { completed(true, canceled); } }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_LINUXAIO_FILE #endif // !STXXL_IO_LINUXAIO_REQUEST_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/request_queue_impl_1q.h000644 001411 000144 00000004067 12405375303 023352 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_queue_impl_1q.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008-2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_QUEUE_IMPL_1Q_HEADER #define STXXL_IO_REQUEST_QUEUE_IMPL_1Q_HEADER #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Implementation of a local request queue having only one queue for both read //! and write requests, thus having only one thread. class request_queue_impl_1q : public request_queue_impl_worker { private: typedef request_queue_impl_1q self; typedef std::list queue_type; mutex m_queue_mutex; queue_type m_queue; state m_thread_state; thread_type m_thread; semaphore m_sem; static const priority_op m_priority_op = WRITE; static void * worker(void* arg); public: // \param n max number of requests simultaneously submitted to disk request_queue_impl_1q(int n = 1); // in a multi-threaded setup this does not work as intended // also there were race conditions possible // and actually an old value was never restored once a new one was set ... // so just disable it and all it's nice implications void set_priority_op(priority_op op) { //_priority_op = op; STXXL_UNUSED(op); } void add_request(request_ptr& req); bool cancel_request(request_ptr& req); ~request_queue_impl_1q(); }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_QUEUE_IMPL_1Q_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/file.h000644 001411 000144 00000016774 12411366426 017766 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2010 Andreas Beckmann * Copyright (C) 2008, 2009 Johannes Singler * Copyright (C) 2013-2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_FILE_HEADER #define STXXL_IO_FILE_HEADER #include #if defined (__linux__) #define STXXL_CHECK_BLOCK_ALIGNING #endif #include #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup iolayer //! \{ //! \defgroup fileimpl File I/O Implementations //! Implementations of \c stxxl::file for various file access methods and //! operating systems. //! \{ class completion_handler; //! Defines interface of file. //! //! It is a base class for different implementations that might //! base on various file systems or even remote storage interfaces class file : private noncopyable { public: //! the offset of a request, also the size of the file typedef request::offset_type offset_type; //! the size of a request typedef request::size_type size_type; //! Definition of acceptable file open modes. //! //! Various open modes in a file system must be //! converted to this set of acceptable modes enum open_mode { RDONLY = 1, //!< only reading of the file is allowed WRONLY = 2, //!< only writing of the file is allowed RDWR = 4, //!< read and write of the file are allowed CREAT = 8, //!< in case file does not exist no error occurs and file is newly created DIRECT = 16, //!< I/Os proceed bypassing file system buffers, i.e. unbuffered I/O. //!< Tries to open with appropriate flags, if fails print warning and open normally. TRUNC = 32, //!< once file is opened its length becomes zero SYNC = 64, //!< open the file with O_SYNC | O_DSYNC | O_RSYNC flags set NO_LOCK = 128, //!< do not acquire an exclusive lock by default REQUIRE_DIRECT = 256 //!< implies DIRECT, fail if opening with DIRECT flag does not work. }; static const int DEFAULT_QUEUE = -1; static const int DEFAULT_LINUXAIO_QUEUE = -2; static const int NO_ALLOCATOR = -1; static const unsigned int DEFAULT_DEVICE_ID = (unsigned int)(-1); //! Construct a new file, usually called by a subclass. file(unsigned int device_id = DEFAULT_DEVICE_ID) : m_device_id(device_id) { } //! Schedules an asynchronous read request to the file. //! \param buffer pointer to memory buffer to read into //! \param pos file position to start read from //! \param bytes number of bytes to transfer //! \param on_cmpl I/O completion handler //! \return \c request_ptr request object, which can be used to track the //! status of the operation virtual request_ptr aread(void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl = completion_handler()) = 0; //! Schedules an asynchronous write request to the file. //! \param buffer pointer to memory buffer to write from //! \param pos starting file position to write //! \param bytes number of bytes to transfer //! \param on_cmpl I/O completion handler //! \return \c request_ptr request object, which can be used to track the //! status of the operation virtual request_ptr awrite(void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl = completion_handler()) = 0; virtual void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) = 0; //! Changes the size of the file. //! \param newsize new file size virtual void set_size(offset_type newsize) = 0; //! Returns size of the file. //! \return file size in bytes virtual offset_type size() = 0; //! Returns the identifier of the file's queue number. //! \remark Files allocated on the same physical device usually share the //! same queue, unless there is a common queue (e.g. with linuxaio). virtual int get_queue_id() const = 0; //! Returns the file's disk allocator number virtual int get_allocator_id() const = 0; //! Locks file for reading and writing (acquires a lock in the file system). virtual void lock() = 0; //! Discard a region of the file (mark it unused). //! Some specialized file types may need to know freed regions virtual void discard(offset_type offset, offset_type size) { STXXL_UNUSED(offset); STXXL_UNUSED(size); } virtual void export_files(offset_type offset, offset_type length, std::string prefix) { STXXL_UNUSED(offset); STXXL_UNUSED(length); STXXL_UNUSED(prefix); } //! close and remove file virtual void close_remove() { } virtual ~file() { unsigned_type nr = get_request_nref(); if (nr != 0) STXXL_ERRMSG("stxxl::file is being deleted while there are " "still " << nr << " (unfinished) requests " "referencing it"); } //! Identifies the type of I/O implementation. //! \return pointer to null terminated string of characters, containing the //! name of I/O implementation virtual const char * io_type() const = 0; protected: //! The file's physical device id (e.g. used for prefetching sequence //! calculation) unsigned int m_device_id; public: //! Returns the file's physical device id unsigned int get_device_id() const { return m_device_id; } protected: //! count the number of requests referencing this file atomic_counted_object m_request_ref; public: //! increment referenced requests void add_request_ref() { m_request_ref.inc_reference(); } //! decrement referenced requests void delete_request_ref() { m_request_ref.dec_reference(); } //! return number of referenced requests unsigned_type get_request_nref() { return m_request_ref.get_reference_count(); } public: //! \name Static Functions for Platform Abstraction //! \{ //! unlink path from filesystem static int unlink(const char* path); //! truncate a path to given length. Use this only if you dont have a //! fileio-specific object, which provides truncate(). static int truncate(const char* path, external_size_type length); //! \} }; //! \} //! \defgroup reqlayer I/O Requests and Queues //! Encapsulation of an I/O request, queues for requests and threads to process //! them. //! \{ //! \} //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_FILE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/disk_queued_file.h000644 001411 000144 00000003322 12411366426 022331 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/disk_queued_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_DISK_QUEUED_FILE_HEADER #define STXXL_IO_DISK_QUEUED_FILE_HEADER #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ class completion_handler; //! Implementation of some file methods based on serving_request. class disk_queued_file : public virtual file { int m_queue_id, m_allocator_id; public: disk_queued_file(int queue_id, int allocator_id) : m_queue_id(queue_id), m_allocator_id(allocator_id) { } request_ptr aread( void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl = completion_handler()); request_ptr awrite( void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl = completion_handler()); virtual int get_queue_id() const { return m_queue_id; } virtual int get_allocator_id() const { return m_allocator_id; } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_DISK_QUEUED_FILE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/wfs_file_base.h000644 001411 000144 00000003177 12405375303 021625 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/wfs_file_base.h * * Windows file system file base * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005 Roman Dementiev * Copyright (C) 2008, 2010 Andreas Beckmann * Copyright (C) 2009, 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_WFS_FILE_BASE_HEADER #define STXXL_IO_WFS_FILE_BASE_HEADER #include #if STXXL_WINDOWS #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Base for Windows file system implementations. class wfs_file_base : public virtual file { protected: typedef void* HANDLE; mutex fd_mutex; // sequentialize function calls involving file_des HANDLE file_des; // file descriptor int mode_; // open mode const std::string filename; offset_type bytes_per_sector; bool locked; wfs_file_base(const std::string& filename, int mode); offset_type _size(); void close(); public: ~wfs_file_base(); offset_type size(); void set_size(offset_type newsize); void lock(); const char * io_type() const; void close_remove(); }; //! \} STXXL_END_NAMESPACE #endif // STXXL_WINDOWS #endif // !STXXL_IO_WFS_FILE_BASE_HEADER stxxl-1.4.1/include/stxxl/bits/io/iostats.h000644 001411 000144 00000037556 12411366426 020536 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/iostats.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2009, 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_IOSTATS_HEADER #define STXXL_IO_IOSTATS_HEADER #ifndef STXXL_IO_STATS #define STXXL_IO_STATS 1 #endif #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup iolayer //! //! \{ //! Collects various I/O statistics. //! \remarks is a singleton class stats : public singleton { friend class singleton; unsigned reads, writes; // number of operations int64 volume_read, volume_written; // number of bytes read/written unsigned c_reads, c_writes; // number of cached operations int64 c_volume_read, c_volume_written; // number of bytes read/written from/to cache double t_reads, t_writes; // seconds spent in operations double p_reads, p_writes; // seconds spent in parallel operations double p_begin_read, p_begin_write; // start time of parallel operation double p_ios; // seconds spent in all parallel I/O operations (read and write) double p_begin_io; double t_waits, p_waits; // seconds spent waiting for completion of I/O operations double p_begin_wait; double t_wait_read, p_wait_read; double p_begin_wait_read; double t_wait_write, p_wait_write; double p_begin_wait_write; int acc_reads, acc_writes; // number of requests, participating in parallel operation int acc_ios; int acc_waits; int acc_wait_read, acc_wait_write; double last_reset; mutex read_mutex, write_mutex, io_mutex, wait_mutex; stats(); public: enum wait_op_type { WAIT_OP_ANY, WAIT_OP_READ, WAIT_OP_WRITE }; class scoped_read_write_timer { typedef unsigned_type size_type; bool is_write; #if STXXL_IO_STATS bool running; #endif public: scoped_read_write_timer(size_type size, bool is_write = false) : is_write(is_write) #if STXXL_IO_STATS , running(false) #endif { start(size); } ~scoped_read_write_timer() { stop(); } void start(size_type size) { #if STXXL_IO_STATS if (!running) { running = true; if (is_write) stats::get_instance()->write_started(size); else stats::get_instance()->read_started(size); } #else STXXL_UNUSED(size); #endif } void stop() { #if STXXL_IO_STATS if (running) { if (is_write) stats::get_instance()->write_finished(); else stats::get_instance()->read_finished(); running = false; } #endif } }; class scoped_write_timer { typedef unsigned_type size_type; #if STXXL_IO_STATS bool running; #endif public: scoped_write_timer(size_type size) #if STXXL_IO_STATS : running(false) #endif { start(size); } ~scoped_write_timer() { stop(); } void start(size_type size) { #if STXXL_IO_STATS if (!running) { running = true; stats::get_instance()->write_started(size); } #else STXXL_UNUSED(size); #endif } void stop() { #if STXXL_IO_STATS if (running) { stats::get_instance()->write_finished(); running = false; } #endif } }; class scoped_read_timer { typedef unsigned_type size_type; #if STXXL_IO_STATS bool running; #endif public: scoped_read_timer(size_type size) #if STXXL_IO_STATS : running(false) #endif { start(size); } ~scoped_read_timer() { stop(); } void start(size_type size) { #if STXXL_IO_STATS if (!running) { running = true; stats::get_instance()->read_started(size); } #else STXXL_UNUSED(size); #endif } void stop() { #if STXXL_IO_STATS if (running) { stats::get_instance()->read_finished(); running = false; } #endif } }; class scoped_wait_timer { #ifndef STXXL_DO_NOT_COUNT_WAIT_TIME bool running; wait_op_type wait_op; #endif public: scoped_wait_timer(wait_op_type wait_op, bool measure_time = true) #ifndef STXXL_DO_NOT_COUNT_WAIT_TIME : running(false), wait_op(wait_op) #endif { if (measure_time) start(); } ~scoped_wait_timer() { stop(); } void start() { #ifndef STXXL_DO_NOT_COUNT_WAIT_TIME if (!running) { running = true; stats::get_instance()->wait_started(wait_op); } #endif } void stop() { #ifndef STXXL_DO_NOT_COUNT_WAIT_TIME if (running) { stats::get_instance()->wait_finished(wait_op); running = false; } #endif } }; public: //! Returns total number of reads. //! \return total number of reads unsigned get_reads() const { return reads; } //! Returns total number of writes. //! \return total number of writes unsigned get_writes() const { return writes; } //! Returns number of bytes read from disks. //! \return number of bytes read int64 get_read_volume() const { return volume_read; } //! Returns number of bytes written to the disks. //! \return number of bytes written int64 get_written_volume() const { return volume_written; } //! Returns total number of reads served from cache. //! \return total number of cached reads unsigned get_cached_reads() const { return c_reads; } //! Returns total number of cached writes. //! \return total number of cached writes unsigned get_cached_writes() const { return c_writes; } //! Returns number of bytes read from cache. //! \return number of bytes read from cache int64 get_cached_read_volume() const { return c_volume_read; } //! Returns number of bytes written to the cache. //! \return number of bytes written to cache int64 get_cached_written_volume() const { return c_volume_written; } //! Time that would be spent in read syscalls if all parallel reads were serialized. //! \return seconds spent in reading double get_read_time() const { return t_reads; } //! Time that would be spent in write syscalls if all parallel writes were serialized. //! \return seconds spent in writing double get_write_time() const { return t_writes; } //! Period of time when at least one I/O thread was executing a read. //! \return seconds spent in reading double get_pread_time() const { return p_reads; } //! Period of time when at least one I/O thread was executing a write. //! \return seconds spent in writing double get_pwrite_time() const { return p_writes; } //! Period of time when at least one I/O thread was executing a read or a write. //! \return seconds spent in I/O double get_pio_time() const { return p_ios; } //! I/O wait time counter. //! \return number of seconds spent in I/O waiting functions \link //! request::wait request::wait \endlink, \c wait_any and \c wait_all double get_io_wait_time() const { return t_waits; } double get_wait_read_time() const { return t_wait_read; } double get_wait_write_time() const { return t_wait_write; } //! Return time of the last reset. //! \return seconds passed from the last reset() double get_last_reset_time() const { return last_reset; } #ifndef STXXL_IO_STATS_RESET_FORBIDDEN //! Resets I/O time counters (including I/O wait counter). STXXL_DEPRECATED(void reset()); #endif //! Resets I/O wait time counter. STXXL_DEPRECATED(void _reset_io_wait_time()); // for library use void write_started(unsigned_type size_, double now = 0.0); void write_canceled(unsigned_type size_); void write_finished(); void write_cached(unsigned_type size_); void read_started(unsigned_type size_, double now = 0.0); void read_canceled(unsigned_type size_); void read_finished(); void read_cached(unsigned_type size_); void wait_started(wait_op_type wait_op); void wait_finished(wait_op_type wait_op); }; #if !STXXL_IO_STATS inline void stats::write_started(unsigned_type size_, double now) { STXXL_UNUSED(size_); STXXL_UNUSED(now); } inline void stats::write_cached(unsigned_type size_) { STXXL_UNUSED(size_); } inline void stats::write_finished() { } inline void stats::read_started(unsigned_type size_, double now) { STXXL_UNUSED(size_); STXXL_UNUSED(now); } inline void stats::read_cached(unsigned_type size_) { STXXL_UNUSED(size_); } inline void stats::read_finished() { } #endif #ifdef STXXL_DO_NOT_COUNT_WAIT_TIME inline void stats::wait_started(wait_op_type) { } inline void stats::wait_finished(wait_op_type) { } #endif class stats_data { //! number of operations unsigned reads, writes; //! number of bytes read/written int64 volume_read, volume_written; //! number of cached operations unsigned c_reads, c_writes; //! number of bytes read/written from/to cache int64 c_volume_read, c_volume_written; //! seconds spent in operations double t_reads, t_writes; //! seconds spent in parallel operations double p_reads, p_writes; //! seconds spent in all parallel I/O operations (read and write) double p_ios; //! seconds spent waiting for completion of I/O operations double t_wait; double t_wait_read, t_wait_write; double elapsed; public: stats_data() : reads(0), writes(0), volume_read(0), volume_written(0), c_reads(0), c_writes(0), c_volume_read(0), c_volume_written(0), t_reads(0.0), t_writes(0.0), p_reads(0.0), p_writes(0.0), p_ios(0.0), t_wait(0.0), t_wait_read(0.0), t_wait_write(0.0), elapsed(0.0) { } stats_data(const stats& s) : reads(s.get_reads()), writes(s.get_writes()), volume_read(s.get_read_volume()), volume_written(s.get_written_volume()), c_reads(s.get_cached_reads()), c_writes(s.get_cached_writes()), c_volume_read(s.get_cached_read_volume()), c_volume_written(s.get_cached_written_volume()), t_reads(s.get_read_time()), t_writes(s.get_write_time()), p_reads(s.get_pread_time()), p_writes(s.get_pwrite_time()), p_ios(s.get_pio_time()), t_wait(s.get_io_wait_time()), t_wait_read(s.get_wait_read_time()), t_wait_write(s.get_wait_write_time()), elapsed(timestamp() - s.get_last_reset_time()) { } stats_data operator + (const stats_data& a) const { stats_data s; s.reads = reads + a.reads; s.writes = writes + a.writes; s.volume_read = volume_read + a.volume_read; s.volume_written = volume_written + a.volume_written; s.c_reads = c_reads + a.c_reads; s.c_writes = c_writes + a.c_writes; s.c_volume_read = c_volume_read + a.c_volume_read; s.c_volume_written = c_volume_written + a.c_volume_written; s.t_reads = t_reads + a.t_reads; s.t_writes = t_writes + a.t_writes; s.p_reads = p_reads + a.p_reads; s.p_writes = p_writes + a.p_writes; s.p_ios = p_ios + a.p_ios; s.t_wait = t_wait + a.t_wait; s.t_wait_read = t_wait_read + a.t_wait_read; s.t_wait_write = t_wait_write + a.t_wait_write; s.elapsed = elapsed + a.elapsed; return s; } stats_data operator - (const stats_data& a) const { stats_data s; s.reads = reads - a.reads; s.writes = writes - a.writes; s.volume_read = volume_read - a.volume_read; s.volume_written = volume_written - a.volume_written; s.c_reads = c_reads - a.c_reads; s.c_writes = c_writes - a.c_writes; s.c_volume_read = c_volume_read - a.c_volume_read; s.c_volume_written = c_volume_written - a.c_volume_written; s.t_reads = t_reads - a.t_reads; s.t_writes = t_writes - a.t_writes; s.p_reads = p_reads - a.p_reads; s.p_writes = p_writes - a.p_writes; s.p_ios = p_ios - a.p_ios; s.t_wait = t_wait - a.t_wait; s.t_wait_read = t_wait_read - a.t_wait_read; s.t_wait_write = t_wait_write - a.t_wait_write; s.elapsed = elapsed - a.elapsed; return s; } unsigned get_reads() const { return reads; } unsigned get_writes() const { return writes; } int64 get_read_volume() const { return volume_read; } int64 get_written_volume() const { return volume_written; } unsigned get_cached_reads() const { return c_reads; } unsigned get_cached_writes() const { return c_writes; } int64 get_cached_read_volume() const { return c_volume_read; } int64 get_cached_written_volume() const { return c_volume_written; } double get_read_time() const { return t_reads; } double get_write_time() const { return t_writes; } double get_pread_time() const { return p_reads; } double get_pwrite_time() const { return p_writes; } double get_pio_time() const { return p_ios; } double get_elapsed_time() const { return elapsed; } double get_io_wait_time() const { return t_wait; } double get_wait_read_time() const { return t_wait_read; } double get_wait_write_time() const { return t_wait_write; } }; std::ostream& operator << (std::ostream& o, const stats_data& s); inline std::ostream& operator << (std::ostream& o, const stats& s) { o << stxxl::stats_data(s); return o; } std::string format_with_SI_IEC_unit_multiplier(uint64 number, const char* unit = "", int multiplier = 1000); inline std::string add_IEC_binary_multiplier(uint64 number, const char* unit = "") { return format_with_SI_IEC_unit_multiplier(number, unit, 1024); } inline std::string add_SI_multiplier(uint64 number, const char* unit = "") { return format_with_SI_IEC_unit_multiplier(number, unit, 1000); } //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_IOSTATS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/request_queue_impl_qwqr.h000644 001411 000144 00000004342 12405375303 024017 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_queue_impl_qwqr.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_QUEUE_IMPL_QWQR_HEADER #define STXXL_IO_REQUEST_QUEUE_IMPL_QWQR_HEADER #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Implementation of a local request queue having two queues, one for read and //! one for write requests, thus having two threads. This is the default //! implementation. class request_queue_impl_qwqr : public request_queue_impl_worker { private: typedef request_queue_impl_qwqr self; typedef std::list queue_type; mutex m_write_mutex; mutex m_read_mutex; queue_type m_write_queue; queue_type m_read_queue; state m_thread_state; thread_type m_thread; semaphore m_sem; static const priority_op m_priority_op = WRITE; static void * worker(void* arg); public: // \param n max number of requests simultaneously submitted to disk request_queue_impl_qwqr(int n = 1); // in a multi-threaded setup this does not work as intended // also there were race conditions possible // and actually an old value was never restored once a new one was set ... // so just disable it and all it's nice implications void set_priority_op(priority_op op) { //_priority_op = op; STXXL_UNUSED(op); } void add_request(request_ptr& req); bool cancel_request(request_ptr& req); ~request_queue_impl_qwqr(); }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_QUEUE_IMPL_QWQR_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/mem_file.h000644 001411 000144 00000003320 12405375303 020600 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/mem_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_MEM_FILE_HEADER #define STXXL_IO_MEM_FILE_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Implementation of file based on new[] and memcpy. class mem_file : public disk_queued_file { //! pointer to memory area of "file" char* m_ptr; //! size of memory area offset_type m_size; //! sequentialize function calls mutex m_mutex; public: //! constructs file object. mem_file( int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID) : file(device_id), disk_queued_file(queue_id, allocator_id), m_ptr(NULL), m_size(0) { } void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); ~mem_file(); offset_type size(); void set_size(offset_type newsize); void lock(); void discard(offset_type offset, offset_type size); const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_MEM_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/io/disk_queues.h000644 001411 000144 00000007572 12405375303 021361 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/disk_queues.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_DISK_QUEUES_HEADER #define STXXL_IO_DISK_QUEUES_HEADER #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Encapsulates disk queues. //! \remark is a singleton class disk_queues : public singleton { friend class singleton; typedef stxxl::int64 DISKID; typedef std::map request_queue_map; protected: request_queue_map queues; disk_queues() { stxxl::stats::get_instance(); // initialize stats before ourselves } public: void add_request(request_ptr& req, DISKID disk) { #ifdef STXXL_HACK_SINGLE_IO_THREAD disk = 42; #endif request_queue_map::iterator qi = queues.find(disk); request_queue* q; if (qi == queues.end()) { // create new request queue #if STXXL_HAVE_LINUXAIO_FILE if (dynamic_cast(req.get())) q = queues[disk] = new linuxaio_queue( dynamic_cast(req->get_file())->get_desired_queue_length() ); else #endif q = queues[disk] = new request_queue_impl_qwqr(); } else q = qi->second; q->add_request(req); } //! Cancel a request. //! The specified request is canceled unless already being processed. //! However, cancelation cannot be guaranteed. //! Cancelled requests must still be waited for in order to ensure correct //! operation. //! \param req request to cancel //! \param disk disk number for disk that \c req was scheduled on //! \return \c true iff the request was canceled successfully bool cancel_request(request_ptr& req, DISKID disk) { #ifdef STXXL_HACK_SINGLE_IO_THREAD disk = 42; #endif if (queues.find(disk) != queues.end()) return queues[disk]->cancel_request(req); else return false; } request_queue * get_queue(DISKID disk) { if (queues.find(disk) != queues.end()) return queues[disk]; else return NULL; } ~disk_queues() { // deallocate all queues for (request_queue_map::iterator i = queues.begin(); i != queues.end(); i++) delete (*i).second; } //! Changes requests priorities. //! \param op one of: //! - READ, read requests are served before write requests within a disk queue //! - WRITE, write requests are served before read requests within a disk queue //! - NONE, read and write requests are served by turns, alternately void set_priority_op(request_queue::priority_op op) { for (request_queue_map::iterator i = queues.begin(); i != queues.end(); i++) i->second->set_priority_op(op); } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_DISK_QUEUES_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/request_operations.h000644 001411 000144 00000012104 12411366426 022761 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_operations.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_OPERATIONS_HEADER #define STXXL_IO_REQUEST_OPERATIONS_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Collection of functions to track statuses of a number of requests. //! Suspends calling thread until \b all given requests are completed. //! \param reqs_begin begin of request sequence to wait for //! \param reqs_end end of request sequence to wait for template void wait_all(RequestIterator reqs_begin, RequestIterator reqs_end) { for ( ; reqs_begin != reqs_end; ++reqs_begin) (request_ptr(*reqs_begin))->wait(); } //! Suspends calling thread until \b all given requests are completed. //! \param req_array array of request_ptr objects //! \param count size of req_array inline void wait_all(request_ptr req_array[], size_t count) { wait_all(req_array, req_array + count); } //! Cancel requests. //! The specified requests are canceled unless already being processed. //! However, cancelation cannot be guaranteed. //! Cancelled requests must still be waited for in order to ensure correct //! operation. //! \param reqs_begin begin of request sequence //! \param reqs_end end of request sequence //! \return number of request canceled template typename std::iterator_traits::difference_type cancel_all(RequestIterator reqs_begin, RequestIterator reqs_end) { typename std::iterator_traits::difference_type num_canceled = 0; while (reqs_begin != reqs_end) { if ((request_ptr(*reqs_begin))->cancel()) ++num_canceled; ++reqs_begin; } return num_canceled; } //! Polls requests. //! \param reqs_begin begin of request sequence to poll //! \param reqs_end end of request sequence to poll //! \return \c true if any of requests is completed, then index contains valid value, otherwise \c false template RequestIterator poll_any(RequestIterator reqs_begin, RequestIterator reqs_end) { while (reqs_begin != reqs_end) { if ((request_ptr(*reqs_begin))->poll()) return reqs_begin; ++reqs_begin; } return reqs_end; } //! Polls requests. //! \param req_array array of request_ptr objects //! \param count size of req_array //! \param index contains index of the \b first completed request if any //! \return \c true if any of requests is completed, then index contains valid value, otherwise \c false inline bool poll_any(request_ptr req_array[], size_t count, size_t& index) { request_ptr* res = poll_any(req_array, req_array + count); index = res - req_array; return res != (req_array + count); } //! Suspends calling thread until \b any of requests is completed. //! \param reqs_begin begin of request sequence to wait for //! \param reqs_end end of request sequence to wait for //! \return index in req_array pointing to the \b first completed request template RequestIterator wait_any(RequestIterator reqs_begin, RequestIterator reqs_end) { stats::scoped_wait_timer wait_timer(stats::WAIT_OP_ANY); onoff_switch sw; RequestIterator cur = reqs_begin, result = reqs_end; for ( ; cur != reqs_end; cur++) { if ((request_ptr(*cur))->add_waiter(&sw)) { // request is already done, no waiter was added to the request result = cur; if (cur != reqs_begin) { while (--cur != reqs_begin) (request_ptr(*cur))->delete_waiter(&sw); (request_ptr(*cur))->delete_waiter(&sw); } (request_ptr(*result))->check_errors(); return result; } } sw.wait_for_on(); for (cur = reqs_begin; cur != reqs_end; cur++) { (request_ptr(*cur))->delete_waiter(&sw); if (result == reqs_end && (request_ptr(*cur))->poll()) result = cur; } return result; } //! Suspends calling thread until \b any of requests is completed. //! \param req_array array of \c request_ptr objects //! \param count size of req_array //! \return index in req_array pointing to the \b first completed request inline size_t wait_any(request_ptr req_array[], size_t count) { return wait_any(req_array, req_array + count) - req_array; } //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_OPERATIONS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/simdisk_file.h000644 001411 000144 00000010273 12411366426 021475 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/simdisk_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2003 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_SIMDISK_FILE_HEADER #define STXXL_IO_SIMDISK_FILE_HEADER #include #ifndef STXXL_HAVE_SIMDISK_FILE // use mmap call #define STXXL_HAVE_SIMDISK_FILE STXXL_HAVE_MMAP_FILE #endif #if STXXL_HAVE_SIMDISK_FILE #include #include #include #include STXXL_BEGIN_NAMESPACE //! \weakgroup fileimpl //! \{ class simdisk_geometry : private noncopyable { struct Zone { // manufactured data #if 0 int last_cyl; int sect_per_track; #endif // derived data int first_sector; int sectors; double sustained_data_rate; // in MiB/s inline Zone(int _first_sector) : first_sector(_first_sector) { } // constructor for zone search inline Zone( #if 0 int _last_cyl, int _sect_per_track, #endif int _first_sector, int _sectors, double _rate) : #if 0 last_cyl(_last_cyl), sect_per_track(_sect_per_track), #endif first_sector(_first_sector), sectors(_sectors), sustained_data_rate(_rate) { } }; struct ZoneCmp { inline bool operator () (const Zone& a, const Zone& b) const { return a.first_sector < b.first_sector; } }; protected: int nsurfaces; int bytes_per_sector; double cmd_ovh; // in s double seek_time; // in s double rot_latency; // in s double head_switch_time; // in s double cyl_switch_time; // in s double revolution_time; // in s double interface_speed; // in byte/s std::set zones; void add_zone(int& first_cyl, int last_cyl, int sec_per_track, int& first_sect); public: inline simdisk_geometry() { } double get_delay(file::offset_type offset, file::size_type size); // returns delay in s inline ~simdisk_geometry() { } static const double s_average_speed; }; class IC35L080AVVA07 : public simdisk_geometry // IBM series 120GXP { public: IC35L080AVVA07(); }; //! Implementation of disk emulation. //! \remark It is emulation of IBM IC35L080AVVA07 disk's timings class sim_disk_file : public ufs_file_base, public disk_queued_file, public IC35L080AVVA07 { public: //! Constructs file object. //! \param filename path of file //! \attention filename must be resided at memory disk partition //! \param mode open mode, see \c stxxl::file::open_modes //! \param queue_id disk queue identifier //! \param allocator_id linked disk_allocator //! \param device_id physical device identifier inline sim_disk_file( const std::string& filename, int mode, int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID) : file(device_id), ufs_file_base(filename, mode), disk_queued_file(queue_id, allocator_id) { std::cout << "Please, make sure that '" << filename << "' is resided on swap memory partition!" << std::endl; } void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); void set_size(offset_type newsize); const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_SIMDISK_FILE #endif // !STXXL_IO_SIMDISK_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/io/linuxaio_file.h000644 001411 000144 00000005016 12411366426 021661 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/linuxaio_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_LINUXAIO_FILE_HEADER #define STXXL_IO_LINUXAIO_FILE_HEADER #include #if STXXL_HAVE_LINUXAIO_FILE #include #include #include STXXL_BEGIN_NAMESPACE class linuxaio_queue; //! \addtogroup fileimpl //! \{ //! Implementation of \c file based on the Linux kernel interface for //! asynchronous I/O class linuxaio_file : public ufs_file_base, public disk_queued_file { friend class linuxaio_request; private: int desired_queue_length; public: //! Constructs file object //! \param filename path of file //! \param mode open mode, see \c stxxl::file::open_modes //! \param queue_id disk queue identifier //! \param allocator_id linked disk_allocator //! \param device_id physical device identifier //! \param desired_queue_length queue length requested from kernel linuxaio_file( const std::string& filename, int mode, int queue_id = DEFAULT_LINUXAIO_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID, int desired_queue_length = 0) : file(device_id), ufs_file_base(filename, mode), disk_queued_file(queue_id, allocator_id), desired_queue_length(desired_queue_length) { } void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); request_ptr aread(void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl = completion_handler()); request_ptr awrite(void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl = completion_handler()); const char * io_type() const; int get_desired_queue_length() const { return desired_queue_length; } }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_LINUXAIO_FILE #endif // !STXXL_IO_LINUXAIO_FILE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/fileperblock_file.h000644 001411 000144 00000004752 12405375303 022475 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/fileperblock_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008, 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_FILEPERBLOCK_FILE_HEADER #define STXXL_IO_FILEPERBLOCK_FILE_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Implementation of file based on other files, dynamically allocate one file per block. //! Allows for dynamic disk space consumption. template class fileperblock_file : public disk_queued_file { private: std::string filename_prefix; int mode; offset_type current_size; bool lock_file_created; base_file_type lock_file; protected: //! Constructs a file name for a given block. std::string filename_for_block(offset_type offset); public: //! Constructs file object. //! param filename_prefix filename prefix, numbering will be appended to it //! param mode open mode, see \c file::open_modes fileperblock_file( const std::string& filename_prefix, int mode, int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID); virtual ~fileperblock_file(); virtual void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); //! Changes the size of the file. //! \param new_size value of the new file size virtual void set_size(offset_type new_size) { current_size = new_size; } //! Returns size of the file. //! \return file size in length virtual offset_type size() { return current_size; } virtual void lock(); //! Frees the specified region. //! Actually deletes the corresponding file if the whole thing is deleted. virtual void discard(offset_type offset, offset_type length); //! Rename the file corresponding to the offset such that it is out of reach for deleting. virtual void export_files(offset_type offset, offset_type length, std::string filename); const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_FILEPERBLOCK_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/io/ufs_file_base.h000644 001411 000144 00000003511 12405375303 021613 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/ufs_file_base.h * * UNIX file system file base * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_UFS_FILE_BASE_HEADER #define STXXL_IO_UFS_FILE_BASE_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Base for UNIX file system implementations. class ufs_file_base : public virtual file { protected: mutex fd_mutex; // sequentialize function calls involving file_des int file_des; // file descriptor int m_mode; // open mode const std::string filename; bool m_is_device; //!< is special device node ufs_file_base(const std::string& filename, int mode); void _after_open(); offset_type _size(); void _set_size(offset_type newsize); void close(); public: ~ufs_file_base(); offset_type size(); void set_size(offset_type newsize); void lock(); const char * io_type() const; void close_remove(); //! unlink file without closing it. void unlink(); //! return true if file is special device node bool is_device() const; }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_UFS_FILE_BASE_HEADER stxxl-1.4.1/include/stxxl/bits/io/request_with_state.h000644 001411 000144 00000003243 12411366426 022755 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_with_state.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_WITH_STATE_HEADER #define STXXL_IO_REQUEST_WITH_STATE_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Request with completion state. class request_with_state : public request_with_waiters { protected: //! states of request //! OP - operating, DONE - request served, READY2DIE - can be destroyed enum request_state { OP = 0, DONE = 1, READY2DIE = 2 }; state m_state; protected: request_with_state( const completion_handler& on_cmpl, file* f, void* buf, offset_type off, size_type b, request_type t) : request_with_waiters(on_cmpl, f, buf, off, b, t), m_state(OP) { } public: virtual ~request_with_state(); void wait(bool measure_time = true); bool poll(); bool cancel(); protected: void completed(bool canceled); }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_WITH_STATE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/request_with_waiters.h000644 001411 000144 00000003050 12405375303 023304 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_with_waiters.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_WITH_WAITERS_HEADER #define STXXL_IO_REQUEST_WITH_WAITERS_HEADER #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Request that is aware of threads waiting for it to complete. class request_with_waiters : public request { mutex m_waiters_mutex; std::set m_waiters; protected: bool add_waiter(onoff_switch* sw); void delete_waiter(onoff_switch* sw); void notify_waiters(); //! returns number of waiters size_t num_waiters(); public: request_with_waiters( const completion_handler& on_cmpl, file* f, void* buf, offset_type off, size_type b, request_type t) : request(on_cmpl, f, buf, off, b, t) { } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_WITH_WAITERS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/completion_handler.h000644 001411 000144 00000005422 12411366426 022701 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/completion_handler.h * * Loki-style completion handler (functors) * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_COMPLETION_HANDLER_HEADER #define STXXL_IO_COMPLETION_HANDLER_HEADER #include #include #include STXXL_BEGIN_NAMESPACE class request; class completion_handler_impl { public: virtual void operator () (request*) = 0; virtual completion_handler_impl * clone() const = 0; virtual ~completion_handler_impl() { } }; template class completion_handler1 : public completion_handler_impl { private: HandlerType m_handler; public: completion_handler1(const HandlerType& handler) : m_handler(handler) { } completion_handler1 * clone() const { return new completion_handler1(*this); } void operator () (request* req) { m_handler(req); } }; //! Completion handler class (Loki-style). //! //! In some situations one needs to execute some actions after completion of an //! I/O request. In these cases one can use an I/O completion handler - a //! function object that can be passed as a parameter to asynchronous I/O calls //! \c stxxl::file::aread and \c stxxl::file::awrite . class completion_handler { compat_unique_ptr::result m_ptr; public: //! Construct default, no operation completion handler. completion_handler() : m_ptr(static_cast(NULL)) { } //! Copy constructor. completion_handler(const completion_handler& obj) : m_ptr(obj.m_ptr.get() ? obj.m_ptr.get()->clone() : NULL) { } //! Construct a completion handler which calls some function. template completion_handler(const HandlerType& handler) : m_ptr(new completion_handler1(handler)) { } //! Assignment operator completion_handler& operator = (const completion_handler& obj) { m_ptr.reset(obj.m_ptr.get() ? obj.m_ptr.get()->clone() : NULL); return *this; } //! Call the enclosed completion handler. void operator () (request* req) { if (m_ptr.get()) (* m_ptr)(req); } }; STXXL_END_NAMESPACE #endif // !STXXL_IO_COMPLETION_HANDLER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/io.h000644 001411 000144 00000002367 12410750556 017447 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/io.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_IO_HEADER #define STXXL_IO_IO_HEADER #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! \c STXXL library namespace STXXL_BEGIN_NAMESPACE STXXL_END_NAMESPACE #endif // !STXXL_IO_IO_HEADER stxxl-1.4.1/include/stxxl/bits/io/mmap_file.h000644 001411 000144 00000003516 12411366426 020766 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/mmap_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_MMAP_FILE_HEADER #define STXXL_IO_MMAP_FILE_HEADER #include #if STXXL_HAVE_MMAP_FILE #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Implementation of memory mapped access file. class mmap_file : public ufs_file_base, public disk_queued_file { public: //! Constructs file object. //! \param filename path of file //! \param mode open mode, see \c stxxl::file::open_modes //! \param queue_id disk queue identifier //! \param allocator_id linked disk_allocator //! \param device_id physical device identifier inline mmap_file( const std::string& filename, int mode, int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID) : file(device_id), ufs_file_base(filename, mode), disk_queued_file(queue_id, allocator_id) { } void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_MMAP_FILE #endif // !STXXL_IO_MMAP_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/io/request_interface.h000644 001411 000144 00000005172 12405375303 022542 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_interface.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2009, 2011 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_INTERFACE_HEADER #define STXXL_IO_REQUEST_INTERFACE_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ class onoff_switch; //! Functional interface of a request. //! //! Since all library I/O operations are asynchronous, //! one needs to keep track of their status: //! e.g. whether an I/O operation completed or not. class request_interface : private noncopyable { public: typedef stxxl::external_size_type offset_type; typedef stxxl::internal_size_type size_type; enum request_type { READ, WRITE }; public: virtual bool add_waiter(onoff_switch* sw) = 0; virtual void delete_waiter(onoff_switch* sw) = 0; protected: virtual void notify_waiters() = 0; protected: virtual void completed(bool canceled) = 0; public: //! Suspends calling thread until completion of the request. virtual void wait(bool measure_time = true) = 0; //! Cancel a request. //! //! The request is canceled unless already being processed. //! However, cancelation cannot be guaranteed. //! Canceled requests must still be waited for in order to ensure correct operation. //! If the request was canceled successfully, the completion handler will not be called. //! \return \c true iff the request was canceled successfully virtual bool cancel() = 0; //! Polls the status of the request. //! \return \c true if request is completed, otherwise \c false virtual bool poll() = 0; //! Identifies the type of I/O implementation. //! \return pointer to null terminated string of characters, containing the name of I/O implementation virtual const char * io_type() const = 0; //! Dumps properties of a request. virtual std::ostream & print(std::ostream& out) const = 0; virtual ~request_interface() { } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_INTERFACE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/boostfd_file.h000644 001411 000144 00000004111 12405375303 021461 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/boostfd_file.h * * File implementation based on boost::iostreams::file_decriptor * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_BOOSTFD_FILE_HEADER #define STXXL_IO_BOOSTFD_FILE_HEADER #include #ifndef STXXL_HAVE_BOOSTFD_FILE #if STXXL_BOOST_CONFIG // if boost is available #define STXXL_HAVE_BOOSTFD_FILE 1 #else #define STXXL_HAVE_BOOSTFD_FILE 0 #endif #endif #if STXXL_HAVE_BOOSTFD_FILE #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Implementation based on boost::iostreams::file_decriptor. class boostfd_file : public disk_queued_file { typedef boost::iostreams::file_descriptor fd_type; protected: //! sequentialize function calls involving m_file_des mutex m_fd_mutex; fd_type m_file_des; int m_mode; offset_type _size(); public: boostfd_file( const std::string& filename, int mode, int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID); ~boostfd_file(); offset_type size(); void set_size(offset_type newsize); void lock(); void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_BOOSTFD_FILE #endif // !STXXL_IO_BOOSTFD_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/io/request_queue.h000644 001411 000144 00000002335 12405375303 021724 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/request_queue.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * Copyright (C) 2011 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_REQUEST_QUEUE_HEADER #define STXXL_IO_REQUEST_QUEUE_HEADER #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Interface of a request_queue to which requests can be added and canceled. class request_queue : private noncopyable { public: enum priority_op { READ, WRITE, NONE }; public: virtual void add_request(request_ptr& req) = 0; virtual bool cancel_request(request_ptr& req) = 0; virtual ~request_queue() { } virtual void set_priority_op(priority_op p) { STXXL_UNUSED(p); } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_REQUEST_QUEUE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/serving_request.h000644 001411 000144 00000002553 12405375303 022257 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/serving_request.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_SERVING_REQUEST_HEADER #define STXXL_IO_SERVING_REQUEST_HEADER #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Request which serves an I/O by calling the synchronous routine of the file. class serving_request : public request_with_state { template friend class fileperblock_file; friend class request_queue_impl_qwqr; friend class request_queue_impl_1q; public: serving_request( const completion_handler& on_cmpl, file* f, void* buf, offset_type off, size_type b, request_type t); protected: virtual void serve(); public: const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_IO_SERVING_REQUEST_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/wincall_file.h000644 001411 000144 00000003775 12405375303 021471 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/wincall_file.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005-2006 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009-2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_WINCALL_FILE_HEADER #define STXXL_IO_WINCALL_FILE_HEADER #include #ifndef STXXL_HAVE_WINCALL_FILE #if STXXL_WINDOWS #define STXXL_HAVE_WINCALL_FILE 1 #else #define STXXL_HAVE_WINCALL_FILE 0 #endif #endif #if STXXL_HAVE_WINCALL_FILE #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Implementation of file based on Windows native I/O calls. class wincall_file : public wfs_file_base, public disk_queued_file { public: //! Constructs file object. //! \param filename path of file //! \param mode open mode, see \c stxxl::file::open_modes //! \param queue_id disk queue identifier //! \param allocator_id linked disk_allocator //! \param device_id physical device identifier wincall_file( const std::string& filename, int mode, int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR, unsigned int device_id = DEFAULT_DEVICE_ID) : file(device_id), wfs_file_base(filename, mode), disk_queued_file(queue_id, allocator_id) { } void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); const char * io_type() const; }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_WINCALL_FILE #endif // !STXXL_IO_WINCALL_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/io/linuxaio_queue.h000644 001411 000144 00000006045 12411366426 022071 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/linuxaio_queue.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_LINUXAIO_QUEUE_HEADER #define STXXL_IO_LINUXAIO_QUEUE_HEADER #include #if STXXL_HAVE_LINUXAIO_FILE #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup reqlayer //! \{ //! Queue for linuxaio_file(s) //! //! Only one queue exists in a program, i.e. it is a singleton. class linuxaio_queue : public request_queue_impl_worker { friend class linuxaio_request; typedef linuxaio_queue self_type; private: //! OS context aio_context_t context; //! storing linuxaio_request* would drop ownership typedef std::list queue_type; // "waiting" request have submitted to this queue, but not yet to the OS, // those are "posted" mutex waiting_mtx, posted_mtx; queue_type waiting_requests, posted_requests; //! max number of OS requests int max_events; //! number of requests in waitings_requests semaphore num_waiting_requests, num_free_events, num_posted_requests; // two threads, one for posting, one for waiting thread_type post_thread, wait_thread; state post_thread_state, wait_thread_state; // Why do we need two threads, one for posting, and one for waiting? Is // one not enough? // 1. User call cannot io_submit directly, since this tends to take // considerable time sometimes // 2. A single thread cannot wait for the user program to post requests // and the OS to produce I/O completion events at the same time // (IOCB_CMD_NOOP does not seem to help here either) static const priority_op _priority_op = WRITE; static void * post_async(void* arg); // thread start callback static void * wait_async(void* arg); // thread start callback void post_requests(); void handle_events(io_event* events, long num_events, bool canceled); void wait_requests(); void suspend(); // needed by linuxaio_request aio_context_t get_io_context() { return context; } public: //! Construct queue. Requests max number of requests simultaneously //! submitted to disk, 0 means as many as possible linuxaio_queue(int desired_queue_length = 0); void add_request(request_ptr& req); bool cancel_request(request_ptr& req); void complete_request(request_ptr& req); ~linuxaio_queue(); }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_LINUXAIO_FILE #endif // !STXXL_IO_LINUXAIO_QUEUE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/io/wbtl_file.h000644 001411 000144 00000006527 12405375303 021006 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/io/wbtl_file.h * * a write-buffered-translation-layer pseudo file * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008-2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_WBTL_FILE_HEADER #define STXXL_IO_WBTL_FILE_HEADER #ifndef STXXL_HAVE_WBTL_FILE #define STXXL_HAVE_WBTL_FILE 1 #endif #if STXXL_HAVE_WBTL_FILE #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup fileimpl //! \{ //! Implementation of file based on buffered writes and block remapping via a //! translation layer. class wbtl_file : public disk_queued_file { typedef std::pair place; typedef std::map sortseq; typedef std::map place_map; // the physical disk used as backend file* storage; offset_type sz; size_type write_block_size; mutex mapping_mutex; // logical to physical address translation sortseq address_mapping; // physical to (logical address, size) translation place_map reverse_mapping; // list of free (physical) regions sortseq free_space; offset_type free_bytes; // the write buffers: // write_buffer[curbuf] is the current write buffer // write_buffer[1-curbuf] is the previous write buffer // buffer_address if the start offset on the backend file // curpos is the next writing position in write_buffer[curbuf] mutex buffer_mutex; char* write_buffer[2]; offset_type buffer_address[2]; int curbuf; size_type curpos; request_ptr backend_request; struct FirstFit : public std::binary_function { bool operator () ( const place& entry, const offset_type size) const { return (entry.second >= size); } }; public: //! Constructs file object. //! param backend_file file object used as storage backend, will be deleted in ~wbtl_file() wbtl_file( file* backend_file, size_type write_buffer_size, int write_buffers = 2, int queue_id = DEFAULT_QUEUE, int allocator_id = NO_ALLOCATOR); ~wbtl_file(); offset_type size(); void set_size(offset_type newsize); void lock(); void serve(void* buffer, offset_type offset, size_type bytes, request::request_type type); void discard(offset_type offset, offset_type size); const char * io_type() const; private: void _add_free_region(offset_type offset, offset_type size); protected: void sread(void* buffer, offset_type offset, size_type bytes); void swrite(void* buffer, offset_type offset, size_type bytes); offset_type get_next_write_block(); void check_corruption(offset_type region_pos, offset_type region_size, sortseq::iterator pred, sortseq::iterator succ); }; //! \} STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_WBTL_FILE #endif // !STXXL_IO_WBTL_FILE_HEADER stxxl-1.4.1/include/stxxl/bits/containers/matrix.h000644 001411 000144 00000145200 12411366426 022074 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/matrix.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_MATRIX_HEADER #define STXXL_CONTAINERS_MATRIX_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \defgroup matrix matrix //! Efficient external memory matrix operations //! \ingroup stlcont //! \{ /* index-variable naming convention: * [MODIFIER_][UNIT_]DIMENSION[_in_[MODIFIER_]ENVIRONMENT] * * e.g.: * block_row = number of row measured in rows consisting of blocks * element_row_in_block = number of row measured in rows consisting of elements in the (row of) block(s) * * size-variable naming convention: * [MODIFIER_][ENVIRONMENT_]DIMENSION[_in_UNITs] * * e.g. * height_in_blocks */ // forward declaration template class matrix; //! external column-vector container for matrix multiplication //! \tparam ValueType type of contained objects (POD with no references to internal memory) template class column_vector : public vector { public: typedef vector vector_type; typedef typename vector_type::size_type size_type; using vector_type::size; //! \param n number of elements column_vector(size_type n = 0) : vector_type(n) { } column_vector operator + (const column_vector& right) const { assert(size() == right.size()); column_vector res(size()); for (size_type i = 0; i < size(); ++i) res[i] = (*this)[i] + right[i]; return res; } column_vector operator - (const column_vector& right) const { assert(size() == right.size()); column_vector res(size()); for (size_type i = 0; i < size(); ++i) res[i] = (*this)[i] - right[i]; return res; } column_vector operator * (const ValueType scalar) const { column_vector res(size()); for (size_type i = 0; i < size(); ++i) res[i] = (*this)[i] * scalar; return res; } column_vector& operator += (const column_vector& right) { assert(size() == right.size()); for (size_type i = 0; i < size(); ++i) (*this)[i] += right[i]; return *this; } column_vector& operator -= (const column_vector& right) { assert(size() == right.size()); for (size_type i = 0; i < size(); ++i) (*this)[i] -= right[i]; return *this; } column_vector& operator *= (const ValueType scalar) { for (size_type i = 0; i < size(); ++i) (*this)[i] *= scalar; return *this; } void set_zero() { for (typename vector_type::iterator it = vector_type::begin(); it != vector_type::end(); ++it) *it = 0; } }; //! external row-vector container for matrix multiplication //! \tparam ValueType type of contained objects (POD with no references to internal memory) template class row_vector : public vector { public: typedef vector vector_type; typedef typename vector_type::size_type size_type; using vector_type::size; //! \param n number of elements row_vector(size_type n = 0) : vector_type(n) { } row_vector operator + (const row_vector& right) const { assert(size() == right.size()); row_vector res(size()); for (size_type i = 0; i < size(); ++i) res[i] = (*this)[i] + right[i]; return res; } row_vector operator - (const row_vector& right) const { assert(size() == right.size()); row_vector res(size()); for (size_type i = 0; i < size(); ++i) res[i] = (*this)[i] - right[i]; return res; } row_vector operator * (const ValueType scalar) const { row_vector res(size()); for (size_type i = 0; i < size(); ++i) res[i] = (*this)[i] * scalar; return res; } template row_vector operator * (const matrix& right) const { return right.multiply_from_left(*this); } ValueType operator * (const column_vector& right) const { ValueType res = 0; for (size_type i = 0; i < size(); ++i) res += (*this)[i] * right[i]; return res; } row_vector& operator += (const row_vector& right) { assert(size() == right.size()); for (size_type i = 0; i < size(); ++i) (*this)[i] += right[i]; return *this; } row_vector& operator -= (const row_vector& right) { assert(size() == right.size()); for (size_type i = 0; i < size(); ++i) (*this)[i] -= right[i]; return *this; } row_vector& operator *= (const ValueType scalar) { for (size_type i = 0; i < size(); ++i) (*this)[i] *= scalar; return *this; } void set_zero() { for (typename vector_type::iterator it = vector_type::begin(); it != vector_type::end(); ++it) *it = 0; } }; //! Specialized swappable_block that interprets uninitialized as containing zeros. //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block //! //! When initializing, all values are set to zero. template class matrix_swappable_block : public swappable_block { public: typedef typename swappable_block::internal_block_type internal_block_type; using swappable_block::get_internal_block; void fill_default() { // get_internal_block checks acquired internal_block_type& data = get_internal_block(); #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type row = 0; row < int_type(BlockSideLength); ++row) for (int_type col = 0; col < int_type(BlockSideLength); ++col) data[row * BlockSideLength + col] = 0; } }; //! External container for a (sub)matrix. Not intended for direct use. //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block //! //! Stores blocks only, so all measures (height, width, row, col) are in blocks. template class swappable_block_matrix : public atomic_counted_object { public: typedef int_type size_type; typedef int_type elem_size_type; typedef block_scheduler > block_scheduler_type; typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; typedef std::vector blocks_type; typedef matrix_local::matrix_operations Ops; block_scheduler_type& bs; private: // assigning is not allowed swappable_block_matrix& operator = (const swappable_block_matrix& other); protected: //! height of the matrix in blocks size_type height, //! width of the matrix in blocks width, //! height copied from supermatrix in blocks height_from_supermatrix, //! width copied from supermatrix in blocks width_from_supermatrix; //! the matrice's blocks in row-major blocks_type blocks; //! if the elements in each block are in col-major instead of row-major bool elements_in_blocks_transposed; //! get identifier of the block at (row, col) swappable_block_identifier_type & bl(const size_type row, const size_type col) { return blocks[row * width + col]; } public: //! Create an empty swappable_block_matrix of given dimensions. swappable_block_matrix(block_scheduler_type& bs, const size_type height_in_blocks, const size_type width_in_blocks, const bool transposed = false) : bs(bs), height(height_in_blocks), width(width_in_blocks), height_from_supermatrix(0), width_from_supermatrix(0), blocks(height * width), elements_in_blocks_transposed(transposed) { for (size_type row = 0; row < height; ++row) for (size_type col = 0; col < width; ++col) bl(row, col) = bs.allocate_swappable_block(); } //! Create swappable_block_matrix of given dimensions that //! represents the submatrix of supermatrix starting at (from_row_in_blocks, from_col_in_blocks). //! //! If supermatrix is not large enough, the submatrix is padded with empty blocks. //! The supermatrix must not be destructed or transposed before the submatrix is destructed. swappable_block_matrix(const swappable_block_matrix& supermatrix, const size_type height_in_blocks, const size_type width_in_blocks, const size_type from_row_in_blocks, const size_type from_col_in_blocks) : bs(supermatrix.bs), height(height_in_blocks), width(width_in_blocks), height_from_supermatrix(std::min(supermatrix.height - from_row_in_blocks, height)), width_from_supermatrix(std::min(supermatrix.width - from_col_in_blocks, width)), blocks(height * width), elements_in_blocks_transposed(supermatrix.elements_in_blocks_transposed) { for (size_type row = 0; row < height_from_supermatrix; ++row) { for (size_type col = 0; col < width_from_supermatrix; ++col) bl(row, col) = supermatrix.block(row + from_row_in_blocks, col + from_col_in_blocks); for (size_type col = width_from_supermatrix; col < width; ++col) bl(row, col) = bs.allocate_swappable_block(); } for (size_type row = height_from_supermatrix; row < height; ++row) for (size_type col = 0; col < width; ++col) bl(row, col) = bs.allocate_swappable_block(); } //! Create swappable_block_matrix that represents the combination matrix ul ur dl dr. //! //! The submatrices are assumed to be of fitting dimensions and equal transposition. //! The submatrices must not be destructed or transposed before the matrix is destructed. swappable_block_matrix(const swappable_block_matrix& ul, const swappable_block_matrix& ur, const swappable_block_matrix& dl, const swappable_block_matrix& dr) : bs(ul.bs), height(ul.height + dl.height), width(ul.width + ur.width), height_from_supermatrix(height), width_from_supermatrix(width), blocks(height * width), elements_in_blocks_transposed(ul.elements_in_blocks_transposed) { for (size_type row = 0; row < ul.height; ++row) { for (size_type col = 0; col < ul.width; ++col) bl(row, col) = ul.block(row, col); for (size_type col = ul.width; col < width; ++col) bl(row, col) = ur.block(row, col - ul.width); } for (size_type row = ul.height; row < height; ++row) { for (size_type col = 0; col < ul.width; ++col) bl(row, col) = dl.block(row - ul.height, col); for (size_type col = ul.width; col < width; ++col) bl(row, col) = dr.block(row - ul.height, col - ul.width); } } swappable_block_matrix(const swappable_block_matrix& other) : atomic_counted_object(other), bs(other.bs), height(other.height), width(other.width), height_from_supermatrix(0), width_from_supermatrix(0), blocks(height * width), elements_in_blocks_transposed(false) { for (size_type row = 0; row < height; ++row) for (size_type col = 0; col < width; ++col) bl(row, col) = bs.allocate_swappable_block(); // 0 + other is copying Ops::element_op(*this, other, typename Ops::addition()); } ~swappable_block_matrix() { for (size_type row = 0; row < height_from_supermatrix; ++row) { for (size_type col = width_from_supermatrix; col < width; ++col) bs.free_swappable_block(bl(row, col)); } for (size_type row = height_from_supermatrix; row < height; ++row) for (size_type col = 0; col < width; ++col) bs.free_swappable_block(bl(row, col)); } static size_type block_index_from_elem(elem_size_type index) { return index / BlockSideLength; } static int_type elem_index_in_block_from_elem(elem_size_type index) { return index % BlockSideLength; } // regards transposed int_type elem_index_in_block_from_elem(elem_size_type row, elem_size_type col) const { return (is_transposed()) ? row % BlockSideLength + col % BlockSideLength * BlockSideLength : row % BlockSideLength * BlockSideLength + col % BlockSideLength; } //! get identifier of the block at (row, col) const swappable_block_identifier_type & block(const size_type row, const size_type col) const { return blocks[row * width + col]; } //! get identifier of the block at (row, col) const swappable_block_identifier_type& operator () (const size_type row, const size_type col) const { return block(row, col); } const size_type & get_height() const { return height; } const size_type & get_width() const { return width; } //! if the elements inside the blocks are in transposed order i.e. column-major const bool & is_transposed() const { return elements_in_blocks_transposed; } void transpose() { // transpose matrix of blocks blocks_type bn(blocks.size()); for (size_type row = 0; row < height; ++row) for (size_type col = 0; col < width; ++col) bn[col * height + row] = bl(row, col); bn.swap(blocks); // swap dimensions std::swap(height, width); std::swap(height_from_supermatrix, width_from_supermatrix); elements_in_blocks_transposed = ! elements_in_blocks_transposed; } void set_zero() { for (typename blocks_type::iterator it = blocks.begin(); it != blocks.end(); ++it) bs.deinitialize(*it); } }; //! general iterator type that points to single elements inside a matrix //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block template class matrix_iterator { protected: typedef matrix matrix_type; typedef typename matrix_type::swappable_block_matrix_type swappable_block_matrix_type; typedef typename matrix_type::block_scheduler_type block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename matrix_type::elem_size_type elem_size_type; typedef typename matrix_type::block_size_type block_size_type; template friend class matrix; template friend class const_matrix_iterator; matrix_type* m; elem_size_type current_row, // \ both indices == -1 <=> empty iterator current_col; // / block_size_type current_block_row, current_block_col; internal_block_type* current_iblock; // NULL if block is not acquired void acquire_current_iblock() { if (! current_iblock) current_iblock = &m->data->bs.acquire(m->data->block(current_block_row, current_block_col)); } void release_current_iblock() { if (current_iblock) { m->data->bs.release(m->data->block(current_block_row, current_block_col), true); current_iblock = 0; } } //! create iterator pointing to given row and col matrix_iterator(matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) : m(&matrix), current_row(start_row), current_col(start_col), current_block_row(m->data->block_index_from_elem(start_row)), current_block_col(m->data->block_index_from_elem(start_col)), current_iblock(0) { } //! create empty iterator matrix_iterator(matrix_type& matrix) : m(&matrix), current_row(-1), // empty iterator current_col(-1), current_block_row(-1), current_block_col(-1), current_iblock(0) { } void set_empty() { release_current_iblock(); current_row = -1; current_col = -1; current_block_row = -1; current_block_col = -1; } public: matrix_iterator(const matrix_iterator& other) : m(other.m), current_row(other.current_row), current_col(other.current_col), current_block_row(other.current_block_row), current_block_col(other.current_block_col), current_iblock(0) { if (other.current_iblock) acquire_current_iblock(); } matrix_iterator& operator = (const matrix_iterator& other) { set_pos(other.current_row, other.current_col); m = other.m; if (other.current_iblock) acquire_current_iblock(); return *this; } ~matrix_iterator() { release_current_iblock(); } void set_row(const elem_size_type new_row) { const block_size_type new_block_row = m->data->block_index_from_elem(new_row); if (new_block_row != current_block_row) { release_current_iblock(); current_block_row = new_block_row; } current_row = new_row; } void set_col(const elem_size_type new_col) { const block_size_type new_block_col = m->data->block_index_from_elem(new_col); if (new_block_col != current_block_col) { release_current_iblock(); current_block_col = new_block_col; } current_col = new_col; } void set_pos(const elem_size_type new_row, const elem_size_type new_col) { const block_size_type new_block_row = m->data->block_index_from_elem(new_row), new_block_col = m->data->block_index_from_elem(new_col); if (new_block_col != current_block_col || new_block_row != current_block_row) { release_current_iblock(); current_block_row = new_block_row; current_block_col = new_block_col; } current_row = new_row; current_col = new_col; } void set_pos(const std::pair new_pos) { set_pos(new_pos.first, new_pos.second); } const elem_size_type & get_row() const { return current_row; } const elem_size_type & get_col() const { return current_col; } std::pair get_pos() const { return std::make_pair(current_row, current_col); } bool empty() const { return current_row == -1 && current_col == -1; } operator bool () const { return ! empty(); } bool operator == (const matrix_iterator& other) const { return current_row == other.current_row && current_col == other.current_col && m == other.m; } //! Returns reference access to the element referenced by the iterator. //! The reference is only valid so long as the iterator is not moved. ValueType& operator * () { acquire_current_iblock(); return (*current_iblock)[m->data->elem_index_in_block_from_elem(current_row, current_col)]; } }; //! row-major iterator that points to single elements inside a matrix //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block template class matrix_row_major_iterator : public matrix_iterator { protected: typedef matrix_iterator matrix_iterator_type; typedef typename matrix_iterator_type::matrix_type matrix_type; typedef typename matrix_iterator_type::elem_size_type elem_size_type; template friend class matrix; using matrix_iterator_type::m; using matrix_iterator_type::set_empty; //! create iterator pointing to given row and col matrix_row_major_iterator(matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) : matrix_iterator_type(matrix, start_row, start_col) { } //! create empty iterator matrix_row_major_iterator(matrix_type& matrix) : matrix_iterator_type(matrix) { } public: //! convert from matrix_iterator matrix_row_major_iterator(const matrix_iterator_type& matrix_iterator) : matrix_iterator_type(matrix_iterator) { } // Has to be not empty, else behavior is undefined. matrix_row_major_iterator& operator ++ () { if (get_col() + 1 < m->get_width()) // => not matrix_row_major_iterator the end of row, move right set_col(get_col() + 1); else if (get_row() + 1 < m->get_height()) // => at end of row but not last row, move to beginning of next row set_pos(get_row() + 1, 0); else // => at end of matrix, set to empty-state set_empty(); return *this; } // Has to be not empty, else behavior is undefined. matrix_row_major_iterator& operator -- () { if (get_col() - 1 >= 0) // => not at the beginning of row, move left set_col(get_col() - 1); else if (get_row() - 1 >= 0) // => at beginning of row but not first row, move to end of previous row set_pos(get_row() - 1, m->get_width() - 1); else // => at beginning of matrix, set to empty-state set_empty(); return *this; } using matrix_iterator_type::get_row; using matrix_iterator_type::get_col; using matrix_iterator_type::set_col; using matrix_iterator_type::set_pos; }; //! column-major iterator that points to single elements inside a matrix //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block template class matrix_col_major_iterator : public matrix_iterator { protected: typedef matrix_iterator matrix_iterator_type; typedef typename matrix_iterator_type::matrix_type matrix_type; typedef typename matrix_iterator_type::elem_size_type elem_size_type; template friend class matrix; using matrix_iterator_type::m; using matrix_iterator_type::set_empty; //! create iterator pointing to given row and col matrix_col_major_iterator(matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) : matrix_iterator_type(matrix, start_row, start_col) { } //! create empty iterator matrix_col_major_iterator(matrix_type& matrix) : matrix_iterator_type(matrix) { } public: //! convert from matrix_iterator matrix_col_major_iterator(const matrix_iterator_type& matrix_iterator) : matrix_iterator_type(matrix_iterator) { } // Has to be not empty, else behavior is undefined. matrix_col_major_iterator& operator ++ () { if (get_row() + 1 < m->get_height()) // => not at the end of col, move down set_row(get_row() + 1); else if (get_col() + 1 < m->get_width()) // => at end of col but not last col, move to beginning of next col set_pos(0, get_col() + 1); else // => at end of matrix, set to empty-state set_empty(); return *this; } // Has to be not empty, else behavior is undefined. matrix_col_major_iterator& operator -- () { if (get_row() - 1 >= 0) // => not at the beginning of col, move up set_row(get_row() - 1); else if (get_col() - 1 >= 0) // => at beginning of col but not first col, move to end of previous col set_pos(m->get_height() - 1, get_col() - 1); else // => at beginning of matrix, set to empty-state set_empty(); return *this; } using matrix_iterator_type::get_row; using matrix_iterator_type::get_col; using matrix_iterator_type::set_row; using matrix_iterator_type::set_pos; }; //! general const_iterator type that points to single elements inside a matrix //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block template class const_matrix_iterator { protected: typedef matrix matrix_type; typedef typename matrix_type::swappable_block_matrix_type swappable_block_matrix_type; typedef typename matrix_type::block_scheduler_type block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename matrix_type::elem_size_type elem_size_type; typedef typename matrix_type::block_size_type block_size_type; template friend class matrix; const matrix_type* m; elem_size_type current_row, // \ both indices == -1 <=> empty iterator current_col; // / block_size_type current_block_row, current_block_col; internal_block_type* current_iblock; // NULL if block is not acquired void acquire_current_iblock() { if (! current_iblock) current_iblock = &m->data->bs.acquire(m->data->block(current_block_row, current_block_col)); } void release_current_iblock() { if (current_iblock) { m->data->bs.release(m->data->block(current_block_row, current_block_col), false); current_iblock = 0; } } //! create iterator pointing to given row and col const_matrix_iterator(const matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) : m(&matrix), current_row(start_row), current_col(start_col), current_block_row(m->data->block_index_from_elem(start_row)), current_block_col(m->data->block_index_from_elem(start_col)), current_iblock(0) { } //! create empty iterator const_matrix_iterator(const matrix_type& matrix) : m(&matrix), current_row(-1), // empty iterator current_col(-1), current_block_row(-1), current_block_col(-1), current_iblock(0) { } void set_empty() { release_current_iblock(); current_row = -1; current_col = -1; current_block_row = -1; current_block_col = -1; } public: const_matrix_iterator(const matrix_iterator& other) : m(other.m), current_row(other.current_row), current_col(other.current_col), current_block_row(other.current_block_row), current_block_col(other.current_block_col), current_iblock(0) { if (other.current_iblock) acquire_current_iblock(); } const_matrix_iterator(const const_matrix_iterator& other) : m(other.m), current_row(other.current_row), current_col(other.current_col), current_block_row(other.current_block_row), current_block_col(other.current_block_col), current_iblock(0) { if (other.current_iblock) acquire_current_iblock(); } const_matrix_iterator& operator = (const const_matrix_iterator& other) { set_pos(other.current_row, other.current_col); m = other.m; if (other.current_iblock) acquire_current_iblock(); return *this; } ~const_matrix_iterator() { release_current_iblock(); } void set_row(const elem_size_type new_row) { const block_size_type new_block_row = m->data->block_index_from_elem(new_row); if (new_block_row != current_block_row) { release_current_iblock(); current_block_row = new_block_row; } current_row = new_row; } void set_col(const elem_size_type new_col) { const block_size_type new_block_col = m->data->block_index_from_elem(new_col); if (new_block_col != current_block_col) { release_current_iblock(); current_block_col = new_block_col; } current_col = new_col; } void set_pos(const elem_size_type new_row, const elem_size_type new_col) { const block_size_type new_block_row = m->data->block_index_from_elem(new_row), new_block_col = m->data->block_index_from_elem(new_col); if (new_block_col != current_block_col || new_block_row != current_block_row) { release_current_iblock(); current_block_row = new_block_row; current_block_col = new_block_col; } current_row = new_row; current_col = new_col; } void set_pos(const std::pair new_pos) { set_pos(new_pos.first, new_pos.second); } const elem_size_type & get_row() const { return current_row; } const elem_size_type & get_col() const { return current_col; } std::pair get_pos() const { return std::make_pair(current_row, current_col); } bool empty() const { return current_row == -1 && current_col == -1; } operator bool () const { return ! empty(); } bool operator == (const const_matrix_iterator& other) const { return current_row == other.current_row && current_col == other.current_col && m == other.m; } //! Returns reference access to the element referenced by the iterator. //! The reference is only valid so long as the iterator is not moved. const ValueType& operator * () { acquire_current_iblock(); return (*current_iblock)[m->data->elem_index_in_block_from_elem(current_row, current_col)]; } }; //! row-major const_iterator that points to single elements inside a matrix //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block template class const_matrix_row_major_iterator : public const_matrix_iterator { protected: typedef const_matrix_iterator const_matrix_iterator_type; typedef typename const_matrix_iterator_type::matrix_type matrix_type; typedef typename const_matrix_iterator_type::elem_size_type elem_size_type; template friend class matrix; using const_matrix_iterator_type::m; using const_matrix_iterator_type::set_empty; //! create iterator pointing to given row and col const_matrix_row_major_iterator(const matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) : const_matrix_iterator_type(matrix, start_row, start_col) { } //! create empty iterator const_matrix_row_major_iterator(const matrix_type& matrix) : const_matrix_iterator_type(matrix) { } public: //! convert from matrix_iterator const_matrix_row_major_iterator(const const_matrix_row_major_iterator& matrix_iterator) : const_matrix_iterator_type(matrix_iterator) { } //! convert from matrix_iterator const_matrix_row_major_iterator(const const_matrix_iterator_type& matrix_iterator) : const_matrix_iterator_type(matrix_iterator) { } // Has to be not empty, else behavior is undefined. const_matrix_row_major_iterator& operator ++ () { if (get_col() + 1 < m->get_width()) // => not matrix_row_major_iterator the end of row, move right set_col(get_col() + 1); else if (get_row() + 1 < m->get_height()) // => at end of row but not last row, move to beginning of next row set_pos(get_row() + 1, 0); else // => at end of matrix, set to empty-state set_empty(); return *this; } // Has to be not empty, else behavior is undefined. const_matrix_row_major_iterator& operator -- () { if (get_col() - 1 >= 0) // => not at the beginning of row, move left set_col(get_col() - 1); else if (get_row() - 1 >= 0) // => at beginning of row but not first row, move to end of previous row set_pos(get_row() - 1, m->get_width() - 1); else // => at beginning of matrix, set to empty-state set_empty(); return *this; } using const_matrix_iterator_type::get_row; using const_matrix_iterator_type::get_col; using const_matrix_iterator_type::set_col; using const_matrix_iterator_type::set_pos; }; //! column-major const_iterator that points to single elements inside a matrix //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block template class const_matrix_col_major_iterator : public const_matrix_iterator { protected: typedef const_matrix_iterator const_matrix_iterator_type; typedef typename const_matrix_iterator_type::matrix_type matrix_type; typedef typename const_matrix_iterator_type::elem_size_type elem_size_type; template friend class matrix; using const_matrix_iterator_type::m; using const_matrix_iterator_type::set_empty; //! create iterator pointing to given row and col const_matrix_col_major_iterator(const matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) : const_matrix_iterator_type(matrix, start_row, start_col) { } //! create empty iterator const_matrix_col_major_iterator(const matrix_type& matrix) : const_matrix_iterator_type(matrix) { } public: //! convert from matrix_iterator const_matrix_col_major_iterator(const matrix_iterator& matrix_iterator) : const_matrix_iterator_type(matrix_iterator) { } //! convert from matrix_iterator const_matrix_col_major_iterator(const const_matrix_iterator_type& matrix_iterator) : const_matrix_iterator_type(matrix_iterator) { } // Has to be not empty, else behavior is undefined. const_matrix_col_major_iterator& operator ++ () { if (get_row() + 1 < m->get_height()) // => not at the end of col, move down set_row(get_row() + 1); else if (get_col() + 1 < m->get_width()) // => at end of col but not last col, move to beginning of next col set_pos(0, get_col() + 1); else // => at end of matrix, set to empty-state set_empty(); return *this; } // Has to be not empty, else behavior is undefined. const_matrix_col_major_iterator& operator -- () { if (get_row() - 1 >= 0) // => not at the beginning of col, move up set_row(get_row() - 1); else if (get_col() - 1 >= 0) // => at beginning of col but not first col, move to end of previous col set_pos(m->get_height() - 1, get_col() - 1); else // => at beginning of matrix, set to empty-state set_empty(); return *this; } using const_matrix_iterator_type::get_row; using const_matrix_iterator_type::get_col; using const_matrix_iterator_type::set_row; using const_matrix_iterator_type::set_pos; }; //! External matrix container. \n //! Introduction to matrix container: see \ref tutorial_matrix tutorial. \n //! Design and Internals of matrix container: see \ref design_matrix. //! //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam BlockSideLength side length of a matrix block //! //! Divides the matrix in square submatrices (blocks). //! Blocks can be swapped individually to and from external memory. //! They are only swapped if necessary to minimize I/O. template class matrix { protected: typedef matrix matrix_type; typedef swappable_block_matrix swappable_block_matrix_type; typedef counting_ptr swappable_block_matrix_pointer_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename swappable_block_matrix_type::size_type block_size_type; typedef typename swappable_block_matrix_type::elem_size_type elem_size_type; typedef matrix_local::matrix_operations Ops; typedef matrix_swappable_block swappable_block_type; public: typedef matrix_iterator iterator; typedef const_matrix_iterator const_iterator; typedef matrix_row_major_iterator row_major_iterator; typedef matrix_col_major_iterator col_major_iterator; typedef const_matrix_row_major_iterator const_row_major_iterator; typedef const_matrix_col_major_iterator const_col_major_iterator; typedef column_vector column_vector_type; typedef row_vector row_vector_type; protected: template friend class matrix_iterator; template friend class const_matrix_iterator; elem_size_type height, width; swappable_block_matrix_pointer_type data; public: //! \name Constructors/Destructors //! \{ //! Creates a new matrix of given dimensions. Elements' values are set to zero. //! \param bs block scheduler used //! \param height height of the created matrix //! \param width width of the created matrix matrix(block_scheduler_type& bs, const elem_size_type height, const elem_size_type width) : height(height), width(width), data( new swappable_block_matrix_type( bs, div_ceil(height, BlockSideLength), div_ceil(width, BlockSideLength)) ) { } matrix(block_scheduler_type& bs, const column_vector_type& left, const row_vector_type& right) : height((elem_size_type)left.size()), width((elem_size_type)right.size()), data( new swappable_block_matrix_type( bs, div_ceil(height, BlockSideLength), div_ceil(width, BlockSideLength)) ) { Ops::recursive_matrix_from_vectors(*data, left, right); } ~matrix() { } //! \} //! \name Capacity //! \{ const elem_size_type & get_height() const { return height; } const elem_size_type & get_width() const { return width; } //! \} //! \name Iterators //! \{ iterator begin() { data.unify(); return iterator(*this, 0, 0); } const_iterator begin() const { return const_iterator(*this, 0, 0); } const_iterator cbegin() const { return const_iterator(*this, 0, 0); } iterator end() { data.unify(); return iterator(*this); } const_iterator end() const { return const_iterator(*this); } const_iterator cend() const { return const_iterator(*this); } const_iterator operator () (const elem_size_type row, const elem_size_type col) const { return const_iterator(*this, row, col); } iterator operator () (const elem_size_type row, const elem_size_type col) { data.unify(); return iterator(*this, row, col); } //! \} //! \name Modifiers //! \{ void transpose() { data.unify(); data->transpose(); std::swap(height, width); } void set_zero() { if (data.unique()) data->set_zero(); else data = new swappable_block_matrix_type (data->bs, div_ceil(height, BlockSideLength), div_ceil(width, BlockSideLength)); } //! \} //! \name Operations //! \{ matrix_type operator + (const matrix_type& right) const { assert(height == right.height && width == right.width); matrix_type res(data->bs, height, width); Ops::element_op(*res.data, *data, *right.data, typename Ops::addition()); // more efficient than copying this and then adding right return res; } matrix_type operator - (const matrix_type& right) const { assert(height == right.height && width == right.width); matrix_type res(data->bs, height, width); Ops::element_op(*res.data, *data, *right.data, typename Ops::subtraction()); // more efficient than copying this and then subtracting right return res; } matrix_type operator * (const matrix_type& right) const { return multiply(right); } matrix_type operator * (const ValueType scalar) const { matrix_type res(data->bs, height, width); Ops::element_op(*res.data, *data, typename Ops::scalar_multiplication(scalar)); return res; } matrix_type& operator += (const matrix_type& right) { assert(height == right.height && width == right.width); data.unify(); Ops::element_op(*data, *right.data, typename Ops::addition()); return *this; } matrix_type& operator -= (const matrix_type& right) { assert(height == right.height && width == right.width); data.unify(); Ops::element_op(*data, *right.data, typename Ops::subtraction()); return *this; } matrix_type& operator *= (const matrix_type& right) { return *this = operator * (right); } // implicitly unifies by constructing a result-matrix matrix_type& operator *= (const ValueType scalar) { data.unify(); Ops::element_op(*data, typename Ops::scalar_multiplication(scalar)); return *this; } column_vector_type operator * (const column_vector_type& right) const { assert(elem_size_type(right.size()) == width); column_vector_type res(height); res.set_zero(); Ops::recursive_matrix_col_vector_multiply_and_add(*data, right, res); return res; } row_vector_type multiply_from_left(const row_vector_type& left) const { assert(elem_size_type(left.size()) == height); row_vector_type res(width); res.set_zero(); Ops::recursive_matrix_row_vector_multiply_and_add(left, *data, res); return res; } //! multiply with another matrix //! \param right matrix to multiply with //! \param multiplication_algorithm allows to choose the applied algorithm //! \param scheduling_algorithm allows to choose the applied algorithm //! //! Available algorithms are: \n //! 0: naive_multiply_and_add (I/O inefficient, slow) \n //! 1: recursive_multiply_and_add (recommended, default, stable time and I/O complexity) \n //! 2: strassen_winograd_multiply_and_add (sometimes fast but unstable time and I/O complexity) \n //! 3: multi_level_strassen_winograd_multiply_and_add (sometimes fast but unstable time and I/O complexity) \n //! 4: strassen_winograd_multiply, optimized pre- and postadditions (sometimes fast but unstable time and I/O complexity) \n //! 5: strassen_winograd_multiply_and_add_interleaved, optimized preadditions (sometimes fast but unstable time and I/O complexity) \n //! 6: multi_level_strassen_winograd_multiply_and_add_block_grained (sometimes fast but unstable time and I/O complexity) matrix_type multiply(const matrix_type& right, const int_type multiplication_algorithm = 1, const int_type scheduling_algorithm = 2) const { assert(width == right.height); assert(&data->bs == &right.data->bs); matrix_type res(data->bs, height, right.width); if (scheduling_algorithm > 0) { // all offline algos need a simulation-run delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_simulation(data->bs) ); switch (multiplication_algorithm) { case 0: Ops::naive_multiply_and_add(*data, *right.data, *res.data); break; case 1: Ops::recursive_multiply_and_add(*data, *right.data, *res.data); break; case 2: Ops::strassen_winograd_multiply_and_add(*data, *right.data, *res.data); break; case 3: Ops::multi_level_strassen_winograd_multiply_and_add(*data, *right.data, *res.data); break; case 4: Ops::strassen_winograd_multiply(*data, *right.data, *res.data); break; case 5: Ops::strassen_winograd_multiply_and_add_interleaved(*data, *right.data, *res.data); break; case 6: Ops::multi_level_strassen_winograd_multiply_and_add_block_grained(*data, *right.data, *res.data); break; default: STXXL_ERRMSG("invalid multiplication-algorithm number"); break; } } switch (scheduling_algorithm) { case 0: delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_online_lru(data->bs) ); break; case 1: delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_offline_lfd(data->bs) ); break; case 2: delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_offline_lru_prefetching(data->bs) ); break; default: STXXL_ERRMSG("invalid scheduling-algorithm number"); } switch (multiplication_algorithm) { case 0: Ops::naive_multiply_and_add(*data, *right.data, *res.data); break; case 1: Ops::recursive_multiply_and_add(*data, *right.data, *res.data); break; case 2: Ops::strassen_winograd_multiply_and_add(*data, *right.data, *res.data); break; case 3: Ops::multi_level_strassen_winograd_multiply_and_add(*data, *right.data, *res.data); break; case 4: Ops::strassen_winograd_multiply(*data, *right.data, *res.data); break; case 5: Ops::strassen_winograd_multiply_and_add_interleaved(*data, *right.data, *res.data); break; case 6: Ops::multi_level_strassen_winograd_multiply_and_add_block_grained(*data, *right.data, *res.data); break; default: STXXL_ERRMSG("invalid multiplication-algorithm number"); break; } delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_online_lru(data->bs) ); return res; } //! Use internal memory multiplication. Designated for testing. May exceed memory limitations. matrix_type multiply_internal(const matrix_type& right, const int_type scheduling_algorithm = 2) const { assert(width == right.height); assert(&data->bs == &right.data->bs); matrix_type res(data->bs, height, right.width); if (scheduling_algorithm > 0) { // all offline algos need a simulation-run delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_simulation(data->bs) ); multiply_internal(right, res); } switch (scheduling_algorithm) { case 0: delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_online_lru(data->bs) ); break; case 1: delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_offline_lfd(data->bs) ); break; case 2: delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_offline_lru_prefetching(data->bs) ); break; default: STXXL_ERRMSG("invalid scheduling-algorithm number"); } multiply_internal(right, res); delete data->bs.switch_algorithm_to( new block_scheduler_algorithm_online_lru(data->bs) ); return res; } //! \} protected: void multiply_internal(const matrix_type& right, matrix_type& res) const { ValueType* A = new ValueType[height * width]; ValueType* B = new ValueType[right.height * right.width]; ValueType* C = new ValueType[res.height * res.width]; ValueType* vit; vit = A; for (const_row_major_iterator mit = cbegin(); mit != cend(); ++mit, ++vit) *vit = *mit; vit = B; for (const_row_major_iterator mit = right.cbegin(); mit != right.cend(); ++mit, ++vit) *vit = *mit; if (! res.data->bs.is_simulating()) { #if STXXL_BLAS gemm_wrapper(height, width, res.width, ValueType(1), false, A, false, B, ValueType(0), false, C); #else assert(false /* internal multiplication is only available for testing with blas */); #endif } vit = C; for (row_major_iterator mit = res.begin(); mit != res.end(); ++mit, ++vit) *mit = *vit; delete[] A; delete[] B; delete[] C; } }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_MATRIX_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/pager.h000644 001411 000144 00000005072 12405375303 021665 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/pager.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2003, 2006 Roman Dementiev * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_PAGER_HEADER #define STXXL_CONTAINERS_PAGER_HEADER #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup stlcont_vector //! \{ enum pager_type { random, lru }; //! Pager with \b random replacement strategy template class random_pager { enum { n_pages = npages_ }; typedef unsigned_type size_type; size_type num_pages; random_number rnd; public: random_pager(size_type num_pages = n_pages) : num_pages(num_pages) { } size_type kick() { return rnd(size()); } void hit(size_type ipage) { STXXL_ASSERT(ipage < size()); } size_type size() const { return num_pages; } }; //! Pager with \b LRU replacement strategy template class lru_pager : private noncopyable { enum { n_pages = npages_ }; typedef unsigned_type size_type; typedef std::list list_type; list_type history; simple_vector history_entry; public: lru_pager(size_type num_pages = n_pages) : history_entry(num_pages) { for (size_type i = 0; i < size(); ++i) history_entry[i] = history.insert(history.end(), i); } size_type kick() { return history.back(); } void hit(size_type ipage) { assert(ipage < size()); history.splice(history.begin(), history, history_entry[ipage]); } void swap(lru_pager& obj) { history.swap(obj.history); history_entry.swap(obj.history_entry); } size_type size() const { return history_entry.size(); } }; //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::lru_pager& a, stxxl::lru_pager& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_PAGER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/sequence.h000644 001411 000144 00000067136 12414452316 022410 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/sequence.h * * based on include/stxxl/bits/containers/queue.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012-2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_SEQUENCE_HEADER #define STXXL_CONTAINERS_SEQUENCE_HEADER #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE #ifndef STXXL_VERBOSE_SEQUENCE #define STXXL_VERBOSE_SEQUENCE STXXL_VERBOSE2 #endif //! \addtogroup stlcont //! \{ //! External sequence or deque container without random access. \n //! Introduction to sequence container: see \ref tutorial_sequence tutorial. \n //! Design and Internals of sequence container: see \ref design_queue /** * Sequence is a primitive container consisting of only a sequence of blocks in * external memory. The sequence provides appending methods similar to a deque: * push_back and push_front; and also the corresponding pop functions. However, * different from stxxl::deque (which is a vector in disguise), the sequence * does not allow random access. Instead, the sequence can only be iterated * using streams: either from front to back or in reverse. * * As with queue and stack, sequences of pushes and pops are made efficient * using overlapping or read-ahead via block pools. The stream access likewise * uses overlapped I/O, just like stream::vector_iterator2stream. * * \tparam ValueType type of the contained objects (POD with no references to internal memory) * \tparam BlockSize size of the external memory block in bytes, default is \c STXXL_DEFAULT_BLOCK_SIZE(ValTp) * \tparam AllocStr parallel disk allocation strategy, default is \c STXXL_DEFAULT_ALLOC_STRATEGY * \tparam SizeType size data type, default is \c stxxl::uint64 */ template class sequence : private noncopyable { public: typedef ValueType value_type; typedef AllocStr alloc_strategy_type; typedef SizeType size_type; enum { block_size = BlockSize }; typedef typed_block block_type; typedef BID bid_type; typedef std::deque bid_deque_type; private: typedef read_write_pool pool_type; /// current number of items in the sequence size_type m_size; /// whether the m_pool object is own and should be deleted. bool m_owns_pool; /// read_write_pool of blocks pool_type* m_pool; /// current front block of sequence block_type* m_front_block; /// current back block of sequence block_type* m_back_block; /// pointer to current front element in m_front_block value_type* m_front_element; /// pointer to current back element in m_back_block value_type* m_back_element; /// block allocation strategy alloc_strategy_type m_alloc_strategy; /// block allocation counter unsigned_type m_alloc_count; /// allocated block identifiers bid_deque_type m_bids; /// block manager used block_manager* m_bm; /// number of blocks to prefetch unsigned_type m_blocks2prefetch; public: //! \name Constructors/Destructors //! \{ //! Constructs empty sequence with own write and prefetch block pool //! //! \param D number of parallel disks, defaulting to the configured number of scratch disks, //! memory consumption will be 2 * D + 2 blocks //! (first and last block, D blocks as write cache, D block for prefetching) explicit sequence(int_type D = -1) : m_size(0), m_owns_pool(true), m_alloc_count(0), m_bm(block_manager::get_instance()) { if (D < 1) D = config::get_instance()->disks_number(); STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::sequence(D)"); m_pool = new pool_type(D, D + 2); init(); } //! Constructs empty sequence with own write and prefetch block pool //! //! \param w_pool_size number of blocks in the write pool, must be at least 2, recommended at least 3 //! \param p_pool_size number of blocks in the prefetch pool, recommended at least 1 //! \param blocks2prefetch defines the number of blocks to prefetch (\c front side), //! default is number of block in the prefetch pool explicit sequence(unsigned_type w_pool_size, unsigned_type p_pool_size, int blocks2prefetch = -1) : m_size(0), m_owns_pool(true), m_alloc_count(0), m_bm(block_manager::get_instance()) { STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::sequence(sizes)"); m_pool = new pool_type(p_pool_size, w_pool_size); init(blocks2prefetch); } //! Constructs empty sequence //! //! \param pool block write/prefetch pool //! \param blocks2prefetch defines the number of blocks to prefetch (\c front side), default is number of blocks in the prefetch pool //! \warning Number of blocks in the write pool must be at least 2, recommended at least 3 //! \warning Number of blocks in the prefetch pool recommended at least 1 sequence(pool_type& pool, int blocks2prefetch = -1) : m_size(0), m_owns_pool(false), m_pool(&pool), m_alloc_count(0), m_bm(block_manager::get_instance()) { STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::sequence(pool)"); init(blocks2prefetch); } //! \} //! \name Modifiers //! \{ void swap(sequence& obj) { std::swap(m_size, obj.m_size); std::swap(m_owns_pool, obj.m_owns_pool); std::swap(m_pool, obj.m_pool); std::swap(m_front_block, obj.m_front_block); std::swap(m_back_block, obj.m_back_block); std::swap(m_front_element, obj.m_front_element); std::swap(m_back_element, obj.m_back_element); std::swap(m_alloc_strategy, obj.m_alloc_strategy); std::swap(m_alloc_count, obj.m_alloc_count); std::swap(m_bids, obj.m_bids); std::swap(m_bm, obj.m_bm); std::swap(m_blocks2prefetch, obj.m_blocks2prefetch); } //! \} private: void init(int blocks2prefetch = -1) { if (m_pool->size_write() < 2) { STXXL_ERRMSG("sequence: invalid configuration, not enough blocks (" << m_pool->size_write() << ") in write pool, at least 2 are needed, resizing to 3"); m_pool->resize_write(3); } if (m_pool->size_write() < 3) { STXXL_MSG("sequence: inefficient configuration, no blocks for buffered writing available"); } if (m_pool->size_prefetch() < 1) { STXXL_MSG("sequence: inefficient configuration, no blocks for prefetching available"); } /// initialize empty sequence m_front_block = m_back_block = m_pool->steal(); m_back_element = m_back_block->begin() - 1; m_front_element = m_back_block->begin(); set_prefetch_aggr(blocks2prefetch); } public: //! \name Miscellaneous //! \{ //! Defines the number of blocks to prefetch (\c front side). //! This method should be called whenever the prefetch pool is resized //! \param blocks2prefetch defines the number of blocks to prefetch (\c front side), //! a negative value means to use the number of blocks in the prefetch pool void set_prefetch_aggr(int_type blocks2prefetch) { if (blocks2prefetch < 0) m_blocks2prefetch = m_pool->size_prefetch(); else m_blocks2prefetch = blocks2prefetch; } //! Returns the number of blocks prefetched from the \c front side unsigned_type get_prefetch_aggr() const { return m_blocks2prefetch; } //! \} //! \name Modifiers //! \{ //! Adds an element to the front of the sequence void push_front(const value_type& val) { if (UNLIKELY(m_front_element == m_front_block->begin())) { if (m_size == 0) { STXXL_VERBOSE1("sequence::push_front Case 0"); assert(m_front_block == m_back_block); m_front_element = m_back_element = m_front_block->end() - 1; *m_front_element = val; ++m_size; return; } // front block is completely filled else if (m_front_block == m_back_block) { // can not write the front block because it // is the same as the back block, must keep it memory STXXL_VERBOSE1("sequence::push_front Case 1"); } else if (size() < 2 * block_type::size) { STXXL_VERBOSE1("sequence::push_front Case 1.5"); // only two blocks with a gap at the end, move elements within memory assert(m_bids.empty()); size_t gap = m_back_block->end() - (m_back_element + 1); assert(gap > 0); std::copy_backward(m_back_block->begin(), m_back_element + 1, m_back_block->end()); std::copy_backward(m_front_block->end() - gap, m_front_block->end(), m_back_block->begin() + gap); std::copy_backward(m_front_block->begin(), m_front_block->end() - gap, m_front_block->end()); m_front_element += gap; m_back_element += gap; --m_front_element; *m_front_element = val; ++m_size; return; } else { STXXL_VERBOSE1("sequence::push_front Case 2"); // write the front block // need to allocate new block bid_type newbid; m_bm->new_block(m_alloc_strategy, newbid, m_alloc_count++); STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: push_front block " << m_front_block << " @ " << FMT_BID(newbid)); m_bids.push_front(newbid); m_pool->write(m_front_block, newbid); if (m_bids.size() <= m_blocks2prefetch) { STXXL_VERBOSE1("sequence::push Case Hints"); m_pool->hint(newbid); } } m_front_block = m_pool->steal(); m_front_element = m_front_block->end() - 1; *m_front_element = val; ++m_size; } else // not at beginning of a block { --m_front_element; *m_front_element = val; ++m_size; } } //! Adds an element to the end of the sequence void push_back(const value_type& val) { if (UNLIKELY(m_back_element == m_back_block->begin() + (block_type::size - 1))) { // back block is completely filled if (m_front_block == m_back_block) { // can not write the back block because it // is the same as the front block, must keep it memory STXXL_VERBOSE1("sequence::push_back Case 1"); } else if (size() < 2 * block_type::size) { STXXL_VERBOSE1("sequence::push_back Case 1.5"); // only two blocks with a gap in the beginning, move elements within memory assert(m_bids.empty()); size_t gap = m_front_element - m_front_block->begin(); assert(gap > 0); std::copy(m_front_element, m_front_block->end(), m_front_block->begin()); std::copy(m_back_block->begin(), m_back_block->begin() + gap, m_front_block->begin() + (block_type::size - gap)); std::copy(m_back_block->begin() + gap, m_back_block->end(), m_back_block->begin()); m_front_element -= gap; m_back_element -= gap; ++m_back_element; *m_back_element = val; ++m_size; return; } else { STXXL_VERBOSE1("sequence::push_back Case 2"); // write the back block // need to allocate new block bid_type newbid; m_bm->new_block(m_alloc_strategy, newbid, m_alloc_count++); STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: push_back block " << m_back_block << " @ " << FMT_BID(newbid)); m_bids.push_back(newbid); m_pool->write(m_back_block, newbid); if (m_bids.size() <= m_blocks2prefetch) { STXXL_VERBOSE1("sequence::push_back Case Hints"); m_pool->hint(newbid); } } m_back_block = m_pool->steal(); m_back_element = m_back_block->begin(); *m_back_element = val; ++m_size; } else // not at end of a block { ++m_back_element; *m_back_element = val; ++m_size; } } //! Removes element from the front of the sequence void pop_front() { assert(!empty()); if (UNLIKELY(m_front_element == m_front_block->begin() + (block_type::size - 1))) { // if there is only one block, it implies ... if (m_back_block == m_front_block) { STXXL_VERBOSE1("sequence::pop_front Case 1"); assert(size() == 1); assert(m_back_element == m_front_element); assert(m_bids.empty()); // reset everything m_back_element = m_back_block->begin() - 1; m_front_element = m_back_block->begin(); m_size = 0; return; } --m_size; if (m_size <= block_type::size) { STXXL_VERBOSE1("sequence::pop_front Case 2"); assert(m_bids.empty()); // the m_back_block is the next block m_pool->add(m_front_block); m_front_block = m_back_block; m_front_element = m_back_block->begin(); return; } STXXL_VERBOSE1("sequence::pop_front Case 3"); assert(!m_bids.empty()); request_ptr req = m_pool->read(m_front_block, m_bids.front()); STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: pop_front block " << m_front_block << " @ " << FMT_BID(m_bids.front())); // give prefetching hints for (unsigned_type i = 0; i < m_blocks2prefetch && i < m_bids.size() - 1; ++i) { STXXL_VERBOSE1("sequence::pop_front Case Hints"); m_pool->hint(m_bids[i + 1]); } m_front_element = m_front_block->begin(); req->wait(); m_bm->delete_block(m_bids.front()); m_bids.pop_front(); } else { ++m_front_element; --m_size; } } //! Removes element from the back of the sequence void pop_back() { assert(!empty()); if (UNLIKELY(m_back_element == m_back_block->begin())) { // if there is only one block, it implies ... if (m_back_block == m_front_block) { STXXL_VERBOSE1("sequence::pop_back Case 1"); assert(size() == 1); assert(m_back_element == m_front_element); assert(m_bids.empty()); // reset everything m_back_element = m_back_block->begin() - 1; m_front_element = m_back_block->begin(); m_size = 0; return; } --m_size; if (m_size <= block_type::size) { STXXL_VERBOSE1("sequence::pop_back Case 2"); assert(m_bids.empty()); // the m_front_block is the next block m_pool->add(m_back_block); m_back_block = m_front_block; m_back_element = m_back_block->end() - 1; return; } STXXL_VERBOSE1("sequence::pop_back Case 3"); assert(!m_bids.empty()); request_ptr req = m_pool->read(m_back_block, m_bids.back()); STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: pop_back block " << m_back_block << " @ " << FMT_BID(m_bids.back())); // give prefetching hints for (unsigned_type i = 1; i < m_blocks2prefetch && i < m_bids.size() - 1; ++i) { STXXL_VERBOSE1("sequence::pop_front Case Hints"); m_pool->hint(m_bids[m_bids.size() - 1 - i]); } m_back_element = m_back_block->end() - 1; req->wait(); m_bm->delete_block(m_bids.back()); m_bids.pop_back(); } else { --m_back_element; --m_size; } } //! \} //! \name Capacity //! \{ //! Returns the size of the sequence size_type size() const { return m_size; } //! Returns \c true if sequence is empty bool empty() const { return (m_size == 0); } //! \} //! \name Operators //! \{ //! Returns a mutable reference at the back of the sequence value_type & back() { assert(!empty()); return *m_back_element; } //! Returns a const reference at the back of the sequence const value_type & back() const { assert(!empty()); return *m_back_element; } //! Returns a mutable reference at the front of the sequence value_type & front() { assert(!empty()); return *m_front_element; } //! Returns a const reference at the front of the sequence const value_type & front() const { assert(!empty()); return *m_front_element; } //! \} //! \name Constructors/Destructors //! \{ ~sequence() { if (m_front_block != m_back_block) m_pool->add(m_back_block); m_pool->add(m_front_block); if (m_owns_pool) delete m_pool; if (!m_bids.empty()) m_bm->delete_blocks(m_bids.begin(), m_bids.end()); } //! \} /**************************************************************************/ class stream { public: typedef typename sequence::value_type value_type; typedef typename bid_deque_type::const_iterator bid_iter_type; protected: const sequence& m_sequence; size_type m_size; value_type* m_current_element; block_type* m_current_block; bid_iter_type m_next_bid; public: stream(const sequence& sequence) : m_sequence(sequence), m_size(sequence.size()) { m_current_block = sequence.m_front_block; m_current_element = sequence.m_front_element; m_next_bid = sequence.m_bids.begin(); } ~stream() { if (m_current_block != m_sequence.m_front_block && m_current_block != m_sequence.m_back_block) // give m_current_block back to pool m_sequence.m_pool->add(m_current_block); } //! return number of element left till end-of-stream. size_type size() const { return m_size; } //! standard stream method bool empty() const { return (m_size == 0); } //! standard stream method const value_type& operator * () const { assert(!empty()); return *m_current_element; } //! standard stream method stream& operator ++ () { assert(!empty()); if (UNLIKELY(m_current_element == m_current_block->begin() + (block_type::size - 1))) { // next item position is beyond end of current block, find next block --m_size; if (m_size == 0) { STXXL_VERBOSE1("sequence::stream::operator++ last block finished clean at block end"); assert(m_next_bid == m_sequence.m_bids.end()); assert(m_current_block == m_sequence.m_back_block); // nothing to give back to sequence pool m_current_element = NULL; return *this; } else if (m_size <= block_type::size) // still items left in last partial block { STXXL_VERBOSE1("sequence::stream::operator++ reached last block"); assert(m_next_bid == m_sequence.m_bids.end()); // the m_back_block is the next block if (m_current_block != m_sequence.m_front_block) // give current_block back to pool m_sequence.m_pool->add(m_current_block); m_current_block = m_sequence.m_back_block; m_current_element = m_current_block->begin(); return *this; } else if (m_current_block == m_sequence.m_front_block) { STXXL_VERBOSE1("sequence::stream::operator++ first own-block case: steal block from sequence's pool"); m_current_block = m_sequence.m_pool->steal(); } STXXL_VERBOSE1("sequence::stream::operator++ default case: fetch next block"); assert(m_next_bid != m_sequence.m_bids.end()); request_ptr req = m_sequence.m_pool->read(m_current_block, *m_next_bid); STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::stream::operator++ read block " << m_current_block << " @ " << FMT_BID(*m_next_bid)); // give prefetching hints bid_iter_type bid = m_next_bid + 1; for (unsigned_type i = 0; i < m_sequence.m_blocks2prefetch && bid != m_sequence.m_bids.end(); ++i, ++bid) { STXXL_VERBOSE1("sequence::stream::operator++ giving prefetch hints"); m_sequence.m_pool->hint(*bid); } m_current_element = m_current_block->begin(); req->wait(); ++m_next_bid; } else { --m_size; ++m_current_element; } return *this; } }; //! \name Miscellaneous //! \{ //! construct a forward stream from this sequence stream get_stream() { return stream(*this); } //! \} /**************************************************************************/ class reverse_stream { public: typedef typename sequence::value_type value_type; typedef typename bid_deque_type::const_reverse_iterator bid_iter_type; protected: const sequence& m_sequence; size_type m_size; value_type* m_current_element; block_type* m_current_block; bid_iter_type m_next_bid; public: reverse_stream(const sequence& sequence) : m_sequence(sequence), m_size(sequence.size()) { m_current_block = sequence.m_back_block; m_current_element = sequence.m_back_element; m_next_bid = sequence.m_bids.rbegin(); } ~reverse_stream() { if (m_current_block != m_sequence.m_front_block && m_current_block != m_sequence.m_back_block) // give m_current_block back to pool m_sequence.m_pool->add(m_current_block); } //! return number of element left till end-of-stream. size_type size() const { return m_size; } //! standard stream method bool empty() const { return (m_size == 0); } //! standard stream method const value_type& operator * () const { assert(!empty()); return *m_current_element; } //! standard stream method reverse_stream& operator ++ () { assert(!empty()); if (UNLIKELY(m_current_element == m_current_block->begin())) { // next item position is beyond begin of current block, find next block --m_size; if (m_size == 0) { STXXL_VERBOSE1("sequence::reverse_stream::operator++ last block finished clean at block begin"); assert(m_next_bid == m_sequence.m_bids.rend()); assert(m_current_block == m_sequence.m_front_block); // nothing to give back to sequence pool m_current_element = NULL; return *this; } else if (m_size <= block_type::size) { STXXL_VERBOSE1("sequence::reverse_stream::operator++ reached first block"); assert(m_next_bid == m_sequence.m_bids.rend()); // the m_back_block is the next block if (m_current_block != m_sequence.m_back_block) // give current_block back to pool m_sequence.m_pool->add(m_current_block); m_current_block = m_sequence.m_front_block; m_current_element = m_current_block->begin() + (block_type::size - 1); return *this; } else if (m_current_block == m_sequence.m_back_block) { STXXL_VERBOSE1("sequence::reverse_stream::operator++ first own-block case: steal block from sequence's pool"); m_current_block = m_sequence.m_pool->steal(); } STXXL_VERBOSE1("sequence::reverse_stream::operator++ default case: fetch previous block"); assert(m_next_bid != m_sequence.m_bids.rend()); request_ptr req = m_sequence.m_pool->read(m_current_block, *m_next_bid); STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::reverse_stream::operator++ read block " << m_current_block << " @ " << FMT_BID(*m_next_bid)); // give prefetching hints bid_iter_type bid = m_next_bid + 1; for (unsigned_type i = 0; i < m_sequence.m_blocks2prefetch && bid != m_sequence.m_bids.rend(); ++i, ++bid) { STXXL_VERBOSE1("sequence::reverse_stream::operator++ giving prefetch hints"); m_sequence.m_pool->hint(*bid); } m_current_element = m_current_block->begin() + (block_type::size - 1); req->wait(); ++m_next_bid; } else { --m_size; --m_current_element; } return *this; } }; //! \name Miscellaneous //! \{ //! construct a reverse stream from this sequence reverse_stream get_reverse_stream() { return reverse_stream(*this); } //! \} }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_SEQUENCE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/pq_mergers.h000644 001411 000144 00000016562 12411366426 022744 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/pq_mergers.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 1999 Peter Sanders * Copyright (C) 2003, 2004, 2007 Roman Dementiev * Copyright (C) 2007-2009 Johannes Singler * Copyright (C) 2007, 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_PQ_MERGERS_HEADER #define STXXL_CONTAINERS_PQ_MERGERS_HEADER #include STXXL_BEGIN_NAMESPACE //! \addtogroup stlcontinternals //! //! \{ /*! \internal */ namespace priority_queue_local { ///////////////////////////////////////////////////////////////////// // auxiliary functions // merge length elements from the two sentinel terminated input // sequences source0 and source1 to target // advance source0 and source1 accordingly // require: at least length nonsentinel elements available in source0, source1 // require: target may overwrite one of the sources as long as // *(sourcex + length) is before the end of sourcex template void merge_iterator( InputIterator& source0, InputIterator& source1, OutputIterator target, SizeType length, CompareType& cmp) { OutputIterator done = target + length; while (target != done) { if (cmp(*source0, *source1)) { *target = *source1; ++source1; } else { *target = *source0; ++source0; } ++target; } } // merge length elements from the three sentinel terminated input // sequences source0, source1 and source2 to target // advance source0, source1 and source2 accordingly // require: at least length nonsentinel elements available in source0, source1 and source2 // require: target may overwrite one of the sources as long as // *(sourcex + length) is before the end of sourcex template void merge3_iterator( InputIterator& source0, InputIterator& source1, InputIterator& source2, OutputIterator target, SizeType length, CompareType& cmp) { OutputIterator done = target + length; if (cmp(*source1, *source0)) { if (cmp(*source2, *source1)) { goto s012; } else { if (cmp(*source0, *source2)) { goto s201; } else { goto s021; } } } else { if (cmp(*source2, *source1)) { if (cmp(*source2, *source0)) { goto s102; } else { goto s120; } } else { goto s210; } } #define Merge3Case(a, b, c) \ s ## a ## b ## c : \ if (target == done) \ return; \ *target = *source ## a; \ ++target; \ ++source ## a; \ if (cmp(*source ## b, *source ## a)) \ goto s ## a ## b ## c; \ if (cmp(*source ## c, *source ## a)) \ goto s ## b ## a ## c; \ goto s ## b ## c ## a; // the order is chosen in such a way that // four of the trailing gotos can be eliminated by the optimizer Merge3Case(0, 1, 2); Merge3Case(1, 2, 0); Merge3Case(2, 0, 1); Merge3Case(1, 0, 2); Merge3Case(0, 2, 1); Merge3Case(2, 1, 0); #undef Merge3Case } // merge length elements from the four sentinel terminated input // sequences source0, source1, source2 and source3 to target // advance source0, source1, source2 and source3 accordingly // require: at least length nonsentinel elements available in source0, source1, source2 and source3 // require: target may overwrite one of the sources as long as // *(sourcex + length) is before the end of sourcex template void merge4_iterator( InputIterator& source0, InputIterator& source1, InputIterator& source2, InputIterator& source3, OutputIterator target, SizeType length, CompareType& cmp) { OutputIterator done = target + length; #define StartMerge4(a, b, c, d) \ if ((!cmp(*source ## a, *source ## b)) && \ (!cmp(*source ## b, *source ## c)) && \ (!cmp(*source ## c, *source ## d))) \ goto s ## a ## b ## c ## d; // b>a c>b d>c // ab) !(b>c) !(c>d) StartMerge4(0, 1, 2, 3); StartMerge4(1, 2, 3, 0); StartMerge4(2, 3, 0, 1); StartMerge4(3, 0, 1, 2); StartMerge4(0, 3, 1, 2); StartMerge4(3, 1, 2, 0); StartMerge4(1, 2, 0, 3); StartMerge4(2, 0, 3, 1); StartMerge4(0, 2, 3, 1); StartMerge4(2, 3, 1, 0); StartMerge4(3, 1, 0, 2); StartMerge4(1, 0, 2, 3); StartMerge4(2, 0, 1, 3); StartMerge4(0, 1, 3, 2); StartMerge4(1, 3, 2, 0); StartMerge4(3, 2, 0, 1); StartMerge4(3, 0, 2, 1); StartMerge4(0, 2, 1, 3); StartMerge4(2, 1, 3, 0); StartMerge4(1, 3, 0, 2); StartMerge4(1, 0, 3, 2); StartMerge4(0, 3, 2, 1); StartMerge4(3, 2, 1, 0); StartMerge4(2, 1, 0, 3); #define Merge4Case(a, b, c, d) \ s ## a ## b ## c ## d : \ if (target == done) \ return; \ *target = *source ## a; \ ++target; \ ++source ## a; \ if (cmp(*source ## c, *source ## a)) \ { \ if (cmp(*source ## b, *source ## a)) \ goto s ## a ## b ## c ## d; \ else \ goto s ## b ## a ## c ## d; \ } \ else \ { \ if (cmp(*source ## d, *source ## a)) \ goto s ## b ## c ## a ## d; \ else \ goto s ## b ## c ## d ## a; \ } Merge4Case(0, 1, 2, 3); Merge4Case(1, 2, 3, 0); Merge4Case(2, 3, 0, 1); Merge4Case(3, 0, 1, 2); Merge4Case(0, 3, 1, 2); Merge4Case(3, 1, 2, 0); Merge4Case(1, 2, 0, 3); Merge4Case(2, 0, 3, 1); Merge4Case(0, 2, 3, 1); Merge4Case(2, 3, 1, 0); Merge4Case(3, 1, 0, 2); Merge4Case(1, 0, 2, 3); Merge4Case(2, 0, 1, 3); Merge4Case(0, 1, 3, 2); Merge4Case(1, 3, 2, 0); Merge4Case(3, 2, 0, 1); Merge4Case(3, 0, 2, 1); Merge4Case(0, 2, 1, 3); Merge4Case(2, 1, 3, 0); Merge4Case(1, 3, 0, 2); Merge4Case(1, 0, 3, 2); Merge4Case(0, 3, 2, 1); Merge4Case(3, 2, 1, 0); Merge4Case(2, 1, 0, 3); #undef StartMerge4 #undef Merge4Case } } // namespace priority_queue_local //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_PQ_MERGERS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/queue.h000644 001411 000144 00000032447 12411366426 021724 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/queue.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_QUEUE_HEADER #define STXXL_CONTAINERS_QUEUE_HEADER #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE #ifndef STXXL_VERBOSE_QUEUE #define STXXL_VERBOSE_QUEUE STXXL_VERBOSE2 #endif //! \addtogroup stlcont //! \{ //! External FIFO queue container. \n //! Introduction to queue container: see \ref tutorial_queue tutorial\n //! Design and Internals of queue container: see \ref design_queue. //! //! \tparam ValueType type of the contained objects (POD with no references to internal memory) //! \tparam BlockSize size of the external memory block in bytes, default is \c STXXL_DEFAULT_BLOCK_SIZE(ValueType) //! \tparam AllocStr parallel disk allocation strategy, default is \c STXXL_DEFAULT_ALLOC_STRATEGY //! \tparam SizeType size data type, default is \c stxxl::uint64 template class queue : private noncopyable { public: typedef ValueType value_type; typedef AllocStr alloc_strategy_type; typedef SizeType size_type; enum { block_size = BlockSize }; typedef typed_block block_type; typedef BID bid_type; private: typedef read_write_pool pool_type; size_type m_size; bool delete_pool; pool_type* pool; block_type* front_block; block_type* back_block; value_type* front_element; value_type* back_element; alloc_strategy_type alloc_strategy; unsigned_type alloc_count; std::deque bids; block_manager* bm; unsigned_type blocks2prefetch; public: //! \name Constructors/Destructors //! \{ //! Constructs empty queue with own write and prefetch block pool. //! //! \param D number of parallel disks, defaulting to the configured number of scratch disks, //! memory consumption will be 2 * D + 2 blocks //! (first and last block, D blocks as write cache, D block for prefetching) explicit queue(int_type D = -1) : m_size(0), delete_pool(true), alloc_count(0), bm(block_manager::get_instance()) { if (D < 1) D = config::get_instance()->disks_number(); STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(D)"); pool = new pool_type(D, D + 2); init(); } //! Constructs empty queue with own write and prefetch block pool. //! //! \param w_pool_size number of blocks in the write pool, must be at least 2, recommended at least 3 //! \param p_pool_size number of blocks in the prefetch pool, recommended at least 1 //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), //! default is number of block in the prefetch pool explicit queue(unsigned_type w_pool_size, unsigned_type p_pool_size, int blocks2prefetch_ = -1) : m_size(0), delete_pool(true), alloc_count(0), bm(block_manager::get_instance()) { STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(sizes)"); pool = new pool_type(p_pool_size, w_pool_size); init(blocks2prefetch_); } //! Constructs empty queue. //! //! \param w_pool write pool //! \param p_pool prefetch pool //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), //! default is number of blocks in the prefetch pool //! \warning Number of blocks in the write pool must be at least 2, recommended at least 3 //! \warning Number of blocks in the prefetch pool recommended at least 1 STXXL_DEPRECATED( queue(write_pool& w_pool, prefetch_pool& p_pool, int blocks2prefetch_ = -1)) : m_size(0), delete_pool(true), alloc_count(0), bm(block_manager::get_instance()) { STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(pools)"); pool = new pool_type(p_pool, w_pool); init(blocks2prefetch_); } //! Constructs empty queue. //! //! \param pool_ block write/prefetch pool //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), //! default is number of blocks in the prefetch pool //! \warning Number of blocks in the write pool must be at least 2, recommended at least 3 //! \warning Number of blocks in the prefetch pool recommended at least 1 queue(pool_type& pool_, int blocks2prefetch_ = -1) : m_size(0), delete_pool(false), pool(&pool_), alloc_count(0), bm(block_manager::get_instance()) { STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(pool)"); init(blocks2prefetch_); } //! \} //! \name Modifiers //! \{ void swap(queue& obj) { std::swap(m_size, obj.m_size); std::swap(delete_pool, obj.delete_pool); std::swap(pool, obj.pool); std::swap(front_block, obj.front_block); std::swap(back_block, obj.back_block); std::swap(front_element, obj.front_element); std::swap(back_element, obj.back_element); std::swap(alloc_strategy, obj.alloc_strategy); std::swap(alloc_count, obj.alloc_count); std::swap(bids, obj.bids); std::swap(bm, obj.bm); std::swap(blocks2prefetch, obj.blocks2prefetch); } //! \} private: void init(int blocks2prefetch_ = -1) { if (pool->size_write() < 2) { STXXL_ERRMSG("queue: invalid configuration, not enough blocks (" << pool->size_write() << ") in write pool, at least 2 are needed, resizing to 3"); pool->resize_write(3); } if (pool->size_write() < 3) { STXXL_MSG("queue: inefficient configuration, no blocks for buffered writing available"); } if (pool->size_prefetch() < 1) { STXXL_MSG("queue: inefficient configuration, no blocks for prefetching available"); } front_block = back_block = pool->steal(); back_element = back_block->begin() - 1; front_element = back_block->begin(); set_prefetch_aggr(blocks2prefetch_); } public: //! \name Miscellaneous //! \{ //! Defines the number of blocks to prefetch (\c front side). //! This method should be called whenever the prefetch pool is resized //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), //! a negative value means to use the number of blocks in the prefetch pool void set_prefetch_aggr(int_type blocks2prefetch_) { if (blocks2prefetch_ < 0) blocks2prefetch = pool->size_prefetch(); else blocks2prefetch = blocks2prefetch_; } //! Returns the number of blocks prefetched from the \c front side. unsigned_type get_prefetch_aggr() const { return blocks2prefetch; } //! \} //! \name Modifiers //! \{ //! Adds an element in the queue. void push(const value_type& val) { if (UNLIKELY(back_element == back_block->begin() + (block_type::size - 1))) { // back block is filled if (front_block == back_block) { // can not write the back block because it // is the same as the front block, must keep it memory STXXL_VERBOSE1("queue::push Case 1"); } else if (size() < 2 * block_type::size) { STXXL_VERBOSE1("queue::push Case 1.5"); // only two blocks with a gap in the beginning, move elements within memory assert(bids.empty()); size_t gap = front_element - front_block->begin(); assert(gap > 0); std::copy(front_element, front_block->end(), front_block->begin()); std::copy(back_block->begin(), back_block->begin() + gap, front_block->begin() + (block_type::size - gap)); std::copy(back_block->begin() + gap, back_block->end(), back_block->begin()); front_element -= gap; back_element -= gap; ++back_element; *back_element = val; ++m_size; return; } else { STXXL_VERBOSE1("queue::push Case 2"); // write the back block // need to allocate new block bid_type newbid; bm->new_block(alloc_strategy, newbid, alloc_count++); STXXL_VERBOSE_QUEUE("queue[" << this << "]: push block " << back_block << " @ " << FMT_BID(newbid)); bids.push_back(newbid); pool->write(back_block, newbid); if (bids.size() <= blocks2prefetch) { STXXL_VERBOSE1("queue::push Case Hints"); pool->hint(newbid); } } back_block = pool->steal(); back_element = back_block->begin(); *back_element = val; ++m_size; return; } ++back_element; *back_element = val; ++m_size; } //! Removes element from the queue. void pop() { assert(!empty()); if (UNLIKELY(front_element == front_block->begin() + (block_type::size - 1))) { // if there is only one block, it implies ... if (back_block == front_block) { STXXL_VERBOSE1("queue::pop Case 3"); assert(size() == 1); assert(back_element == front_element); assert(bids.empty()); // reset everything back_element = back_block->begin() - 1; front_element = back_block->begin(); m_size = 0; return; } --m_size; if (m_size <= block_type::size) { STXXL_VERBOSE1("queue::pop Case 4"); assert(bids.empty()); // the back_block is the next block pool->add(front_block); front_block = back_block; front_element = back_block->begin(); return; } STXXL_VERBOSE1("queue::pop Case 5"); assert(!bids.empty()); request_ptr req = pool->read(front_block, bids.front()); STXXL_VERBOSE_QUEUE("queue[" << this << "]: pop block " << front_block << " @ " << FMT_BID(bids.front())); // give prefetching hints for (unsigned_type i = 0; i < blocks2prefetch && i < bids.size() - 1; ++i) { STXXL_VERBOSE1("queue::pop Case Hints"); pool->hint(bids[i + 1]); } front_element = front_block->begin(); req->wait(); bm->delete_block(bids.front()); bids.pop_front(); return; } ++front_element; --m_size; } //! \} //! \name Operators //! \{ //! Returns a mutable reference at the back of the queue. value_type & back() { assert(!empty()); return *back_element; } //! Returns a const reference at the back of the queue. const value_type & back() const { assert(!empty()); return *back_element; } //! Returns a mutable reference at the front of the queue. value_type & front() { assert(!empty()); return *front_element; } //! Returns a const reference at the front of the queue. const value_type & front() const { assert(!empty()); return *front_element; } //! \} //! \name Constructors/Destructors //! \{ ~queue() { if (front_block != back_block) pool->add(back_block); pool->add(front_block); if (delete_pool) { delete pool; } if (!bids.empty()) bm->delete_blocks(bids.begin(), bids.end()); } //! \} //! \name Capacity //! \{ //! Returns the size of the queue. size_type size() const { return m_size; } //! Returns \c true if queue is empty. bool empty() const { return (m_size == 0); } //! \} }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_QUEUE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/hash_map/block_cache.h000644 001411 000144 00000043751 12411366426 024575 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/hash_map/block_cache.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_HASH_MAP_BLOCK_CACHE_HEADER #define STXXL_CONTAINERS_HASH_MAP_BLOCK_CACHE_HEADER #ifdef STXXL_BOOST_CONFIG #include #endif #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE namespace hash_map { //! Used inside block_cache for buffering write requests of cached blocks. template class block_cache_write_buffer : private noncopyable { public: typedef BlockType block_type; typedef typename block_type::bid_type bid_type; protected: std::vector blocks_; std::vector reqs_; std::vector free_blocks_; std::list busy_blocks_; // TODO make that a circular-buffer public: block_cache_write_buffer(unsigned_type size) { blocks_.reserve(size); free_blocks_.reserve(size); reqs_.resize(size); for (unsigned_type i = 0; i < size; i++) { blocks_.push_back(new block_type()); free_blocks_.push_back(i); } } //! Writes the given block back to disk; //! callers have to exchange the passed block with the returned one! block_type * write(block_type* write_block, const bid_type& bid) { if (free_blocks_.empty()) { unsigned_type i_buffer = busy_blocks_.front(); busy_blocks_.pop_front(); if (reqs_[i_buffer].valid()) reqs_[i_buffer]->wait(); free_blocks_.push_back(i_buffer); } unsigned_type i_buffer = free_blocks_.back(); free_blocks_.pop_back(); block_type* buffer = blocks_[i_buffer]; blocks_[i_buffer] = write_block; reqs_[i_buffer] = blocks_[i_buffer]->write(bid); busy_blocks_.push_back(i_buffer); return buffer; } void flush() { while (!busy_blocks_.empty()) { unsigned_type i_buffer = busy_blocks_.front(); busy_blocks_.pop_front(); if (reqs_[i_buffer].valid()) reqs_[i_buffer]->wait(); } busy_blocks_.clear(); free_blocks_.clear(); for (unsigned_type i = 0; i < blocks_.size(); i++) free_blocks_.push_back(i); } void swap(block_cache_write_buffer& obj) { std::swap(blocks_, obj.blocks_); std::swap(reqs_, obj.reqs_); std::swap(free_blocks_, obj.free_blocks_); std::swap(busy_blocks_, obj.busy_blocks_); } ~block_cache_write_buffer() { flush(); for (unsigned_type i = 0; i < blocks_.size(); i++) delete blocks_[i]; } }; //! Cache of blocks contained in an external memory hash map. Uses the //! stxxl::lru_pager as eviction algorithm. template class block_cache : private noncopyable { public: typedef BlockType block_type; typedef typename block_type::bid_type bid_type; typedef typename block_type::value_type subblock_type; typedef typename subblock_type::bid_type subblock_bid_type; protected: struct bid_eq { bool operator () (const bid_type& a, const bid_type& b) const { return (a.storage == b.storage && a.offset == b.offset); } }; struct bid_hash { size_t operator () (const bid_type& bid) const { return longhash1(bid.offset + reinterpret_cast(bid.storage)); } #ifdef STXXL_MSVC bool operator () (const bid_type& a, const bid_type& b) const { return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset); } enum { // parameters for hash table bucket_size = 4, // 0 < bucket_size min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N }; #endif }; typedef stxxl::lru_pager<> pager_type; typedef block_cache_write_buffer write_buffer_type; typedef typename compat_hash_map::result bid_map_type; enum { valid_all = block_type::size }; write_buffer_type write_buffer_; //! cached blocks std::vector blocks_; //! bids of cached blocks std::vector bids_; std::vector retain_count_; //! true iff block has been altered while in cache std::vector dirty_; //! valid_all or the actually loaded subblock's index std::vector valid_subblock_; //! free blocks as indices to blocks_-vector std::vector free_blocks_; std::vector reqs_; bid_map_type bid_map_; pager_type pager_; /* statistics */ int64 n_found; int64 n_not_found; int64 n_read; int64 n_written; int64 n_clean_forced; int64 n_wrong_subblock; public: //! Construct a new block-cache. //! \param cache_size cache-size in number of blocks block_cache(unsigned_type cache_size) : write_buffer_(config::get_instance()->disks_number() * 2), blocks_(cache_size), bids_(cache_size), retain_count_(cache_size), dirty_(cache_size, false), valid_subblock_(cache_size), free_blocks_(cache_size), reqs_(cache_size), pager_(cache_size), n_found(0), n_not_found(0), n_read(0), n_written(0), n_clean_forced(0), n_wrong_subblock(0) { for (unsigned_type i = 0; i < cache_size; i++) { blocks_[i] = new block_type(); free_blocks_[i] = i; } } //! Return cache-size unsigned_type size() const { return blocks_.size(); } ~block_cache() { STXXL_VERBOSE1("hash_map::block_cache destructor addr=" << this); for (typename bid_map_type::const_iterator i = bid_map_.begin(); i != bid_map_.end(); ++i) { const unsigned_type i_block = (*i).second; if (reqs_[i_block].valid()) reqs_[i_block]->wait(); if (dirty_[i_block]) { blocks_[i_block] = write_buffer_.write(blocks_[i_block], bids_[i_block]); } } write_buffer_.flush(); for (unsigned_type i = 0; i < size(); ++i) delete blocks_[i]; } protected: //! Force a block from the cache; write back to disk if dirty void kick_block() { unsigned_type i_block2kick; unsigned_type max_tries = size() + 1; unsigned_type i = 0; do { ++i; i_block2kick = pager_.kick(); if (i == max_tries) { throw std::runtime_error( "The block cache is too small," "no block can be kicked out (all blocks are retained)!" ); } pager_.hit(i_block2kick); } while (retain_count_[i_block2kick] > 0); if (valid_subblock_[i_block2kick] == valid_all && reqs_[i_block2kick].valid()) { reqs_[i_block2kick]->wait(); } if (dirty_[i_block2kick]) { blocks_[i_block2kick] = write_buffer_.write(blocks_[i_block2kick], bids_[i_block2kick]); ++n_written; } else ++n_clean_forced; bid_map_.erase(bids_[i_block2kick]); free_blocks_.push_back(i_block2kick); } public: //! Retain a block in cache. Blocks, that are retained by at least one //! client, won't get kicked. Make sure to release all retained blocks //! again. //! //! \param bid block, whose retain-count is to be increased //! \return true if block was cached, false otherwise bool retain_block(const bid_type& bid) { typename bid_map_type::const_iterator it = bid_map_.find(bid); if (it == bid_map_.end()) return false; unsigned_type i_block = (*it).second; retain_count_[i_block]++; return true; } //! Release a block (decrement retain-count). If the retain-count reaches //! 0, a block may be kicked again. //! //! \param bid block, whose retain-count is to be decremented //! \return true if operation was successfull (block cached and //! retain-count > 0), false otherwise bool release_block(const bid_type& bid) { typename bid_map_type::const_iterator it = bid_map_.find(bid); if (it == bid_map_.end()) return false; unsigned_type i_block = (*it).second; if (retain_count_[i_block] == 0) return false; retain_count_[i_block]--; return true; } //! Set given block's dirty-flag. Note: If the given block was only //! partially loaded, it will be completely reloaded. //! //! \return true if block cached, false otherwise bool make_dirty(const bid_type& bid) { typename bid_map_type::const_iterator it = bid_map_.find(bid); if (it == bid_map_.end()) return false; unsigned_type i_block = (*it).second; // only complete blocks can be marked as dirty if (valid_subblock_[i_block] != valid_all) { reqs_[i_block] = blocks_[i_block]->read(bid); valid_subblock_[i_block] = valid_all; } if (reqs_[i_block].valid()) { if (reqs_[i_block]->poll() == false) reqs_[i_block]->wait(); } dirty_[i_block] = true; return true; } //! Retrieve a subblock from the cache. If not yet cached, only the //! subblock will be loaded. //! //! \param bid block, to which the requested subblock belongs //! \param i_subblock index of requested subblock //! \return pointer to subblock subblock_type * get_subblock(const bid_type& bid, unsigned_type i_subblock) { block_type* block; unsigned_type i_block; n_read++; // block (partly) cached? typename bid_map_type::const_iterator it = bid_map_.find(bid); if (it != bid_map_.end()) { i_block = (*it).second; block = blocks_[i_block]; // complete block or wanted subblock is in the cache if (valid_subblock_[i_block] == valid_all || valid_subblock_[i_block] == i_subblock) { ++n_found; if (valid_subblock_[i_block] == valid_all && reqs_[i_block].valid()) { // request not yet completed? if (reqs_[i_block]->poll() == false) reqs_[i_block]->wait(); } return &((*block)[i_subblock]); } // wrong subblock in cache else { ++n_not_found; ++n_wrong_subblock; // actually loading the subblock will be done below // note: if a client still holds a reference to the "old" // subblock, it will find its data to be still valid. } } // block not cached else { n_not_found++; if (free_blocks_.empty()) kick_block(); i_block = free_blocks_.back(), free_blocks_.pop_back(); block = blocks_[i_block]; bid_map_[bid] = i_block; bids_[i_block] = bid; dirty_[i_block] = false; retain_count_[i_block] = 0; } // now actually load the wanted subblock and store it within *block subblock_bid_type subblock_bid( bid.storage, bid.offset + i_subblock * subblock_type::raw_size ); request_ptr req = ((*block)[i_subblock]).read(subblock_bid); req->wait(); valid_subblock_[i_block] = i_subblock; pager_.hit(i_block); return &((*block)[i_subblock]); } //! Load a block in advance. //! \param bid Identifier of the block to load void prefetch_block(const bid_type& bid) { unsigned_type i_block; // cached typename bid_map_type::const_iterator it = bid_map_.find(bid); if (it != bid_map_.end()) { i_block = (*it).second; // complete block cached; we can finish here if (valid_subblock_[i_block] == valid_all) { pager_.hit(i_block); return; } // only a single subblock is cached; we have to load the // complete block (see below) } // not even a subblock cached else { if (free_blocks_.empty()) kick_block(); i_block = free_blocks_.back(), free_blocks_.pop_back(); bid_map_[bid] = i_block; bids_[i_block] = bid; retain_count_[i_block] = 0; dirty_[i_block] = false; } // now actually load the block reqs_[i_block] = blocks_[i_block]->read(bid); valid_subblock_[i_block] = valid_all; pager_.hit(i_block); } //! Write all dirty blocks back to disk void flush() { for (typename bid_map_type::const_iterator i = bid_map_.begin(); i != bid_map_.end(); ++i) { const unsigned_type i_block = (*i).second; if (dirty_[i_block]) { blocks_[i_block] = write_buffer_.write(blocks_[i_block], bids_[i_block]); dirty_[i_block] = false; } } write_buffer_.flush(); } //! Empty cache; don't write back dirty blocks void clear() { free_blocks_.clear(); for (unsigned_type i = 0; i < size(); i++) { if (reqs_[i].valid()) { reqs_[i]->cancel(); reqs_[i]->wait(); } free_blocks_.push_back(i); } bid_map_.clear(); } //! Print statistics: Number of hits/misses, blocks forced from cache or //! written back. void print_statistics(std::ostream& o = std::cout) const { o << "Blocks found : " << n_found << " (" << 100. * double(n_found) / double(n_read) << "%)" << std::endl; o << "Blocks not found : " << n_not_found << std::endl; o << "Blocks read : " << n_read << std::endl; o << "Blocks written : " << n_written << std::endl; o << "Clean blocks forced from the cache: " << n_clean_forced << std::endl; o << "Wrong subblock cached : " << n_wrong_subblock << std::endl; } //! Reset all counters to zero void reset_statistics() { n_found = 0; n_not_found = 0; n_read = 0; n_written = 0; n_clean_forced = 0; n_wrong_subblock = 0; } //! Exchange contents of two caches //! \param obj cache to swap contents with void swap(block_cache& obj) { write_buffer_.swap(obj.write_buffer_); std::swap(blocks_, obj.blocks_); std::swap(bids_, obj.bids_); std::swap(retain_count_, obj.retain_count_); std::swap(dirty_, obj.dirty_); std::swap(valid_subblock_, obj.valid_subblock_); std::swap(free_blocks_, obj.free_blocks_); std::swap(reqs_, obj.reqs_); std::swap(bid_map_, obj.bid_map_); std::swap(pager_, obj.pager_); std::swap(n_found, obj.n_found); std::swap(n_not_found, obj.n_found); std::swap(n_read, obj.n_read); std::swap(n_written, obj.n_written); std::swap(n_clean_forced, obj.n_clean_forced); std::swap(n_wrong_subblock, obj.n_wrong_subblock); } #if 0 // for debugging, requires data items to be ostream-able. //! Show currently cached blocks void dump_cache(std::ostream& os) const { for (size_t i = 0; i < blocks_.size(); i++) { bid_type bid = bids_[i]; if (bid_map_.count(bid) == 0) { os << "Block " << i << ": empty\n"; continue; } os << "Block " << i << ": bid=" << bids_[i] << " dirty=" << dirty_[i] << " retain_count=" << retain_count_[i] << " valid_subblock=" << valid_subblock_[i] << "\n"; for (size_t k = 0; k < block_type::size; k++) { os << " Subbblock " << k << ": "; if (valid_subblock_[i] != valid_all && valid_subblock_[i] != k) { os << "not valid\n"; continue; } for (size_t l = 0; l < block_type::value_type::size; l++) { os << "(" << (*blocks_[i])[k][l].first << ", " << (*blocks_[i])[k][l].second << ") "; } os << std::endl; } } } #endif }; } // namespace hash_map STXXL_END_NAMESPACE namespace std { template void swap(stxxl::hash_map::block_cache_write_buffer& a, stxxl::hash_map::block_cache_write_buffer& b) { a.swap(b); } template void swap(stxxl::hash_map::block_cache& a, stxxl::hash_map::block_cache& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_HASH_MAP_BLOCK_CACHE_HEADER stxxl-1.4.1/include/stxxl/bits/containers/hash_map/iterator.h000644 001411 000144 00000045316 12411366426 024210 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/hash_map/iterator.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_HASH_MAP_ITERATOR_HEADER #define STXXL_CONTAINERS_HASH_MAP_ITERATOR_HEADER #include #include #include STXXL_BEGIN_NAMESPACE namespace hash_map { template class iterator_map; template class hash_map_iterator; template class hash_map_const_iterator; template class block_cache; template class hash_map_iterator_base { public: friend class iterator_map; friend void HashMap::erase(hash_map_const_iterator it); typedef HashMap hash_map_type; typedef typename hash_map_type::internal_size_type internal_size_type; typedef typename hash_map_type::external_size_type external_size_type; typedef typename hash_map_type::value_type value_type; typedef typename hash_map_type::key_type key_type; typedef typename hash_map_type::reference reference; typedef typename hash_map_type::const_reference const_reference; typedef typename hash_map_type::node_type node_type; typedef typename hash_map_type::bucket_type bucket_type; typedef typename hash_map_type::bid_iterator_type bid_iterator_type; typedef typename hash_map_type::source_type source_type; typedef buffered_reader reader_type; typedef std::forward_iterator_tag iterator_category; protected: HashMap* map_; reader_type* reader_; //! true if prefetching enabled; false by default, will be set to true when //! incrementing (see find_next()) bool prefetch_; //! index of current bucket internal_size_type i_bucket_; //! source of current value: external or internal source_type source_; //! current (source=internal) or old (src=external) internal node node_type* node_; //! position of current (source=external) or next (source=internal) //! external value (see _ext_valid) external_size_type i_external_; //! key of current value key_type key_; /*! true if i_external points to the current or next external value example: iterator was created by hash_map::find() and the value was found in internal memory => iterator pointing to internal node is created and location of next external value is unknown (_ext_valid == false) => when incrementing the iterator the external values will be scanned from the beginning of the bucket to find the valid external index */ bool ext_valid_; //! true if iterator equals end() bool end_; public: //! Construct a new iterator hash_map_iterator_base(HashMap* map, internal_size_type i_bucket, node_type* node, external_size_type i_external, source_type source, bool ext_valid, key_type key) : map_(map), reader_(NULL), prefetch_(false), i_bucket_(i_bucket), source_(source), node_(node), i_external_(i_external), key_(key), ext_valid_(ext_valid), end_(false) { STXXL_VERBOSE3("hash_map_iterator_base parameter construct addr=" << this); map_->iterator_map_.register_iterator(*this); } //! Construct a new iterator pointing to the end of the given hash-map. hash_map_iterator_base(hash_map_type* map) : map_(map), reader_(NULL), prefetch_(false), i_bucket_(0), source_(hash_map_type::src_unknown), node_(NULL), i_external_(0), ext_valid_(false), end_(true) { } //! Construct a new iterator from an existing one hash_map_iterator_base(const hash_map_iterator_base& obj) : map_(obj.map_), reader_(NULL), prefetch_(obj.prefetch_), i_bucket_(obj.i_bucket_), source_(obj.source_), node_(obj.node_), i_external_(obj.i_external_), key_(obj.key_), ext_valid_(obj.ext_valid_), end_(obj.end_) { STXXL_VERBOSE3("hash_map_iterator_base constr from" << (&obj) << " to " << this); if (!end_ && map_) map_->iterator_map_.register_iterator(*this); } //! Assignment operator hash_map_iterator_base& operator = (const hash_map_iterator_base& obj) { STXXL_VERBOSE3("hash_map_iterator_base copy from" << (&obj) << " to " << this); if (&obj != this) { if (map_ && !end_) map_->iterator_map_.unregister_iterator(*this); reset_reader(); map_ = obj.map_; i_bucket_ = obj.i_bucket_; node_ = obj.node_; source_ = obj.source_; i_external_ = obj.i_external_; ext_valid_ = obj.ext_valid_; prefetch_ = obj.prefetch_; end_ = obj.end_; key_ = obj.key_; if (map_ && !end_) map_->iterator_map_.register_iterator(*this); } return *this; } //! Two iterators are equal if the point to the same value in the same map bool operator == (const hash_map_iterator_base& obj) const { if (end_ && obj.end_) return true; if (map_ != obj.map_ || i_bucket_ != obj.i_bucket_ || source_ != obj.source_) return false; if (source_ == hash_map_type::src_internal) return node_ == obj.node_; else return i_external_ == obj.i_external_; } bool operator != (const hash_map_iterator_base& obj) const { return ! operator == (obj); } protected: //! Initialize reader object to scan external values void init_reader() { const bucket_type& bucket = map_->buckets_[i_bucket_]; bid_iterator_type begin = map_->bids_.begin() + bucket.i_block_; bid_iterator_type end = map_->bids_.end(); reader_ = new reader_type(begin, end, map_->block_cache_, bucket.i_subblock_, prefetch_); // external value's index already known if (ext_valid_) { // TODO: speed this up (go directly to i_external_ for (external_size_type i = 0; i < i_external_; i++) ++(*reader_); } // otw lookup external value. // case I: no internal value => first external value is the desired one else if (node_ == NULL) { i_external_ = 0; ext_valid_ = true; } // case II: search for smallest external value > internal value else { i_external_ = 0; while (i_external_ < bucket.n_external_) { if (map_->_gt(reader_->const_value().first, node_->value_.first)) break; ++(*reader_); ++i_external_; } // note: i_external==num_external just means that there was no // external value > internal value (which is perfectly OK) ext_valid_ = true; } } //! Reset reader-object void reset_reader() { if (reader_) { delete reader_; reader_ = NULL; } } public: //! Advance iterator to the next value //! The next value is determined in the following way //! - if there are remaining internal or external values in the current //! bucket, choose the smallest among them, that is not marked as deleted //! - otherwise continue with the next bucket void find_next(bool start_prefetching = false) { // invariant: current external value is always > current internal value assert(!end_); internal_size_type i_bucket_old = i_bucket_; bucket_type bucket = map_->buckets_[i_bucket_]; if (reader_ == NULL) init_reader(); // when incremented once, more increments are likely to follow; // therefore start prefetching if (start_prefetching && !prefetch_) { reader_->enable_prefetching(); prefetch_ = true; } // determine starting-points for comparision, which are given by: // - tmp_node: smallest internal value > old value (tmp_node may be NULL) // - reader_: smallest external value > old value (external value may not exists) node_type* tmp_node = (node_) ? node_ : bucket.list_; if (source_ == hash_map_type::src_external) { while (tmp_node && map_->_leq(tmp_node->value_.first, key_)) tmp_node = tmp_node->next(); ++i_external_; ++(*reader_); } else if (source_ == hash_map_type::src_internal) tmp_node = node_->next(); // else (source unknown): tmp_node and reader_ already point to the // correct values while (true) { // internal and external values available while (tmp_node && i_external_ < bucket.n_external_) { // internal value less or equal external value => internal wins if (map_->_leq(tmp_node->value_.first, reader_->const_value().first)) { node_ = tmp_node; if (map_->_eq(node_->value_.first, reader_->const_value().first)) { ++i_external_; ++(*reader_); } if (!node_->deleted()) { key_ = node_->value_.first; source_ = hash_map_type::src_internal; goto end_search; // just this once - I promise... } else // continue search if internal value flaged as deleted tmp_node = tmp_node->next(); } // otherwise external wins else { key_ = reader_->const_value().first; source_ = hash_map_type::src_external; goto end_search; } } // only external values left if (i_external_ < bucket.n_external_) { key_ = reader_->const_value().first; source_ = hash_map_type::src_external; goto end_search; } // only internal values left while (tmp_node) { node_ = tmp_node; if (!node_->deleted()) { key_ = node_->value_.first; source_ = hash_map_type::src_internal; goto end_search; } else tmp_node = tmp_node->next(); // continue search } // at this point there are obviously no more values in the current // bucket let's try the next one (outer while-loop!) i_bucket_++; if (i_bucket_ == map_->buckets_.size()) { end_ = true; reset_reader(); goto end_search; } else { bucket = map_->buckets_[i_bucket_]; i_external_ = 0; tmp_node = bucket.list_; node_ = NULL; reader_->skip_to(map_->bids_.begin() + bucket.i_block_, bucket.i_subblock_); } } end_search: if (end_) { this->map_->iterator_map_.unregister_iterator(*this, i_bucket_old); } else if (i_bucket_old != i_bucket_) { this->map_->iterator_map_.unregister_iterator(*this, i_bucket_old); this->map_->iterator_map_.register_iterator(*this, i_bucket_); } } virtual ~hash_map_iterator_base() { STXXL_VERBOSE3("hash_map_iterator_base deconst " << this); if (map_ && !end_) map_->iterator_map_.unregister_iterator(*this); reset_reader(); } }; template class hash_map_iterator : public hash_map_iterator_base { public: typedef HashMap hash_map_type; typedef typename hash_map_type::internal_size_type internal_size_type; typedef typename hash_map_type::external_size_type external_size_type; typedef typename hash_map_type::value_type value_type; typedef typename hash_map_type::key_type key_type; typedef typename hash_map_type::reference reference; typedef typename hash_map_type::const_reference const_reference; typedef typename hash_map_type::pointer pointer; typedef typename hash_map_type::const_pointer const_pointer; typedef typename hash_map_type::node_type node_type; typedef typename hash_map_type::bucket_type bucket_type; typedef typename hash_map_type::bid_iterator_type bid_iterator_type; typedef typename hash_map_type::source_type source_type; typedef buffered_reader reader_type; typedef std::forward_iterator_tag iterator_category; typedef stxxl::hash_map::hash_map_iterator_base base_type; typedef stxxl::hash_map::hash_map_const_iterator hash_map_const_iterator; public: hash_map_iterator(hash_map_type* map, internal_size_type i_bucket, node_type* node, external_size_type i_external, source_type source, bool ext_valid, key_type key) : base_type(map, i_bucket, node, i_external, source, ext_valid, key) { } hash_map_iterator() : base_type(NULL) { } hash_map_iterator(hash_map_type* map) : base_type(map) { } hash_map_iterator(const hash_map_iterator& obj) : base_type(obj) { } hash_map_iterator& operator = (const hash_map_iterator& obj) { base_type::operator = (obj); return *this; } bool operator == (const hash_map_iterator& obj) const { return base_type::operator == (obj); } bool operator == (const hash_map_const_iterator& obj) const { return base_type::operator == (obj); } bool operator != (const hash_map_iterator& obj) const { return base_type::operator != (obj); } bool operator != (const hash_map_const_iterator& obj) const { return base_type::operator != (obj); } //! Return reference to current value. If source is external, mark the //! value's block as dirty reference operator * () { if (this->source_ == hash_map_type::src_internal) { return this->node_->value_; } else { if (this->reader_ == NULL) base_type::init_reader(); return this->reader_->value(); } } //! Return reference to current value. If source is external, mark the //! value's block as dirty pointer operator -> () { return &operator * (); } //! Increment iterator hash_map_iterator& operator ++ () { base_type::find_next(true); return *this; } }; template class hash_map_const_iterator : public hash_map_iterator_base { public: typedef HashMap hash_map_type; typedef typename hash_map_type::internal_size_type internal_size_type; typedef typename hash_map_type::external_size_type external_size_type; typedef typename hash_map_type::value_type value_type; typedef typename hash_map_type::key_type key_type; typedef typename hash_map_type::reference reference; typedef typename hash_map_type::const_reference const_reference; typedef typename hash_map_type::pointer pointer; typedef typename hash_map_type::const_pointer const_pointer; typedef typename hash_map_type::node_type node_type; typedef typename hash_map_type::bucket_type bucket_type; typedef typename hash_map_type::bid_iterator_type bid_iterator_type; typedef typename hash_map_type::source_type source_type; typedef buffered_reader reader_type; typedef std::forward_iterator_tag iterator_category; typedef stxxl::hash_map::hash_map_iterator_base base_type; typedef stxxl::hash_map::hash_map_iterator hash_map_iterator; public: hash_map_const_iterator(hash_map_type* map, internal_size_type i_bucket, node_type* node, external_size_type i_external, source_type source, bool ext_valid, key_type key) : base_type(map, i_bucket, node, i_external, source, ext_valid, key) { } hash_map_const_iterator() : base_type(NULL) { } hash_map_const_iterator(hash_map_type* map) : base_type(map) { } hash_map_const_iterator(const hash_map_iterator& obj) : base_type(obj) { } hash_map_const_iterator(const hash_map_const_iterator& obj) : base_type(obj) { } hash_map_const_iterator& operator = (const hash_map_const_iterator& obj) { base_type::operator = (obj); return *this; } bool operator == (const hash_map_const_iterator& obj) const { return base_type::operator == (obj); } bool operator == (const hash_map_iterator& obj) const { return base_type::operator == (obj); } bool operator != (const hash_map_const_iterator& obj) const { return base_type::operator != (obj); } bool operator != (const hash_map_iterator& obj) const { return base_type::operator != (obj); } //! Return const-reference to current value const_reference operator * () { if (this->source_ == hash_map_type::src_internal) { return this->node_->value_; } else { if (this->reader_ == NULL) base_type::init_reader(); return this->reader_->const_value(); } } //! Increment iterator hash_map_const_iterator& operator ++ () { base_type::find_next(true); return *this; } }; } // namespace hash_map STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_HASH_MAP_ITERATOR_HEADER stxxl-1.4.1/include/stxxl/bits/containers/hash_map/tuning.h000644 001411 000144 00000002577 12411366426 023665 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/hash_map/tuning.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_HASH_MAP_TUNING_HEADER #define STXXL_CONTAINERS_HASH_MAP_TUNING_HEADER #define _STXXL_TUNING_H_ #include #include STXXL_BEGIN_NAMESPACE namespace hash_map { //! Tuning parameters for external memory hash map. class tuning : public singleton { friend class singleton; public: //! see buffered_reader size_t prefetch_page_size; //! see buffered_reader size_t prefetch_pages; //! see block_cache and hash_map size_t blockcache_size; private: /*! set reasonable default values for tuning params */ tuning() : prefetch_page_size(config::get_instance()->disks_number() * 2), prefetch_pages(2), blockcache_size(config::get_instance()->disks_number() * 12) { } }; } // namespace hash_map STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_HASH_MAP_TUNING_HEADER stxxl-1.4.1/include/stxxl/bits/containers/hash_map/iterator_map.h000644 001411 000144 00000021547 12411366426 025045 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/hash_map/iterator_map.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_HASH_MAP_ITERATOR_MAP_HEADER #define STXXL_CONTAINERS_HASH_MAP_ITERATOR_MAP_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE namespace hash_map { template class iterator_map : private noncopyable { public: typedef HashMap hash_map_type; typedef typename hash_map_type::node_type node_type; typedef typename hash_map_type::source_type source_type; typedef typename hash_map_type::key_type key_type; typedef typename hash_map_type::internal_size_type internal_size_type; typedef typename hash_map_type::external_size_type external_size_type; typedef hash_map_iterator_base iterator_base; private: #if 0 typedef std::multimap multimap_type; #else struct hasher { size_t operator () (const internal_size_type& key) const { return longhash1(key); } #if STXXL_MSVC bool operator () (const internal_size_type& a, const internal_size_type& b) const { return (a < b); } enum { // parameters for hash table bucket_size = 4, // 0 < bucket_size min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N }; #endif }; // store iterators by bucket-index typedef typename compat_hash_multimap< internal_size_type, iterator_base*, hasher >::result multimap_type; #endif //! bucket-index and pointer to iterator_base typedef typename multimap_type::value_type pair_type; typedef typename multimap_type::iterator mmiterator_type; typedef typename multimap_type::const_iterator const_mmiterator_type; hash_map_type* map_; multimap_type it_map_; public: iterator_map(hash_map_type* map) : map_(map) { } ~iterator_map() { it_map_.clear(); } void register_iterator(iterator_base& it) { register_iterator(it, it.i_bucket_); } void register_iterator(iterator_base& it, internal_size_type i_bucket) { STXXL_VERBOSE2("hash_map::iterator_map register_iterator addr=" << &it << " bucket=" << i_bucket); it_map_.insert(pair_type(i_bucket, &it)); } void unregister_iterator(iterator_base& it) { unregister_iterator(it, it.i_bucket_); } void unregister_iterator(iterator_base& it, internal_size_type i_bucket) { STXXL_VERBOSE2("hash_map::iterator_map unregister_iterator addr=" << &it << " bucket=" << i_bucket); std::pair range = it_map_.equal_range(i_bucket); assert(range.first != range.second); for (mmiterator_type i = range.first; i != range.second; ++i) { if (i->second == &it) { it_map_.erase(i); return; } } throw std::runtime_error("unregister_iterator Panic in hash_map::iterator_map, can not find and unregister iterator"); } //! Update iterators with given key and bucket and make them point to the //! specified location in external memory (will be called during //! re-hashing) void fix_iterators_2ext(internal_size_type i_bucket_old, const key_type& key, internal_size_type i_bucket_new, external_size_type i_ext) { STXXL_VERBOSE2("hash_map::iterator_map fix_iterators_2ext i_bucket=" << i_bucket_old << " new_i_ext=" << i_ext); std::vector its2fix; _find(i_bucket_old, its2fix); for (typename std::vector::iterator it2fix = its2fix.begin(); it2fix != its2fix.end(); ++it2fix) { if (!map_->_eq(key, (**it2fix).key_)) continue; if (i_bucket_old != i_bucket_new) { unregister_iterator(**it2fix); register_iterator(**it2fix, i_bucket_new); } (**it2fix).i_bucket_ = i_bucket_new; (**it2fix).node_ = NULL; (**it2fix).i_external_ = i_ext; (**it2fix).source_ = hash_map_type::src_external; // external position is now known (i_ext) and therefore valid (**it2fix).ext_valid_ = true; (**it2fix).reset_reader(); (**it2fix).reader_ = NULL; } } //! Update iterators with given key and bucket and make them point to the //! specified node in internal memory (will be called by insert_oblivious) void fix_iterators_2int(internal_size_type i_bucket, const key_type& key, node_type* node) { STXXL_VERBOSE2("hash_map::iterator_map fix_iterators_2int i_bucket=" << i_bucket << " node=" << node); std::vector its2fix; _find(i_bucket, its2fix); for (typename std::vector::iterator it2fix = its2fix.begin(); it2fix != its2fix.end(); ++it2fix) { if (!map_->_eq((**it2fix).key_, key)) continue; assert((**it2fix).source_ == hash_map_type::src_external); (**it2fix).source_ = hash_map_type::src_internal; (**it2fix).node_ = node; (**it2fix).i_external_++; if ((** it2fix).reader_) (** it2fix).reader_->operator ++ (); } } //! Update iterators with given key and bucket and make them point to the //! end of the hash-map (called by erase and erase_oblivious) void fix_iterators_2end(internal_size_type i_bucket, const key_type& key) { STXXL_VERBOSE2("hash_map::iterator_map fix_iterators_2end i_bucket=" << i_bucket); std::vector its2fix; _find(i_bucket, its2fix); for (typename std::vector::iterator it2fix = its2fix.begin(); it2fix != its2fix.end(); ++it2fix) { if (!map_->_eq(key, (**it2fix).key_)) continue; (**it2fix).end_ = true; (**it2fix).reset_reader(); unregister_iterator(**it2fix); } } //! Update all iterators and make them point to the end of the hash-map //! (used by clear()) void fix_iterators_all2end() { for (mmiterator_type it2fix = it_map_.begin(); it2fix != it_map_.end(); ++it2fix) { (*it2fix).second->end_ = true; (*it2fix).second->reset_reader(); } it_map_.clear(); } private: //! Find all iterators registered with given bucket and add them to outc template void _find(internal_size_type i_bucket, OutputContainer& outc) { std::pair range = it_map_.equal_range(i_bucket); for (mmiterator_type i = range.first; i != range.second; ++i) outc.push_back((*i).second); } // changes hash_map pointer in all contained iterators void change_hash_map_pointers(hash_map_type* map) { for (mmiterator_type it = it_map_.begin(); it != it_map_.end(); ++it) ((*it).second)->map_ = map; } public: void swap(iterator_map& obj) { std::swap(it_map_, obj.it_map_); std::swap(map_, obj.map_); change_hash_map_pointers(map_); obj.change_hash_map_pointers(obj.map_); } void print_statistics(std::ostream& o = std::cout) const { o << "Registered iterators: " << it_map_.size() << "\n"; for (const_mmiterator_type i = it_map_.begin(); i != it_map_.end(); ++i) { o << " Address=" << i->second << ", Bucket=" << i->second->i_bucket_ << ", Node=" << i->second->node_ << ", i_ext=" << i->second->i_external_ << ", " << ((i->second->source_ == hash_map_type::src_external) ? "external" : "internal") << std::endl; } } }; } // namespace hash_map STXXL_END_NAMESPACE namespace std { template void swap(stxxl::hash_map::iterator_map& a, stxxl::hash_map::iterator_map& b) { if (&a != &b) a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_HASH_MAP_ITERATOR_MAP_HEADER stxxl-1.4.1/include/stxxl/bits/containers/hash_map/hash_map.h000644 001411 000144 00000161256 12411366426 024141 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/hash_map/hash_map.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_HASH_MAP_HASH_MAP_HEADER #define STXXL_CONTAINERS_HASH_MAP_HASH_MAP_HEADER #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE #define STXXL_VERBOSE_HASH_MAP(m) \ STXXL_VERBOSE1("hash_map[" << static_cast(this) << "]::" << m) //! External memory hash-map namespace hash_map { /*! * Main implementation of external memory hash map. * * \tparam KeyType the key type * \tparam MappedType the mapped type associated with a key * \tparam HashType a hash functional * \tparam CompareType a less comparison relation for KeyType * \tparam SubBlockSize the raw size of a subblock (caching granularity) * (default: 8192) * \tparam SubBlocksPerBlock the number of subblocks per external block * (default: 256 -> 2MB blocks) * \tparam AllocType allocator for internal-memory buffer */ template > > class hash_map : private noncopyable { protected: typedef hash_map self_type; public: //! type of the keys being used typedef KeyType key_type; //! type of the data to be stored typedef MappedType mapped_type; //! actually store (key-data)-pairs typedef std::pair value_type; //! type for value-references typedef value_type& reference; //! type for constant value-references typedef const value_type& const_reference; //! pointer to type of keys typedef value_type* pointer; //! const pointer to type of keys typedef value_type const* const_pointer; typedef stxxl::external_size_type external_size_type; typedef stxxl::internal_size_type internal_size_type; typedef stxxl::int64 difference_type; //! type of (mother) hash-function typedef HashType hasher; //! functor that imposes a ordering on keys (but see _lt()) typedef KeyCompareType key_compare; //! allocator template type typedef AllocatorType allocator_type; typedef hash_map_iterator iterator; typedef hash_map_const_iterator const_iterator; //! subblock- and block-size in bytes enum { block_raw_size = SubBlocksPerBlock * SubBlockSize, subblock_raw_size = SubBlockSize }; //! Subblock-size as number of elements, block-size as number of subblocks enum { subblocks_per_block = SubBlocksPerBlock, subblock_size = SubBlockSize / sizeof(value_type) }; //! a subblock consists of subblock_size values typedef typed_block subblock_type; //! a block consists of block_size subblocks typedef typed_block block_type; //! block-identifier for subblocks typedef typename subblock_type::bid_type subblock_bid_type; //! block-identifier for blocks typedef typename block_type::bid_type bid_type; //! container for block-bids typedef std::vector bid_container_type; //! iterator for block-bids typedef typename bid_container_type::iterator bid_iterator_type; enum source_type { src_internal, src_external, src_unknown }; //! nodes for internal-memory buffer typedef node node_type; //! buckets typedef bucket bucket_type; typedef std::vector buckets_container_type; //! for tracking active iterators typedef iterator_map iterator_map_type; typedef block_cache block_cache_type; typedef buffered_reader reader_type; typedef typename allocator_type::template rebind::other node_allocator_type; protected: //! user supplied mother hash-function hasher hash_; //! user supplied strict-weak-ordering for keys key_compare cmp_; //! array of bucket buckets_container_type buckets_; //! blocks-ids of allocated blocks bid_container_type bids_; //! size of internal-memory buffer in number of entries internal_size_type buffer_size_; //! maximum size for internal-memory buffer internal_size_type max_buffer_size_; //! keeps track of all active iterators iterator_map_type iterator_map_; mutable block_cache_type block_cache_; //! used to allocate new nodes for internal buffer node_allocator_type node_allocator_; //! false if the total-number of values is correct (false) or true if //! estimated (true); see *oblivious_-methods mutable bool oblivious_; //! (estimated) number of values mutable external_size_type num_total_; //! desired load factor after rehashing float opt_load_factor_; public: /*! * Construct a new hash-map * \param n initial number of buckets * \param hf hash-function * \param cmp comparator-object * \param buffer_size size of internal-memory buffer in bytes * \param a allocation-strategory for internal-memory buffer */ hash_map(internal_size_type n = 0, const hasher& hf = hasher(), const key_compare& cmp = key_compare(), internal_size_type buffer_size = 128*1024*1024, const allocator_type& a = allocator_type()) : hash_(hf), cmp_(cmp), buckets_(n), bids_(0), buffer_size_(0), iterator_map_(this), block_cache_(tuning::get_instance()->blockcache_size), node_allocator_(a), oblivious_(false), num_total_(0), opt_load_factor_(0.875) { max_buffer_size_ = buffer_size / sizeof(node_type); } /*! * Construct a new hash-map and insert all values in the range [f,l) * * \param begin beginning of the range * \param end end of the range * \param mem_to_sort internal memory that may be used for bulk-construction (not * to be confused with the buffer-memory) * \param n initial number of buckets * \param hf hash-function * \param cmp comparator-object * \param buffer_size size of internal-memory buffer in bytes * \param a allocation-strategory for internal-memory buffer */ template hash_map(InputIterator begin, InputIterator end, internal_size_type mem_to_sort = 256*1024*1024, internal_size_type n = 0, const hasher& hf = hasher(), const key_compare& cmp = key_compare(), internal_size_type buffer_size = 128*1024*1024, const allocator_type& a = allocator_type()) : hash_(hf), cmp_(cmp), buckets_(n), // insert will determine a good size bids_(0), buffer_size_(0), iterator_map_(this), block_cache_(tuning::get_instance()->blockcache_size), node_allocator_(a), oblivious_(false), num_total_(0), opt_load_factor_(0.875) { max_buffer_size_ = buffer_size / sizeof(node_type); insert(begin, end, mem_to_sort); } ~hash_map() { clear(); } public: //! Hash-function used by this hash-map hasher hash_function() const { return hash_; } //! Strict-weak-ordering used by this hash-map key_compare key_cmp() const { return cmp_; } //! Get node memory allocator allocator_type get_allocator() const { return node_allocator_; } protected: /*! * After using *oblivious_-methods only an estimate for the total number of * elements can be given. This method accesses external memory to * calculate the exact number. */ void _make_conscious() { /* const */ //! TODO: make const again if (!oblivious_) return; typedef HashedValuesStream values_stream_type; // this will start prefetching automatically reader_type reader(bids_.begin(), bids_.end(), block_cache_); values_stream_type values(buckets_.begin(), buckets_.end(), reader, bids_.begin(), *this); num_total_ = 0; while (!values.empty()) { ++num_total_; ++values; } oblivious_ = false; } public: //! Number of values currently stored. Note: If the correct number is //! currently unknown (because *_oblivous-methods were used), external //! memory will be scanned. external_size_type size() const { if (oblivious_) ((self_type*)this)->_make_conscious(); return num_total_; } //! The hash-map may store up to this number of values external_size_type max_size() const { return std::numeric_limits::max(); } //! Check if container is empty. bool empty() const { return size() != 0; } /*! * Insert a new value if no value with the same key is already present; * external memory must therefore be accessed * * \param value what to insert * \return a tuple whose second part is true iff the value was actually * added (no value with the same key present); the first part is an * iterator pointing to the newly inserted or already stored value */ std::pair insert(const value_type& value) { if (buckets_.size() == 0) _rebuild_buckets(128); internal_size_type i_bucket = _bkt_num(value.first); bucket_type& bucket = buckets_[i_bucket]; node_type* node = _find_key_internal(bucket, value.first); // found value in internal memory if (node && _eq(node->value_.first, value.first)) { bool old_deleted = node->deleted(); if (old_deleted) { node->set_deleted(false); node->value_ = value; ++num_total_; } return std::pair( iterator(this, i_bucket, node, 0, src_internal, false, value.first), old_deleted); } // search external memory ... else { tuple result = _find_key_external(bucket, value.first); external_size_type i_external = result.first; value_type ext_value = result.second; // ... if found, return iterator pointing to external position ... if (i_external < bucket.n_external_ && _eq(ext_value.first, value.first)) { return std::pair( iterator(this, i_bucket, node, i_external, src_external, true, value.first), false); } // ... otherwise create a new buffer-node to add the value else { ++num_total_; node_type* new_node = node ? node->set_next(_new_node(value, node->next(), false)) : (bucket.list_ = _new_node(value, bucket.list_, false)); iterator it(this, i_bucket, new_node, 0, src_internal, false, value.first); ++buffer_size_; if (buffer_size_ >= max_buffer_size_) _rebuild_buckets(); // will fix it as well return std::pair(it, true); } } } //! Insert a value; external memory is not accessed so that another value //! with the same key may be overwritten //! \param value what to insert //! \return iterator pointing to the inserted value iterator insert_oblivious(const value_type& value) { internal_size_type i_bucket = _bkt_num(value.first); bucket_type& bucket = buckets_[i_bucket]; node_type* node = _find_key_internal(bucket, value.first); // found value in internal memory if (node && _eq(node->value_.first, value.first)) { if (node->deleted()) ++num_total_; node->set_deleted(false); node->value_ = value; return iterator(this, i_bucket, node, 0, src_internal, false, value.first); } // not found; ignore external memory and add a new node to the // internal-memory buffer else { oblivious_ = true; ++num_total_; node_type* new_node = node ? node->set_next(_new_node(value, node->next(), false)) : (bucket.list_ = _new_node(value, bucket.list_, false)); // there may be some iterators that reference the newly inserted // value in external memory these need to be fixed (make them point // to new_node) iterator_map_.fix_iterators_2int(i_bucket, value.first, new_node); iterator it(this, i_bucket, new_node, 0, src_internal, false, value.first); ++buffer_size_; if (buffer_size_ >= max_buffer_size_) _rebuild_buckets(); return it; } } //! Erase value by iterator //! \param it iterator pointing to the value to erase void erase(const_iterator it) { --num_total_; bucket_type& bucket = buckets_[it.i_bucket_]; if (it.source_ == src_internal) { it.node_->set_deleted(true); iterator_map_.fix_iterators_2end(it.i_bucket_, it.key_); } else { // find biggest value < iterator's value node_type* node = _find_key_internal(bucket, it.key_); assert(!node || !_eq(node->value_.first, it.key_)); // add delete-node to buffer if (node) node->set_next(_new_node(value_type(it.key_, mapped_type()), node->next(), true)); else bucket.list_ = _new_node(value_type(it.key_, mapped_type()), bucket.list_, true); iterator_map_.fix_iterators_2end(it.i_bucket_, it.key_); ++buffer_size_; if (buffer_size_ >= max_buffer_size_) _rebuild_buckets(); } } //! Erase value by key; check external memory //! \param key key of value to erase //! \return number of values actually erased (0 or 1) external_size_type erase(const key_type& key) { internal_size_type i_bucket = _bkt_num(key); bucket_type& bucket = buckets_[i_bucket]; node_type* node = _find_key_internal(bucket, key); // found in internal memory if (node && _eq(node->value_.first, key)) { if (!node->deleted()) { node->set_deleted(true); --num_total_; iterator_map_.fix_iterators_2end(i_bucket, key); return 1; } else return 0; // already deleted } // check external memory else { tuple result = _find_key_external(bucket, key); external_size_type i_external = result.first; value_type ext_value = result.second; // found in external memory; add delete-node if (i_external < bucket.n_external_ && _eq(ext_value.first, key)) { --num_total_; if (node) node->set_next(_new_node(value_type(key, mapped_type()), node->next(), true)); else bucket.list_ = _new_node(value_type(key, mapped_type()), bucket.list_, true); iterator_map_.fix_iterators_2end(i_bucket, key); ++buffer_size_; if (buffer_size_ >= max_buffer_size_) _rebuild_buckets(); return 1; } // no value with given key else return 0; } } //! Erase value by key but without looking at external memory //! \param key key for value to release void erase_oblivious(const key_type& key) { internal_size_type i_bucket = _bkt_num(key); bucket_type& bucket = buckets_[i_bucket]; node_type* node = _find_key_internal(bucket, key); // found value in internal-memory if (node && _eq(node->value_.first, key)) { if (!node->deleted()) { --num_total_; node->set_deleted(true); iterator_map_.fix_iterators_2end(i_bucket, key); } } // not found; ignore external memory and add delete-node else { oblivious_ = true; --num_total_; if (node) node->set_next(_new_node(value_type(key, mapped_type()), node->next(), true)); else bucket.list_ = _new_node(value_type(key, mapped_type()), bucket.list_, true); iterator_map_.fix_iterators_2end(i_bucket, key); ++buffer_size_; if (buffer_size_ >= max_buffer_size_) _rebuild_buckets(); } } //! Reset hash-map: erase all values, invalidate all iterators void clear() { STXXL_VERBOSE_HASH_MAP("clear()"); iterator_map_.fix_iterators_all2end(); block_cache_.flush(); block_cache_.clear(); // reset buckets and release buffer-memory for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { _erase_nodes(buckets_[i_bucket].list_, NULL); buckets_[i_bucket] = bucket_type(); } oblivious_ = false; num_total_ = 0; buffer_size_ = 0; // free external memory block_manager* bm = block_manager::get_instance(); bm->delete_blocks(bids_.begin(), bids_.end()); bids_.clear(); } //! Exchange stored values with another hash-map //! \param obj hash-map to swap values with void swap(self_type& obj) { std::swap(buckets_, obj.buckets_); std::swap(bids_, obj.bids_); std::swap(oblivious_, obj.oblivious_); std::swap(num_total_, obj.num_total_); std::swap(node_allocator_, obj.node_allocator_); std::swap(hash_, obj.hash_); std::swap(cmp_, obj.cmp_); std::swap(buffer_size_, obj.buffer_size_); std::swap(max_buffer_size_, obj.max_buffer_size_); std::swap(opt_load_factor_, obj.opt_load_factor_); std::swap(iterator_map_, obj.iterator_map_); std::swap(block_cache_, obj.block_cache_); } protected: // find statistics mutable external_size_type n_subblocks_loaded; mutable external_size_type n_found_internal; mutable external_size_type n_found_external; mutable external_size_type n_not_found; public: //! Reset hash-map statistics void reset_statistics() { block_cache_.reset_statistics(); n_subblocks_loaded = n_found_external = n_found_internal = n_not_found = 0; } //! Print short general statistics to output stream void print_statistics(std::ostream& o = std::cout) const { o << "Find-statistics:" << std::endl; o << " Found internal : " << n_found_internal << std::endl; o << " Found external : " << n_found_external << std::endl; o << " Not found : " << n_not_found << std::endl; o << " Subblocks searched : " << n_subblocks_loaded << std::endl; iterator_map_.print_statistics(o); block_cache_.print_statistics(o); } //! Look up value by key. Non-const access. //! \param key key for value to look up iterator find(const key_type& key) { if (buffer_size_ + 1 >= max_buffer_size_) // (*) _rebuild_buckets(); internal_size_type i_bucket = _bkt_num(key); bucket_type& bucket = buckets_[i_bucket]; node_type* node = _find_key_internal(bucket, key); // found in internal-memory buffer if (node && _eq(node->value_.first, key)) { n_found_internal++; if (node->deleted()) return this->_end(); else return iterator(this, i_bucket, node, 0, src_internal, false, key); } // search external elements else { tuple result = _find_key_external(bucket, key); external_size_type i_external = result.first; value_type value = result.second; // found in external memory if (i_external < bucket.n_external_ && _eq(value.first, key)) { n_found_external++; // we ultimately expect the user to de-reference the returned // iterator to change its value (non-const!). to prevent an // additional disk-access, we create a new node in the // internal-memory buffer overwriting the external value. // note: by checking and rebuilding (if neccessary) in (*) we // made sure that the new node will fit into the buffer and no // rebuild is neccessary here. node_type* new_node = node ? node->set_next(_new_node(value, node->next(), false)) : (bucket.list_ = _new_node(value, bucket.list_, false)); ++buffer_size_; iterator_map_.fix_iterators_2int(i_bucket, value.first, new_node); return iterator(this, i_bucket, new_node, i_external + 1, src_internal, true, key); } // not found in external memory else { n_not_found++; return this->_end(); } } } //! Look up value by key. Const access. //! \param key key for value to look up const_iterator find(const key_type& key) const { internal_size_type i_bucket = _bkt_num(key); const bucket_type& bucket = buckets_[i_bucket]; node_type* node = _find_key_internal(bucket, key); // found in internal-memory buffer if (node && _eq(node->value_.first, key)) { n_found_internal++; if (node->deleted()) return this->_end(); else return const_iterator((self_type*)this, i_bucket, node, 0, src_internal, false, key); } // search external elements else { tuple result = _find_key_external(bucket, key); external_size_type i_external = result.first; value_type value = result.second; // found in external memory if (i_external < bucket.n_external_ && _eq(value.first, key)) { n_found_external++; return const_iterator((self_type*)this, i_bucket, node, i_external, src_external, true, key); } // not found in external memory else { n_not_found++; return this->_end(); } } } //! Number of values with given key //! \param k key for value to look up //! \return 0 or 1 depending on the presence of a value with the given key external_size_type count(const key_type& k) const { const_iterator cit = find(k); return (cit == end()) ? 0 : 1; } //! Finds a range containing all values with given key. Non-const access //! \param key key to look for# //! \return range may be empty or contains exactly one value std::pair equal_range(const key_type& key) { iterator it = find(key); return std::pair(it, it); } //! Finds a range containing all values with given key. Const access //! \param key key to look for# //! \return range may be empty or contains exactly one value std::pair equal_range(const key_type& key) const { const_iterator cit = find(key); return std::pair(cit, cit); } //! Convenience operator to quickly insert or find values. Use with caution //! since using this operator will check external-memory. mapped_type& operator [] (const key_type& key) { if (buffer_size_ + 1 >= max_buffer_size_) // (*) _rebuild_buckets(); internal_size_type i_bucket = _bkt_num(key); bucket_type& bucket = buckets_[i_bucket]; node_type* node = _find_key_internal(bucket, key); // found in internal-memory buffer if (node && _eq(node->value_.first, key)) { if (node->deleted()) { node->set_deleted(false); node->value_.second = mapped_type(); ++num_total_; } return node->value_.second; } // search external elements else { tuple result = _find_key_external(bucket, key); external_size_type i_external = result.first; value_type found_value = result.second; value_type buffer_value = (i_external < bucket.n_external_ && _eq(found_value.first, key)) ? found_value : value_type(key, mapped_type()); // add a new node to the buffer. this new node's value overwrites // the external value if it was found and otherwise is set to (key, // mapped_type()) node_type* new_node = node ? node->set_next(_new_node(buffer_value, node->next(), false)) : (bucket.list_ = _new_node(buffer_value, bucket.list_, false)); ++buffer_size_; // note that we already checked the buffer-size in (*) return new_node->value_.second; } } //! Number of buckets internal_size_type bucket_count() const { return buckets_.size(); } //! Maximum number of buckets internal_size_type max_bucket_count() const { return (internal_size_type)(max_size() / subblock_size); } //! Bucket-index for values with given key. internal_size_type bucket_index(const key_type& k) const { return _bkt_num(k); } public: //! Average number of (sub)blocks occupied by a bucket. float load_factor() const { return (float)num_total_ / ((float)subblock_size * (float)buckets_.size()); } //! Get desired load-factor float opt_load_factor() const { return opt_load_factor_; } //! Set desired load-factor void opt_load_factor(float z) { opt_load_factor_ = z; if (load_factor() > opt_load_factor_) _rebuild_buckets(); } //! Rehash with (at least) n buckets void rehash(internal_size_type n = 0) { _rebuild_buckets(n); } //! Number of bytes occupied by buffer internal_size_type buffer_size() const { // buffer-size internally stored as number of nodes return buffer_size_ * sizeof(node_type); } //! Maximum buffer size in byte internal_size_type max_buffer_size() const { return max_buffer_size_ * sizeof(node_type); } //! Set maximum buffer size //! \param buffer_size new size in byte void max_buffer_size(internal_size_type buffer_size) { max_buffer_size_ = buffer_size / sizeof(node_type); if (buffer_size_ >= max_buffer_size_) _rebuild_buckets(); } protected: //! iterator pointing to the beginnning of the hash-map template Iterator _begin() const { self_type* non_const_this = (self_type*)this; if (buckets_.size() == 0) return _end(); // correct key will be set by find_next() Iterator it(non_const_this, 0, buckets_[0].list_, 0, src_unknown, true, key_type()); it.find_next(); return it; } //! iterator pointing to the end of the hash-map (iterator-type as //! template-parameter) template Iterator _end() const { self_type* non_const_this = (self_type*)this; return Iterator(non_const_this); } public: //! Returns an iterator pointing to the beginning of the hash-map iterator begin() { return _begin(); } //! Returns a const_interator pointing to the beginning of the hash-map const_iterator begin() const { return _begin(); } //! Returns an iterator pointing to the end of the hash-map iterator end() { return _end(); } //! Returns a const_iterator pointing to the end of the hash-map const_iterator end() const { return _end(); } protected: //! Allocate a new buffer-node node_type * _get_node() { return node_allocator_.allocate(1); } //! Free given node void _put_node(node_type* node) { node_allocator_.deallocate(node, 1); } //! Allocate a new buffer-node and initialize with given value, node and //! deleted-flag node_type * _new_node(const value_type& value, node_type* nxt, bool del) { node_type* node = _get_node(); node->value_ = value; node->set_next(nxt); node->set_deleted(del); return node; } //! Free nodes in range [first, last). If last is NULL all nodes will be //! freed. void _erase_nodes(node_type* first, node_type* last) { node_type* curr = first; while (curr != last) { node_type* next = curr->next(); _put_node(curr); curr = next; } } //! Bucket-index for values with given key internal_size_type _bkt_num(const key_type& key) const { return _bkt_num(key, buckets_.size()); } /*! * Bucket-index for values with given key. The total number of buckets has * to be specified as well. The bucket number is determined by \f$ * bucket_num = (hash/max_hash)*n_buckets \f$ max_hash is in fact 2^63-1 * (internal_size_type=uint64 (or uint32)) but we rather divide by 2^64, so * we can use plain integer arithmetic easily (there should be only a small * difference): this way we must only calculate the upper 64 bits of the * product hash*n_buckets and we're done. See * http://www.cs.uaf.edu/~cs301/notes/Chapter5/node5.html */ internal_size_type _bkt_num(const key_type& key, internal_size_type n) const { //! TODO maybe specialize double arithmetic to integer. the old code //! was faulty -tb. return (internal_size_type)( (double)n * ((double)hash_(key) / (double)std::numeric_limits::max()) ); } /*! * Locate the given key in the internal-memory chained list. If the key is * not present, the node with the biggest key smaller than the given key is * returned. Note that the returned value may be zero: either because the * chained list is empty or because the given key is smaller than all other * keys in the chained list. */ node_type* _find_key_internal(const bucket_type& bucket, const key_type& key) const { node_type* old = NULL; for (node_type* curr = bucket.list_; curr && _leq(curr->value_.first, key); curr = curr->next()) { old = curr; } return old; } /*! * Search for key in external part of bucket. Return value is (i_external, * value), where i_ext = bucket._num_external if key could not be found. */ tuple _find_key_external(const bucket_type& bucket, const key_type& key) const { subblock_type* subblock; // number of subblocks occupied by bucket internal_size_type n_subblocks = (internal_size_type)( bucket.n_external_ / subblock_size ); if (bucket.n_external_ % subblock_size != 0) n_subblocks++; for (internal_size_type i_subblock = 0; i_subblock < n_subblocks; i_subblock++) { subblock = _load_subblock(bucket, i_subblock); // number of values in i-th subblock internal_size_type n_values = (i_subblock + 1 < n_subblocks) ? (internal_size_type)subblock_size : (internal_size_type)( bucket.n_external_ - i_subblock * subblock_size ); //! TODO: replace with bucket.n_external_ % subblock_size // biggest key in current subblock still too small => next subblock if (_lt((*subblock)[n_values - 1].first, key)) continue; // binary search in current subblock internal_size_type i_lower = 0, i_upper = n_values; while (i_lower + 1 != i_upper) { internal_size_type i_middle = (i_lower + i_upper) / 2; if (_leq((*subblock)[i_middle].first, key)) i_lower = i_middle; else i_upper = i_middle; } value_type value = (*subblock)[i_lower]; if (_eq(value.first, key)) return tuple (i_subblock * subblock_size + i_lower, value); else return tuple (bucket.n_external_, value_type()); } return tuple (bucket.n_external_, value_type()); } /*! * Load the given bucket's i-th subblock. * Since a bucket may be spread over several blocks, we must * 1. determine in which block the requested subblock is located * 2. at which position within the obove-mentioned block the questioned subblock is located */ subblock_type* _load_subblock(const bucket_type& bucket, internal_size_type which_subblock) const { n_subblocks_loaded++; // index of the requested subblock counted from the very beginning of // the bucket's first block external_size_type i_abs_subblock = bucket.i_subblock_ + which_subblock; /* 1. */ bid_type bid = bids_[bucket.i_block_ + (internal_size_type)(i_abs_subblock / subblocks_per_block)]; /* 2. */ internal_size_type i_subblock_within = (internal_size_type)(i_abs_subblock % subblocks_per_block); return block_cache_.get_subblock(bid, i_subblock_within); } typedef HashedValue hashed_value_type; //! Functor to extracts the actual value from a HashedValue-struct struct HashedValueExtractor { value_type& operator () (hashed_value_type& hvalue) { return hvalue.value_; } }; /*! * Will return from its input-stream all values that are to be stored in * the given bucket. Those values must appear in consecutive order * beginning with the input-stream's current value. */ template struct HashingStream { typedef typename InputStream::value_type value_type; self_type* map_; InputStream& input_; internal_size_type i_bucket_; external_size_type bucket_size_; value_type value_; bool empty_; ValueExtractor vextract_; HashingStream(InputStream& input, internal_size_type i_bucket, ValueExtractor vextract, self_type* map) : map_(map), input_(input), i_bucket_(i_bucket), bucket_size_(0), vextract_(vextract) { empty_ = find_next(); } const value_type& operator * () { return value_; } bool empty() const { return empty_; } void operator ++ () { ++input_; empty_ = find_next(); } bool find_next() { if (input_.empty()) return true; value_ = *input_; if (map_->_bkt_num(vextract_(value_).first) != i_bucket_) return true; ++bucket_size_; return false; } }; /* Rebuild hash-map. The desired number of buckets may be supplied. */ void _rebuild_buckets(internal_size_type n_desired = 0) { STXXL_VERBOSE_HASH_MAP("_rebuild_buckets()"); typedef buffered_writer writer_type; typedef HashedValuesStream values_stream_type; typedef HashingStream hashing_stream_type; const int_type write_buffer_size = config::get_instance()->disks_number() * 4; // determine new number of buckets from desired load_factor ... internal_size_type n_new; n_new = (internal_size_type)ceil((double)num_total_ / ((double)subblock_size * (double)opt_load_factor())); // ... but give the user the chance to request even more buckets if (n_desired > n_new) n_new = std::min(n_desired, max_bucket_count()); // allocate new buckets and bids buckets_container_type old_buckets(n_new); std::swap(buckets_, old_buckets); bid_container_type old_bids; std::swap(bids_, old_bids); // read stored values in consecutive order // use new to control point of destruction (see below) reader_type* reader = new reader_type(old_bids.begin(), old_bids.end(), block_cache_); values_stream_type values_stream(old_buckets.begin(), old_buckets.end(), *reader, old_bids.begin(), *this); writer_type writer(&bids_, write_buffer_size, write_buffer_size / 2); // re-distribute values among new buckets. // this makes use of the fact that if value1 preceeds value2 before // resizing, value1 will preceed value2 after resizing as well (uniform // rehashing) num_total_ = 0; for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { buckets_[i_bucket] = bucket_type(); buckets_[i_bucket].i_block_ = writer.i_block(); buckets_[i_bucket].i_subblock_ = writer.i_subblock(); hashing_stream_type hasher(values_stream, i_bucket, HashedValueExtractor(), this); external_size_type i_ext = 0; while (!hasher.empty()) { const hashed_value_type& hvalue = *hasher; iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, i_ext); writer.append(hvalue.value_); ++hasher; ++i_ext; } writer.finish_subblock(); buckets_[i_bucket].n_external_ = hasher.bucket_size_; num_total_ += hasher.bucket_size_; } writer.flush(); // reader must be deleted before deleting old_bids because its // destructor will dereference the bid-iterator delete reader; block_cache_.clear(); // get rid of old blocks and buckets block_manager* bm = stxxl::block_manager::get_instance(); bm->delete_blocks(old_bids.begin(), old_bids.end()); for (internal_size_type i_bucket = 0; i_bucket < old_buckets.size(); i_bucket++) { _erase_nodes(old_buckets[i_bucket].list_, NULL); old_buckets[i_bucket] = bucket_type(); } buffer_size_ = 0; oblivious_ = false; } /*! * Stream for filtering duplicates. Used to eliminate duplicated values * when bulk-inserting Note: input has to be sorted, so that duplicates * will occure in row */ template struct UniqueValueStream { typedef typename InputStream::value_type value_type; self_type& map_; InputStream& in_; UniqueValueStream(InputStream& input, self_type& map) : map_(map), in_(input) { } bool empty() const { return in_.empty(); } const value_type& operator * () { return *in_; } void operator ++ () { value_type v_old = *in_; ++in_; while (!in_.empty() && v_old.first == (*in_).first) ++in_; } }; template struct AddHashStream { //! (hash,value) typedef std::pair value_type; self_type& map_; InputStream& in_; AddHashStream(InputStream& input, self_type& map) : map_(map), in_(input) { } bool empty() const { return in_.empty(); } value_type operator * () { return value_type(map_.hash_((*in_).first), *in_); } void operator ++ () { ++in_; } }; /*! * Extracts the value-part (ignoring the hashvalue); required by * HashingStream (see above) */ struct StripHashFunctor { const value_type& operator () (std::pair& v) { return v.second; } }; /*! * Comparator object for values as required by stxxl::sort. Sorting is done * lexicographically by Note: the hash-value has already * been computed. */ struct Cmp : public std::binary_function< std::pair, std::pair, bool > { self_type& map_; Cmp(self_type& map) : map_(map) { } bool operator () (const std::pair& a, const std::pair& b) const { return (a.first < b.first) || ((a.first == b.first) && map_.cmp_(a.second.first, b.second.first)); } std::pair min_value() const { return std::pair( std::numeric_limits::min(), value_type(map_.cmp_.min_value(), mapped_type()) ); } std::pair max_value() const { return std::pair( std::numeric_limits::max(), value_type(map_.cmp_.max_value(), mapped_type()) ); } }; public: //! Bulk-insert of values in the range [f, l) //! \param f beginning of the range //! \param l end of the range //! \param mem internal memory that may be used (note: this memory will be used additionally to the buffer). The more the better template void insert(InputIterator f, InputIterator l, internal_size_type mem) { //! values already stored in the hashtable ("old values") typedef HashedValuesStream old_values_stream; //! old values, that are to be stored in a certain (new) bucket typedef HashingStream old_hashing_stream; //! values to insert ("new values") typedef typename stxxl::stream::streamify_traits::stream_type input_stream; //! new values with added hash: (hash, (key, mapped)) typedef AddHashStream new_values_stream; //! new values sorted by typedef stxxl::stream::sort new_sorted_values_stream; //! new values sorted by with duplicates eliminated typedef UniqueValueStream new_unique_values_stream; //! new values, that are to be stored in a certain bucket typedef HashingStream new_hashing_stream; typedef buffered_writer writer_type; int_type write_buffer_size = config::get_instance()->disks_number() * 2; // calculate new number of buckets external_size_type num_total_new = num_total_ + (l - f); // estimated number of elements external_size_type n_buckets_new = (external_size_type)ceil((double)num_total_new / ((double)subblock_size * (double)opt_load_factor())); if (n_buckets_new > max_bucket_count()) n_buckets_new = max_bucket_count(); STXXL_VERBOSE_HASH_MAP("insert() items=" << (l - f) << " buckets_new=" << n_buckets_new); // prepare new buckets and bids buckets_container_type old_buckets((internal_size_type)n_buckets_new); std::swap(buckets_, old_buckets); // writer will allocate new blocks as necessary bid_container_type old_bids; std::swap(bids_, old_bids); // already stored values ("old values") reader_type* reader = new reader_type(old_bids.begin(), old_bids.end(), block_cache_); old_values_stream old_values(old_buckets.begin(), old_buckets.end(), *reader, old_bids.begin(), *this); // values to insert ("new values") input_stream input = stxxl::stream::streamify(f, l); new_values_stream new_values(input, *this); new_sorted_values_stream new_sorted_values(new_values, Cmp(*this), mem); new_unique_values_stream new_unique_values(new_sorted_values, *this); writer_type writer(&bids_, write_buffer_size, write_buffer_size / 2); num_total_ = 0; for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { buckets_[i_bucket] = bucket_type(); buckets_[i_bucket].i_block_ = writer.i_block(); buckets_[i_bucket].i_subblock_ = writer.i_subblock(); old_hashing_stream old_hasher(old_values, i_bucket, HashedValueExtractor(), this); new_hashing_stream new_hasher(new_unique_values, i_bucket, StripHashFunctor(), this); internal_size_type bucket_size = 0; // more old and new values for the current bucket => choose smallest while (!old_hasher.empty() && !new_hasher.empty()) { internal_size_type old_hash = hash_((*old_hasher).value_.first); internal_size_type new_hash = (*new_hasher).first; key_type old_key = (*old_hasher).value_.first; key_type new_key = (*new_hasher).second.first; // old value wins if ((old_hash < new_hash) || (old_hash == new_hash && cmp_(old_key, new_key))) // (_lt((*old_hasher)._value.first, (*new_hasher).second.first)) { const hashed_value_type& hvalue = *old_hasher; iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, bucket_size); writer.append(hvalue.value_); ++old_hasher; } // new value smaller or equal => new value wins else { if (_eq(old_key, new_key)) { const hashed_value_type& hvalue = *old_hasher; iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, bucket_size); ++old_hasher; } writer.append((*new_hasher).second); ++new_hasher; } ++bucket_size; } // no more new values for the current bucket while (!old_hasher.empty()) { const hashed_value_type& hvalue = *old_hasher; iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, bucket_size); writer.append(hvalue.value_); ++old_hasher; ++bucket_size; } // no more old values for the current bucket while (!new_hasher.empty()) { writer.append((*new_hasher).second); ++new_hasher; ++bucket_size; } writer.finish_subblock(); buckets_[i_bucket].n_external_ = bucket_size; num_total_ += bucket_size; } writer.flush(); delete reader; block_cache_.clear(); // release old blocks block_manager* bm = stxxl::block_manager::get_instance(); bm->delete_blocks(old_bids.begin(), old_bids.end()); // free nodes in old bucket lists for (internal_size_type i_bucket = 0; i_bucket < old_buckets.size(); i_bucket++) { _erase_nodes(old_buckets[i_bucket].list_, NULL); old_buckets[i_bucket] = bucket_type(); } buffer_size_ = 0; oblivious_ = false; } protected: /* 1 iff a < b The comparison is done lexicographically by (hash-value, key) */ bool _lt(const key_type& a, const key_type& b) const { internal_size_type hash_a = hash_(a); internal_size_type hash_b = hash_(b); return (hash_a < hash_b) || ((hash_a == hash_b) && cmp_(a, b)); } //! true iff a > b bool _gt(const key_type& a, const key_type& b) const { return _lt(b, a); } //! true iff a <= b bool _leq(const key_type& a, const key_type& b) const { return !_gt(a, b); } //! true iff a >= b bool _geq(const key_type& a, const key_type& b) const { return !_lt(a, b); } //! true iff a == b. note: it is mandatory that equal keys yield equal //! hash-values => hashing not neccessary for equality-testing. bool _eq(const key_type& a, const key_type& b) const { return !cmp_(a, b) && !cmp_(b, a); } friend class hash_map_iterator_base; friend class hash_map_iterator; friend class hash_map_const_iterator; friend class iterator_map; friend class block_cache; friend struct HashedValuesStream; #if 1 void _dump_external() { reader_type reader(bids_.begin(), bids_.end(), &block_cache_); for (internal_size_type i_block = 0; i_block < bids_.size(); i_block++) { std::cout << "block " << i_block << ":\n"; for (internal_size_type i_subblock = 0; i_subblock < subblocks_per_block; i_subblock++) { std::cout << " subblock " << i_subblock << ":\n "; for (external_size_type i_element = 0; i_element < subblocks_per_block; i_element++) { std::cout << reader.const_value().first << ", "; ++reader; } std::cout << std::endl; } } } void _dump_buckets() { reader_type reader(bids_.begin(), bids_.end(), &block_cache_); std::cout << "number of buckets: " << buckets_.size() << std::endl; for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { const bucket_type& bucket = buckets_[i_bucket]; reader.skip_to(bids_.begin() + bucket.i_block_, bucket.i_subblock_); std::cout << " bucket " << i_bucket << ": block=" << bucket.i_block_ << ", subblock=" << bucket.i_subblock_ << ", external=" << bucket.n_external_ << std::endl; node_type* node = bucket.list_; std::cout << " internal_list="; while (node) { std::cout << node->value_.first << " (del=" << node->deleted() << "), "; node = node->next(); } std::cout << std::endl; std::cout << " external="; for (external_size_type i_element = 0; i_element < bucket.n_external_; i_element++) { std::cout << reader.const_value().first << ", "; ++reader; } std::cout << std::endl; } } void _dump_bucket_statistics() { std::cout << "number of buckets: " << buckets_.size() << std::endl; for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { const bucket_type& bucket = buckets_[i_bucket]; std::cout << " bucket " << i_bucket << ": block=" << bucket.i_block_ << ", subblock=" << bucket.i_subblock_ << ", external=" << bucket.n_external_ << ", list=" << bucket.list_ << std::endl; } } #endif public: //! Construct an equality predicate from the comparison operator struct equal_to : public std::binary_function { //! reference to hash_map const self_type& m_map; //! constructor requires reference to hash_map equal_to(const self_type& map) : m_map(map) { } //! return whether the arguments compare equal (x==y). bool operator () (const key_type& x, const key_type& y) const { return m_map._eq(x, y); } //! C++11 required type typedef key_type first_argument_type; //! C++11 required type typedef key_type second_argument_type; //! C++11 required type typedef bool result_type; }; //! Type of constructed equality predicate typedef equal_to key_equal; //! Constructed equality predicate used by this hash-map key_equal key_eq() const { return equal_to(*this); } public: //! Even more statistics: Number of buckets, number of values, buffer-size, //! values per bucket void print_load_statistics(std::ostream& o = std::cout) const { external_size_type sum_external = 0; external_size_type square_sum_external = 0; external_size_type max_external = 0; for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { const bucket_type& b = buckets_[i_bucket]; sum_external += b.n_external_; square_sum_external += b.n_external_ * b.n_external_; if (b.n_external_ > max_external) max_external = b.n_external_; } double avg_external = (double)sum_external / (double)buckets_.size(); double std_external = sqrt(((double)square_sum_external / (double)buckets_.size()) - (avg_external * avg_external)); o << "Bucket count : " << buckets_.size() << std::endl; o << "Values total : " << num_total_ << std::endl; o << "Values buffered : " << buffer_size_ << std::endl; o << "Max Buffer-Size : " << max_buffer_size_ << std::endl; o << "Max external/bucket : " << max_external << std::endl; o << "Avg external/bucket : " << avg_external << std::endl; o << "Std external/bucket : " << std_external << std::endl; o << "Load-factor : " << load_factor() << std::endl; o << "Blocks allocated : " << bids_.size() << " => " << (bids_.size() * block_type::raw_size) << " bytes" << std::endl; o << "Bytes per value : " << ((double)(bids_.size() * block_type::raw_size) / (double)num_total_) << std::endl; } }; /* end of class hash_map */ } // namespace hash_map STXXL_END_NAMESPACE namespace std { template void swap(stxxl::hash_map::hash_map& a, stxxl::hash_map::hash_map& b) { if (&a != &b) a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_HASH_MAP_HASH_MAP_HEADER stxxl-1.4.1/include/stxxl/bits/containers/hash_map/util.h000644 001411 000144 00000041760 12423673631 023335 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/hash_map/util.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_HASH_MAP_UTIL_HEADER #define STXXL_CONTAINERS_HASH_MAP_UTIL_HEADER #define STXXL_CONTAINERS_HASHMAP__UTIL_H #include #include #include #include STXXL_BEGIN_NAMESPACE namespace hash_map { // For internal memory chaining: struct to compose next-pointer and delete-flag // share the same memory: the lowest bit is occupied by the del-flag. template struct node { node* next_and_del_; ValueType value_; //! check if the next node is deleted. bool deleted() { return ((int_type)next_and_del_ & 0x01) == 1; } //! change deleted flag on the next node bool set_deleted(bool d) { next_and_del_ = (node*)(((int_type)next_and_del_ & ~0x01) | (int_type)d); return d; } //! return the next node, without the "next" flag. node * next() { return (node*)((int_type)next_and_del_ & ~0x01); } //! change the "next" value of next node pointer node * set_next(node* n) { next_and_del_ = (node*)(((int_type)next_and_del_ & 0x01) | (int_type)n); return n; } }; template struct bucket { //! entry point to the chain in internal memory NodeType* list_; //! number of elements in external memory external_size_type n_external_; //! index of first block's bid (to be used as index for hash_map's //! bids_-array internal_size_type i_block_; //! index of first subblock internal_size_type i_subblock_; bucket() : list_(NULL), n_external_(0), i_block_(0), i_subblock_(0) { } bucket(NodeType* list, external_size_type n_external, internal_size_type i_block, internal_size_type i_subblock) : list_(list), n_external_(n_external), i_block_(i_block), i_subblock_(i_subblock) { } }; //! Used to scan external memory with prefetching. template class buffered_reader : private noncopyable { public: typedef CacheType cache_type; typedef BidIterator bid_iterator; typedef typename cache_type::block_type block_type; typedef typename block_type::value_type subblock_type; typedef typename subblock_type::value_type value_type; typedef typename bid_iterator::value_type bid_type; enum { block_size = block_type::size, subblock_size = subblock_type::size }; private: //! index within current block unsigned_type i_value_; //! points to the beginning of the block-sequence bid_iterator begin_bid_; //! points to the current block bid_iterator curr_bid_; //! points to the end of the block-sequence bid_iterator end_bid_; //! points to the next block to prefetch bid_iterator pref_bid_; //! shared block-cache cache_type& cache_; //! true if prefetching enabled bool prefetch_; //! pages, which are read at once from disk, consist of this many blocks unsigned_type page_size_; //! number of pages to prefetch unsigned_type prefetch_pages_; //! current block dirty ? bool dirty_; //! current subblock subblock_type* subblock_; public: //! Create a new buffered reader to read the blocks in [seq_begin, seq_end) //! \param seq_begin First block's bid //! \param seq_end Last block's bid //! \param cache Block-cache used for prefetching //! \param i_subblock Start reading from this subblock //! \param prefetch Enable/Disable prefetching buffered_reader(bid_iterator seq_begin, bid_iterator seq_end, cache_type& cache, internal_size_type i_subblock = 0, bool prefetch = true) : i_value_(0), begin_bid_(seq_begin), curr_bid_(seq_begin), end_bid_(seq_end), cache_(cache), prefetch_(false), page_size_(tuning::get_instance()->prefetch_page_size), prefetch_pages_(tuning::get_instance()->prefetch_pages), dirty_(false), subblock_(NULL) { if (seq_begin == seq_end) return; if (prefetch) enable_prefetching(); // will (amongst other things) set subblock_ and retain current block skip_to(seq_begin, i_subblock); } ~buffered_reader() { if (curr_bid_ != end_bid_) cache_.release_block(*curr_bid_); } void enable_prefetching() { if (prefetch_) return; prefetch_ = true; pref_bid_ = curr_bid_; // start prefetching page_size*prefetch_pages blocks beginning with current one for (unsigned_type i = 0; i < page_size_ * prefetch_pages_; i++) { if (pref_bid_ == end_bid_) break; cache_.prefetch_block(*pref_bid_); ++pref_bid_; } } //! Get const-reference to current value. const value_type & const_value() { return (*subblock_)[i_value_ % subblock_size]; } //! Get reference to current value. The current value's block's dirty flag //! will be set. value_type & value() { if (!dirty_) { cache_.make_dirty(*curr_bid_); dirty_ = true; } return (*subblock_)[i_value_ % subblock_size]; } //! Advance to the next value //! \return false if last value has been reached, otherwise true. bool operator ++ () { if (curr_bid_ == end_bid_) return false; // same block if (i_value_ + 1 < block_size * subblock_size) { i_value_++; } // entered new block else { cache_.release_block(*curr_bid_); i_value_ = 0; dirty_ = false; ++curr_bid_; if (curr_bid_ == end_bid_) return false; cache_.retain_block(*curr_bid_); // if a complete page has been consumed, prefetch the next one if (prefetch_ && (curr_bid_ - begin_bid_) % page_size_ == 0) { for (unsigned i = 0; i < page_size_; i++) { if (pref_bid_ == end_bid_) break; cache_.prefetch_block(*pref_bid_); ++pref_bid_; } } } // entered new subblock if (i_value_ % subblock_size == 0) { subblock_ = cache_.get_subblock(*curr_bid_, i_value_ / subblock_size); } return true; } //! Skip remaining values of the current subblock. void next_subblock() { i_value_ = (i_value_ / subblock_size + 1) * subblock_size - 1; operator ++ (); // takes care of prefetching etc } //! Continue reading at given block and subblock. void skip_to(bid_iterator bid, internal_size_type i_subblock) { if (curr_bid_ == end_bid_) return; if (bid != curr_bid_) dirty_ = false; cache_.release_block(*curr_bid_); if (bid == end_bid_) return; // skip to block while (curr_bid_ != bid) { ++curr_bid_; if (prefetch_ && (curr_bid_ - begin_bid_) % page_size_ == 0) { for (unsigned i = 0; i < page_size_; i++) { if (pref_bid_ == end_bid_) break; cache_.prefetch_block(*pref_bid_); ++pref_bid_; } } } // skip to subblock i_value_ = i_subblock * subblock_size; subblock_ = cache_.get_subblock(*curr_bid_, i_subblock); cache_.retain_block(*curr_bid_); } }; //! Buffered writing of values. New Blocks are allocated as needed. template class buffered_writer : private noncopyable { public: typedef BlockType block_type; typedef BidContainer bid_container_type; typedef typename block_type::value_type subblock_type; typedef typename subblock_type::value_type value_type; typedef stxxl::buffered_writer writer_type; enum { block_size = block_type::size, subblock_size = subblock_type::size }; private: //! buffered writer writer_type writer_; //! current buffer-block block_type* block_; //! sequence of allocated blocks (to be expanded as needed) bid_container_type* bids_; //! current block's index unsigned_type i_block_; //! current value's index in the range of [0..\#values per block[ unsigned_type i_value_; //! number of blocks to allocate in a row unsigned_type increase_; public: //! Create a new buffered writer. //! \param c write values to these blocks (c holds the bids) //! \param buffer_size Number of write-buffers to use //! \param batch_size bulk buffered writing buffered_writer(bid_container_type* c, int_type buffer_size, int_type batch_size) : writer_(buffer_size, batch_size), bids_(c), i_block_(0), i_value_(0), increase_(config::get_instance()->disks_number() * 3) { block_ = writer_.get_free_block(); } ~buffered_writer() { flush(); } //! Write all values from given stream. template void append_from_stream(StreamType& stream) { while (!stream.empty()) { append(*stream); ++stream; } } //! Write given value. void append(const value_type& value) { internal_size_type i_subblock = (i_value_ / subblock_size); (*block_)[i_subblock][i_value_ % subblock_size] = value; if (i_value_ + 1 < block_size * subblock_size) i_value_++; // reached end of a block else { i_value_ = 0; // allocate new blocks if neccessary ... if (i_block_ == bids_->size()) { bids_->resize(bids_->size() + increase_); block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(striping(), bids_->end() - increase_, bids_->end()); } // ... and write current block block_ = writer_.write(block_, (*bids_)[i_block_]); i_block_++; } } //! Continue writing at the beginning of the next subblock. TODO more //! efficient void finish_subblock() { i_value_ = (i_value_ / subblock_size + 1) * subblock_size - 1; append(value_type()); // writing and allocating blocks etc } //! Flushes not yet written blocks. void flush() { i_value_ = 0; if (i_block_ == bids_->size()) { bids_->resize(bids_->size() + increase_); block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(striping(), bids_->end() - increase_, bids_->end()); } block_ = writer_.write(block_, (*bids_)[i_block_]); i_block_++; writer_.flush(); } //! Index of current block. internal_size_type i_block() { return i_block_; } //! Index of current subblock. internal_size_type i_subblock() { return i_value_ / subblock_size; } }; /*! * Additional information about a stored value: * - the bucket in which it can be found * - where it is currently stored (intern or extern) * - the buffer-node * - the position in external memory */ template struct HashedValue { typedef HashMap hash_map_type; typedef typename hash_map_type::value_type value_type; typedef typename hash_map_type::source_type source_type; typedef typename hash_map_type::node_type node_type; typedef typename hash_map_type::internal_size_type internal_size_type; typedef typename hash_map_type::external_size_type external_size_type; value_type value_; internal_size_type i_bucket_; source_type source_; node_type* node_; external_size_type i_external_; HashedValue() : i_bucket_(internal_size_type(-1)) { } HashedValue(const value_type& value, internal_size_type i_bucket, source_type src, node_type* node, external_size_type i_external) : value_(value), i_bucket_(i_bucket), source_(src), node_(node), i_external_(i_external) { } }; /*! * Stream interface for all value-pairs currently stored in the map. Returned * values are HashedValue-objects (actual value enriched with information on * where the value can be found (bucket-number, internal, external)). Values, * marked as deleted in internal-memory, are not returned; for modified values * only the one in internal memory is returned. */ template struct HashedValuesStream { typedef HashMap hash_map_type; typedef HashedValue value_type; typedef typename hash_map_type::node_type node_type; typedef typename hash_map_type::bid_container_type::iterator bid_iterator; typedef typename hash_map_type::buckets_container_type::iterator bucket_iterator; typedef typename hash_map_type::internal_size_type internal_size_type; typedef typename hash_map_type::external_size_type external_size_type; hash_map_type& map_; Reader& reader_; bucket_iterator curr_bucket_; bucket_iterator end_bucket_; bid_iterator begin_bid_; internal_size_type i_bucket_; node_type* node_; external_size_type i_external_; value_type value_; HashedValuesStream(bucket_iterator begin_bucket, bucket_iterator end_bucket, Reader& reader, bid_iterator begin_bid, hash_map_type& map) : map_(map), reader_(reader), curr_bucket_(begin_bucket), end_bucket_(end_bucket), begin_bid_(begin_bid), i_bucket_(0), node_(curr_bucket_ != end_bucket_ ? curr_bucket_->list_ : NULL), i_external_(0) { if (!empty()) value_ = find_next(); } const value_type& operator * () { return value_; } bool empty() const { return curr_bucket_ == end_bucket_; } void operator ++ () { if (value_.source_ == hash_map_type::src_internal) node_ = node_->next(); else { ++reader_; ++i_external_; } value_ = find_next(); } value_type find_next() { while (true) { // internal and external elements available while (node_ && i_external_ < curr_bucket_->n_external_) { if (map_._leq(node_->value_.first, reader_.const_value().first)) { if (map_._eq(node_->value_.first, reader_.const_value().first)) { ++reader_; ++i_external_; } if (!node_->deleted()) return value_type(node_->value_, i_bucket_, hash_map_type::src_internal, node_, i_external_); else node_ = node_->next(); } else return value_type(reader_.const_value(), i_bucket_, hash_map_type::src_external, node_, i_external_); } // only internal elements left while (node_) { if (!node_->deleted()) return value_type(node_->value_, i_bucket_, hash_map_type::src_internal, node_, i_external_); else node_ = node_->next(); } // only external elements left while (i_external_ < curr_bucket_->n_external_) return value_type(reader_.const_value(), i_bucket_, hash_map_type::src_external, node_, i_external_); // if we made it to this point there are obviously no more values in the current bucket // let's try the next one (outer while-loop!) ++curr_bucket_; ++i_bucket_; if (curr_bucket_ == end_bucket_) return value_type(); node_ = curr_bucket_->list_; i_external_ = 0; reader_.skip_to(begin_bid_ + curr_bucket_->i_block_, curr_bucket_->i_subblock_); } } }; } // namespace hash_map STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_HASH_MAP_UTIL_HEADER stxxl-1.4.1/include/stxxl/bits/containers/matrix_low_level.h000644 001411 000144 00000110416 12405153574 024146 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/matrix_low_level.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_MATRIX_LOW_LEVEL_HEADER #define STXXL_CONTAINERS_MATRIX_LOW_LEVEL_HEADER #ifndef STXXL_BLAS #define STXXL_BLAS 0 #endif #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup matrix //! \{ namespace matrix_local { // forward declaration template struct matrix_operations; // generic declaration template struct switch_major_index; // row-major specialization template struct switch_major_index { inline switch_major_index(const int_type row, const int_type col) : i(row * BlockSideLength + col) { } inline operator int_type& () { return i; } private: int_type i; }; //column-major specialization template struct switch_major_index { inline switch_major_index(const int_type row, const int_type col) : i(row + col * BlockSideLength) { } inline operator int_type& () { return i; } private: int_type i; }; //! c = a [op] b; for arbitrary entries template struct low_level_matrix_binary_ass_op { low_level_matrix_binary_ass_op(ValueType* c, const ValueType* a, const ValueType* b, Op op = Op()) { if (a) if (b) #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type row = 0; row < int_type(BlockSideLength); ++row) for (int_type col = 0; col < int_type(BlockSideLength); ++col) op(c[switch_major_index < BlockSideLength, false > (row, col)], a[switch_major_index < BlockSideLength, a_transposed > (row, col)], b[switch_major_index < BlockSideLength, b_transposed > (row, col)]); else #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type row = 0; row < int_type(BlockSideLength); ++row) for (int_type col = 0; col < int_type(BlockSideLength); ++col) op(c[switch_major_index < BlockSideLength, false > (row, col)], a[switch_major_index < BlockSideLength, a_transposed > (row, col)], 0); else { assert(b /* do not add nothing to nothing */); #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type row = 0; row < int_type(BlockSideLength); ++row) for (int_type col = 0; col < int_type(BlockSideLength); ++col) op(c[switch_major_index < BlockSideLength, false > (row, col)], 0, b[switch_major_index < BlockSideLength, b_transposed > (row, col)]); } } }; //! c [op]= a; for arbitrary entries template struct low_level_matrix_unary_ass_op { low_level_matrix_unary_ass_op(ValueType* c, const ValueType* a, Op op = Op()) { if (a) #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type row = 0; row < int_type(BlockSideLength); ++row) for (int_type col = 0; col < int_type(BlockSideLength); ++col) op(c[switch_major_index < BlockSideLength, false > (row, col)], a[switch_major_index < BlockSideLength, a_transposed > (row, col)]); } }; //! c =[op] a; for arbitrary entries template struct low_level_matrix_unary_op { low_level_matrix_unary_op(ValueType* c, const ValueType* a, Op op = Op()) { assert(a); #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type row = 0; row < int_type(BlockSideLength); ++row) for (int_type col = 0; col < int_type(BlockSideLength); ++col) c[switch_major_index < BlockSideLength, false > (row, col)] = op(a[switch_major_index < BlockSideLength, a_transposed > (row, col)]); } }; //! multiplies matrices A and B, adds result to C, for arbitrary entries //! param pointer to blocks of A,B,C; elements in blocks have to be in row-major /* designated usage as: * void * low_level_matrix_multiply_and_add(const double * a, bool a_in_col_major, const double * b, bool b_in_col_major, double * c, const bool c_in_col_major) */ template struct low_level_matrix_multiply_and_add { low_level_matrix_multiply_and_add(const ValueType* a, bool a_in_col_major, const ValueType* b, bool b_in_col_major, ValueType* c, const bool c_in_col_major) { if (c_in_col_major) { std::swap(a, b); bool a_cm = ! b_in_col_major; b_in_col_major = ! a_in_col_major; a_in_col_major = a_cm; } if (! a_in_col_major) { if (! b_in_col_major) { // => both row-major #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables for (unsigned_type k = 0; k < BlockSideLength; ++k) for (unsigned_type j = 0; j < BlockSideLength; ++j) c[i * BlockSideLength + j] += a[i * BlockSideLength + k] * b[k * BlockSideLength + j]; } else { // => a row-major, b col-major #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables for (unsigned_type j = 0; j < BlockSideLength; ++j) for (unsigned_type k = 0; k < BlockSideLength; ++k) c[i * BlockSideLength + j] += a[i * BlockSideLength + k] * b[k + j * BlockSideLength]; } } else { if (! b_in_col_major) { // => a col-major, b row-major #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables for (unsigned_type k = 0; k < BlockSideLength; ++k) for (unsigned_type j = 0; j < BlockSideLength; ++j) c[i * BlockSideLength + j] += a[i + k * BlockSideLength] * b[k * BlockSideLength + j]; } else { // => both col-major #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables for (unsigned_type k = 0; k < BlockSideLength; ++k) for (unsigned_type j = 0; j < BlockSideLength; ++j) c[i * BlockSideLength + j] += a[i + k * BlockSideLength] * b[k + j * BlockSideLength]; } } } }; #if STXXL_BLAS typedef int_type blas_int; typedef std::complex blas_double_complex; typedef std::complex blas_single_complex; // --- vector add (used as matrix-add) ----------------- extern "C" void daxpy_(const blas_int* n, const double* alpha, const double* x, const blas_int* incx, double* y, const blas_int* incy); extern "C" void saxpy_(const blas_int* n, const float* alpha, const float* x, const blas_int* incx, float* y, const blas_int* incy); extern "C" void zaxpy_(const blas_int* n, const blas_double_complex* alpha, const blas_double_complex* x, const blas_int* incx, blas_double_complex* y, const blas_int* incy); extern "C" void caxpy_(const blas_int* n, const blas_single_complex* alpha, const blas_single_complex* x, const blas_int* incx, blas_single_complex* y, const blas_int* incy); extern "C" void dcopy_(const blas_int* n, const double* x, const blas_int* incx, double* y, const blas_int* incy); extern "C" void scopy_(const blas_int* n, const float* x, const blas_int* incx, float* y, const blas_int* incy); extern "C" void zcopy_(const blas_int* n, const blas_double_complex* x, const blas_int* incx, blas_double_complex* y, const blas_int* incy); extern "C" void ccopy_(const blas_int* n, const blas_single_complex* x, const blas_int* incx, blas_single_complex* y, const blas_int* incy); //! c = a + b; for double entries template struct low_level_matrix_binary_ass_op::addition> { low_level_matrix_binary_ass_op(double* c, const double* a, const double* b, typename matrix_operations::addition = typename matrix_operations::addition()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::addition> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::addition> (c, b); } } }; //! c = a - b; for double entries template struct low_level_matrix_binary_ass_op::subtraction> { low_level_matrix_binary_ass_op(double* c, const double* a, const double* b, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::subtraction> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::subtraction> (c, b); } } }; //! c += a; for double entries template struct low_level_matrix_unary_ass_op::addition> { low_level_matrix_unary_ass_op(double* c, const double* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const double one = 1.0; if (a) daxpy_(&size, &one, a, &int_one, c, &int_one); } }; //! c -= a; for double entries template struct low_level_matrix_unary_ass_op::subtraction> { low_level_matrix_unary_ass_op(double* c, const double* a, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const double minusone = -1.0; if (a) daxpy_(&size, &minusone, a, &int_one, c, &int_one); } }; //! c = a; for double entries template struct low_level_matrix_unary_op::addition> { low_level_matrix_unary_op(double* c, const double* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; dcopy_(&size, a, &int_one, c, &int_one); } }; //! c = a + b; for float entries template struct low_level_matrix_binary_ass_op::addition> { low_level_matrix_binary_ass_op(float* c, const float* a, const float* b, typename matrix_operations::addition = typename matrix_operations::addition()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::addition> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::addition> (c, b); } } }; //! c = a - b; for float entries template struct low_level_matrix_binary_ass_op::subtraction> { low_level_matrix_binary_ass_op(float* c, const float* a, const float* b, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::subtraction> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::subtraction> (c, b); } } }; //! c += a; for float entries template struct low_level_matrix_unary_ass_op::addition> { low_level_matrix_unary_ass_op(float* c, const float* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const float one = 1.0; if (a) saxpy_(&size, &one, a, &int_one, c, &int_one); } }; //! c -= a; for float entries template struct low_level_matrix_unary_ass_op::subtraction> { low_level_matrix_unary_ass_op(float* c, const float* a, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const float minusone = -1.0; if (a) saxpy_(&size, &minusone, a, &int_one, c, &int_one); } }; //! c = a; for float entries template struct low_level_matrix_unary_op::addition> { low_level_matrix_unary_op(float* c, const float* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; scopy_(&size, a, &int_one, c, &int_one); } }; //! c = a + b; for blas_double_complex entries template struct low_level_matrix_binary_ass_op::addition> { low_level_matrix_binary_ass_op(blas_double_complex* c, const blas_double_complex* a, const blas_double_complex* b, typename matrix_operations::addition = typename matrix_operations::addition()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::addition> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::addition> (c, b); } } }; //! c = a - b; for blas_double_complex entries template struct low_level_matrix_binary_ass_op::subtraction> { low_level_matrix_binary_ass_op(blas_double_complex* c, const blas_double_complex* a, const blas_double_complex* b, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::subtraction> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::subtraction> (c, b); } } }; //! c += a; for blas_double_complex entries template struct low_level_matrix_unary_ass_op::addition> { low_level_matrix_unary_ass_op(blas_double_complex* c, const blas_double_complex* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const blas_double_complex one = 1.0; if (a) zaxpy_(&size, &one, a, &int_one, c, &int_one); } }; //! c -= a; for blas_double_complex entries template struct low_level_matrix_unary_ass_op::subtraction> { low_level_matrix_unary_ass_op(blas_double_complex* c, const blas_double_complex* a, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const blas_double_complex minusone = -1.0; if (a) zaxpy_(&size, &minusone, a, &int_one, c, &int_one); } }; //! c = a; for blas_double_complex entries template struct low_level_matrix_unary_op::addition> { low_level_matrix_unary_op(blas_double_complex* c, const blas_double_complex* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; zcopy_(&size, a, &int_one, c, &int_one); } }; //! c = a + b; for blas_single_complex entries template struct low_level_matrix_binary_ass_op::addition> { low_level_matrix_binary_ass_op(blas_single_complex* c, const blas_single_complex* a, const blas_single_complex* b, typename matrix_operations::addition = typename matrix_operations::addition()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::addition> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::addition> (c, b); } } }; //! c = a - b; for blas_single_complex entries template struct low_level_matrix_binary_ass_op::subtraction> { low_level_matrix_binary_ass_op(blas_single_complex* c, const blas_single_complex* a, const blas_single_complex* b, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { if (a) if (b) { low_level_matrix_unary_op::addition> (c, a); low_level_matrix_unary_ass_op::subtraction> (c, b); } else low_level_matrix_unary_op::addition> (c, a); else { assert(b /* do not add nothing to nothing */); low_level_matrix_unary_op::subtraction> (c, b); } } }; //! c += a; for blas_single_complex entries template struct low_level_matrix_unary_ass_op::addition> { low_level_matrix_unary_ass_op(blas_single_complex* c, const blas_single_complex* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const blas_single_complex one = 1.0; if (a) caxpy_(&size, &one, a, &int_one, c, &int_one); } }; //! c -= a; for blas_single_complex entries template struct low_level_matrix_unary_ass_op::subtraction> { low_level_matrix_unary_ass_op(blas_single_complex* c, const blas_single_complex* a, typename matrix_operations::subtraction = typename matrix_operations::subtraction()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; const blas_single_complex minusone = -1.0; if (a) caxpy_(&size, &minusone, a, &int_one, c, &int_one); } }; //! c = a; for blas_single_complex entries template struct low_level_matrix_unary_op::addition> { low_level_matrix_unary_op(blas_single_complex* c, const blas_single_complex* a, typename matrix_operations::addition = typename matrix_operations::addition()) { const blas_int size = BlockSideLength * BlockSideLength; const blas_int int_one = 1; ccopy_(&size, a, &int_one, c, &int_one); } }; // --- matrix-matrix multiplication --------------- extern "C" void dgemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const double* alpha, const double* a, const blas_int* lda, const double* b, const blas_int* ldb, const double* beta, double* c, const blas_int* ldc); extern "C" void sgemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const float* alpha, const float* a, const blas_int* lda, const float* b, const blas_int* ldb, const float* beta, float* c, const blas_int* ldc); extern "C" void zgemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const blas_double_complex* alpha, const blas_double_complex* a, const blas_int* lda, const blas_double_complex* b, const blas_int* ldb, const blas_double_complex* beta, blas_double_complex* c, const blas_int* ldc); extern "C" void cgemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const blas_single_complex* alpha, const blas_single_complex* a, const blas_int* lda, const blas_single_complex* b, const blas_int* ldb, const blas_single_complex* beta, blas_single_complex* c, const blas_int* ldc); template void gemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const ValueType* alpha, const ValueType* a, const blas_int* lda, const ValueType* b, const blas_int* ldb, const ValueType* beta, ValueType* c, const blas_int* ldc); //! calculates c = alpha * a * b + beta * c //! \tparam ValueType type of elements //! \param n height of a and c //! \param l width of a and height of b //! \param m width of b and c //! \param a_in_col_major if a is stored in column-major rather than row-major //! \param b_in_col_major if b is stored in column-major rather than row-major //! \param c_in_col_major if c is stored in column-major rather than row-major template void gemm_wrapper(const blas_int n, const blas_int l, const blas_int m, const ValueType alpha, const bool a_in_col_major, const ValueType* a, const bool b_in_col_major, const ValueType* b, const ValueType beta, const bool c_in_col_major, ValueType* c) { const blas_int& stride_in_a = a_in_col_major ? n : l; const blas_int& stride_in_b = b_in_col_major ? l : m; const blas_int& stride_in_c = c_in_col_major ? n : m; const char transa = a_in_col_major xor c_in_col_major ? 'T' : 'N'; const char transb = b_in_col_major xor c_in_col_major ? 'T' : 'N'; if (c_in_col_major) // blas expects matrices in column-major unless specified via transa rsp. transb gemm_(&transa, &transb, &n, &m, &l, &alpha, a, &stride_in_a, b, &stride_in_b, &beta, c, &stride_in_c); else // blas expects matrices in column-major, so we calculate c^T = alpha * b^T * a^T + beta * c^T gemm_(&transb, &transa, &m, &n, &l, &alpha, b, &stride_in_b, a, &stride_in_a, &beta, c, &stride_in_c); } template <> void gemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const double* alpha, const double* a, const blas_int* lda, const double* b, const blas_int* ldb, const double* beta, double* c, const blas_int* ldc) { dgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); } template <> void gemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const float* alpha, const float* a, const blas_int* lda, const float* b, const blas_int* ldb, const float* beta, float* c, const blas_int* ldc) { sgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); } template <> void gemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const blas_double_complex* alpha, const blas_double_complex* a, const blas_int* lda, const blas_double_complex* b, const blas_int* ldb, const blas_double_complex* beta, blas_double_complex* c, const blas_int* ldc) { zgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); } template <> void gemm_(const char* transa, const char* transb, const blas_int* m, const blas_int* n, const blas_int* k, const blas_single_complex* alpha, const blas_single_complex* a, const blas_int* lda, const blas_single_complex* b, const blas_int* ldb, const blas_single_complex* beta, blas_single_complex* c, const blas_int* ldc) { cgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); } //! multiplies matrices A and B, adds result to C, for double entries template struct low_level_matrix_multiply_and_add { low_level_matrix_multiply_and_add(const double* a, bool a_in_col_major, const double* b, bool b_in_col_major, double* c, const bool c_in_col_major) { gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, 1.0, a_in_col_major, a, /**/ b_in_col_major, b, 1.0, c_in_col_major, c); } }; //! multiplies matrices A and B, adds result to C, for float entries template struct low_level_matrix_multiply_and_add { low_level_matrix_multiply_and_add(const float* a, bool a_in_col_major, const float* b, bool b_in_col_major, float* c, const bool c_in_col_major) { gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, 1.0, a_in_col_major, a, /**/ b_in_col_major, b, 1.0, c_in_col_major, c); } }; //! multiplies matrices A and B, adds result to C, for complex entries template struct low_level_matrix_multiply_and_add { low_level_matrix_multiply_and_add(const blas_single_complex* a, bool a_in_col_major, const blas_single_complex* b, bool b_in_col_major, blas_single_complex* c, const bool c_in_col_major) { gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, 1.0, a_in_col_major, a, /**/ b_in_col_major, b, 1.0, c_in_col_major, c); } }; //! multiplies matrices A and B, adds result to C, for complex entries template struct low_level_matrix_multiply_and_add { low_level_matrix_multiply_and_add(const blas_double_complex* a, bool a_in_col_major, const blas_double_complex* b, bool b_in_col_major, blas_double_complex* c, const bool c_in_col_major) { gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, 1.0, a_in_col_major, a, /**/ b_in_col_major, b, 1.0, c_in_col_major, c); } }; #endif } // namespace matrix_local //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_MATRIX_LOW_LEVEL_HEADER stxxl-1.4.1/include/stxxl/bits/containers/pq_ext_merger.h000644 001411 000144 00000115054 12414452316 023432 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/pq_ext_merger.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 1999 Peter Sanders * Copyright (C) 2003, 2004, 2007 Roman Dementiev * Copyright (C) 2007-2009 Johannes Singler * Copyright (C) 2007-2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER #define STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER #include STXXL_BEGIN_NAMESPACE //! \addtogroup stlcontinternals //! //! \{ /*! \internal */ namespace priority_queue_local { template class short_sequence : public std::pair { typedef std::pair pair; public: typedef Iterator iterator; typedef const iterator const_iterator; typedef typename std::iterator_traits::value_type value_type; typedef typename std::iterator_traits::difference_type size_type; typedef value_type& reference; typedef const value_type& const_reference; typedef unsigned_type origin_type; private: origin_type m_origin; public: short_sequence(Iterator first, Iterator last, origin_type origin) : pair(first, last), m_origin(origin) { } iterator begin() { return this->first; } const_iterator begin() const { return this->first; } const_iterator cbegin() const { return begin(); } iterator end() { return this->second; } const_iterator end() const { return this->second; } const_iterator cend() const { return end(); } reference front() { return *begin(); } const_reference front() const { return *begin(); } reference back() { return *(end() - 1); } const_reference back() const { return *(end() - 1); } size_type size() const { return end() - begin(); } bool empty() const { return size() == 0; } origin_type origin() const { return m_origin; } }; /*! * External merger, based on the loser tree data structure. * \param Arity_ maximum arity of merger, does not need to be a power of 2 */ template class ext_merger : private noncopyable { public: typedef stxxl::uint64 size_type; typedef BlockType block_type; typedef typename block_type::bid_type bid_type; typedef typename block_type::value_type value_type; typedef Cmp comparator_type; typedef AllocStr alloc_strategy; typedef read_write_pool pool_type; // arity_bound / 2 < arity <= arity_bound enum { arity = Arity, arity_bound = 1UL << (LOG2::ceil) }; protected: comparator_type cmp; bool is_sentinel(const value_type& a) const { return !(cmp(cmp.min_value(), a)); // a <= cmp.min_value() } bool not_sentinel(const value_type& a) const { return cmp(cmp.min_value(), a); // a > cmp.min_value() } struct sequence_state : private noncopyable { block_type* block; //current block unsigned_type current; //current index in current block std::list* bids; //list of blocks forming this sequence comparator_type cmp; ext_merger* merger; bool allocated; //! \returns current element const value_type& operator * () const { return (*block)[current]; } sequence_state() : block(NULL), current(0), bids(NULL), merger(NULL), allocated(false) { } ~sequence_state() { STXXL_VERBOSE2("ext_merger sequence_state::~sequence_state()"); if (bids != NULL) { block_manager* bm = block_manager::get_instance(); bm->delete_blocks(bids->begin(), bids->end()); delete bids; } } void make_inf() { current = 0; (*block)[0] = cmp.min_value(); } bool is_sentinel(const value_type& a) const { return !(cmp(cmp.min_value(), a)); } bool not_sentinel(const value_type& a) const { return cmp(cmp.min_value(), a); } void swap(sequence_state& obj) { if (&obj != this) { std::swap(current, obj.current); std::swap(block, obj.block); std::swap(bids, obj.bids); assert(merger == obj.merger); std::swap(allocated, obj.allocated); } } sequence_state& operator ++ () { assert(not_sentinel((*block)[current])); assert(current < block->size); ++current; if (current == block->size) { STXXL_VERBOSE2("ext_merger sequence_state operator++ crossing block border "); // go to the next block assert(bids != NULL); if (bids->empty()) // if there is no next block { STXXL_VERBOSE2("ext_merger sequence_state operator++ it was the last block in the sequence "); delete bids; bids = NULL; make_inf(); } else { STXXL_VERBOSE2("ext_merger sequence_state operator++ there is another block "); bid_type bid = bids->front(); bids->pop_front(); merger->pool->hint(bid); if (!(bids->empty())) { STXXL_VERBOSE2("ext_merger sequence_state operator++ more blocks exist in a sequence, hinting the next"); merger->pool->hint(bids->front()); } merger->pool->read(block, bid)->wait(); STXXL_VERBOSE2("first element of read block " << bid << " " << *(block->begin()) << " cached in " << block); if (!(bids->empty())) merger->pool->hint(bids->front()); // re-hint, reading might have made a block free block_manager::get_instance()->delete_block(bid); current = 0; } } return *this; } }; #if STXXL_PQ_EXTERNAL_LOSER_TREE struct Entry { value_type key; // key of loser element (winner for 0) unsigned_type index; // the number of the losing segment }; #endif //STXXL_PQ_EXTERNAL_LOSER_TREE size_type size_; // total number of elements stored unsigned_type log_k; // log of current tree size unsigned_type k; // invariant (k == 1 << log_k), always a power of 2 // only entries 0 .. arity-1 may hold actual sequences, the other // entries arity .. arity_bound-1 are sentinels to make the size of the tree // a power of 2 always // stack of empty segment indices internal_bounded_stack free_segments; #if STXXL_PQ_EXTERNAL_LOSER_TREE // upper levels of loser trees // entry[0] contains the winner info Entry entry[arity_bound]; #endif //STXXL_PQ_EXTERNAL_LOSER_TREE // leaf information // note that Knuth uses indices k..k-1 // while we use 0..k-1 sequence_state states[arity_bound]; // sequence including current position, dereference gives current element pool_type* pool; block_type* sentinel_block; public: ext_merger() : size_(0), log_k(0), k(1), pool(0) { init(); } ext_merger(pool_type* pool_) : size_(0), log_k(0), k(1), pool(pool_) { init(); } virtual ~ext_merger() { STXXL_VERBOSE1("ext_merger::~ext_merger()"); for (unsigned_type i = 0; i < arity; ++i) { delete states[i].block; } delete sentinel_block; } void set_pool(pool_type* pool_) { pool = pool_; } private: void init() { STXXL_VERBOSE2("ext_merger::init()"); assert(!cmp(cmp.min_value(), cmp.min_value())); // verify strict weak ordering sentinel_block = NULL; if (arity < arity_bound) { sentinel_block = new block_type; for (unsigned_type i = 0; i < block_type::size; ++i) (*sentinel_block)[i] = cmp.min_value(); if (arity + 1 == arity_bound) { // same memory consumption, but smaller merge width, better use arity = arity_bound STXXL_ERRMSG("inefficient PQ parameters for ext_merger: arity + 1 == arity_bound"); } } for (unsigned_type i = 0; i < arity_bound; ++i) { states[i].merger = this; if (i < arity) states[i].block = new block_type; else states[i].block = sentinel_block; states[i].make_inf(); } assert(k == 1); free_segments.push(0); //total state: one free sequence rebuild_loser_tree(); #if STXXL_PQ_EXTERNAL_LOSER_TREE assert(is_sentinel(*states[entry[0].index])); #endif //STXXL_PQ_EXTERNAL_LOSER_TREE } // rebuild loser tree information from the values in current void rebuild_loser_tree() { #if STXXL_PQ_EXTERNAL_LOSER_TREE unsigned_type winner = init_winner(1); entry[0].index = winner; entry[0].key = *(states[winner]); #endif //STXXL_PQ_EXTERNAL_LOSER_TREE } #if STXXL_PQ_EXTERNAL_LOSER_TREE // given any values in the leaves this // routing recomputes upper levels of the tree // from scratch in linear time // initialize entry[root].index and the subtree rooted there // return winner index unsigned_type init_winner(unsigned_type root) { if (root >= k || root >= arity_bound) { // leaf reached return root - k; } else { unsigned_type left = init_winner(2 * root); unsigned_type right = init_winner(2 * root + 1); value_type lk = *(states[left]); value_type rk = *(states[right]); assert(root < arity_bound); if (!(cmp(lk, rk))) { // right subtree looses entry[root].index = right; entry[root].key = rk; return left; } else { entry[root].index = left; entry[root].key = lk; return right; } } } // first go up the tree all the way to the root // hand down old winner for the respective subtree // based on new value, and old winner and loser // update each node on the path to the root top down. // This is implemented recursively void update_on_insert( unsigned_type node, const value_type& new_key, unsigned_type new_index, value_type* winner_key, unsigned_type* winner_index, // old winner unsigned_type* mask) // 1 << (ceil(log KNK) - dist-from-root) { if (node == 0) { // winner part of root *mask = unsigned_type(1) << (log_k - 1); *winner_key = entry[0].key; *winner_index = entry[0].index; if (cmp(entry[node].key, new_key)) { entry[node].key = new_key; entry[node].index = new_index; } } else { update_on_insert(node >> 1, new_key, new_index, winner_key, winner_index, mask); value_type loserKey = entry[node].key; unsigned_type loserIndex = entry[node].index; if ((*winner_index & *mask) != (new_index & *mask)) { // different subtrees if (cmp(loserKey, new_key)) { // new_key will have influence here if (cmp(*winner_key, new_key)) { // old winner loses here entry[node].key = *winner_key; entry[node].index = *winner_index; } else { // new entry looses here entry[node].key = new_key; entry[node].index = new_index; } } *winner_key = loserKey; *winner_index = loserIndex; } // note that nothing needs to be done if // the winner came from the same subtree // a) new_key <= winner_key => even more reason for the other tree to lose // b) new_key > winner_key => the old winner will beat the new // entry further down the tree // also the same old winner is handed down the tree *mask >>= 1; // next level } } #endif //STXXL_PQ_EXTERNAL_LOSER_TREE // make the tree two times as wide void double_k() { STXXL_VERBOSE1("ext_merger::double_k (before) k=" << k << " log_k=" << log_k << " arity_bound=" << arity_bound << " arity=" << arity << " #free=" << free_segments.size()); assert(k > 0); assert(k < arity); assert(free_segments.empty()); // stack was free (probably not needed) // make all new entries free // and push them on the free stack for (unsigned_type i = 2 * k - 1; i >= k; i--) //backwards { states[i].make_inf(); if (i < arity) free_segments.push(i); } // double the size k *= 2; log_k++; STXXL_VERBOSE1("ext_merger::double_k (after) k=" << k << " log_k=" << log_k << " arity_bound=" << arity_bound << " arity=" << arity << " #free=" << free_segments.size()); assert(!free_segments.empty()); assert(k <= arity_bound); // recompute loser tree information rebuild_loser_tree(); } // compact nonempty segments in the left half of the tree void compact_tree() { STXXL_VERBOSE1("ext_merger::compact_tree (before) k=" << k << " log_k=" << log_k << " #free=" << free_segments.size()); assert(log_k > 0); // compact all nonempty segments to the left unsigned_type target = 0; for (unsigned_type from = 0; from < k; from++) { if (!is_segment_empty(from)) { assert(is_segment_allocated(from)); if (from != target) { assert(!is_segment_allocated(target)); states[target].swap(states[from]); } ++target; } } // half degree as often as possible while (k > 1 && target <= (k / 2)) { k /= 2; log_k--; } // overwrite garbage and compact the stack of free segment indices free_segments.clear(); // none free for ( ; target < k; target++) { assert(!is_segment_allocated(target)); states[target].make_inf(); if (target < arity) free_segments.push(target); } STXXL_VERBOSE1("ext_merger::compact_tree (after) k=" << k << " log_k=" << log_k << " #free=" << free_segments.size()); assert(k > 0); // recompute loser tree information rebuild_loser_tree(); } #if 0 void swap(ext_merger& obj) { std::swap(cmp, obj.cmp); std::swap(free_segments, obj.free_segments); std::swap(size_, obj.size_); std::swap(log_k, obj.log_k); std::swap(k, obj.k); swap_1D_arrays(entry, obj.entry, arity_bound); swap_1D_arrays(states, obj.states, arity_bound); // std::swap(pool,obj.pool); } #endif public: unsigned_type mem_cons() const // only rough estimation { return (STXXL_MIN(arity + 1, arity_bound) * block_type::raw_size); } // delete the (length = end-begin) smallest elements and write them to [begin..end) // empty segments are deallocated // requires: // - there are at least length elements // - segments are ended by sentinels template void multi_merge(OutputIterator begin, OutputIterator end) { int_type length = end - begin; STXXL_VERBOSE1("ext_merger::multi_merge from " << k << " sequence(s)," " length = " << length); if (length == 0) return; assert(k > 0); assert(length <= (int_type)size_); //This is the place to make statistics about external multi_merge calls. #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL typedef stxxl::int64 diff_type; typedef std::pair sequence; std::vector seqs; std::vector orig_seq_index; Cmp cmp; priority_queue_local::invert_order inv_cmp(cmp); for (unsigned_type i = 0; i < k; ++i) //initialize sequences { if (states[i].current == states[i].block->size || is_sentinel(*states[i])) continue; seqs.push_back(std::make_pair(states[i].block->begin() + states[i].current, states[i].block->end())); orig_seq_index.push_back(i); #if STXXL_CHECK_ORDER_IN_SORTS if (!is_sentinel(*seqs.back().first) && !stxxl::is_sorted(seqs.back().first, seqs.back().second, inv_cmp)) { STXXL_VERBOSE1("length " << i << " " << (seqs.back().second - seqs.back().first)); for (value_type* v = seqs.back().first + 1; v < seqs.back().second; ++v) { if (inv_cmp(*v, *(v - 1))) { STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs.back().first - 1) << "/" << (v - seqs.back().first) << " " << *(v - 1) << " " << *v); } if (is_sentinel(*v)) { STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs.back().first)); } } assert(false); } #endif //Hint first non-internal (actually second) block of this sequence. if (states[i].bids != NULL && !states[i].bids->empty()) pool->hint(states[i].bids->front()); } assert(seqs.size() > 0); #if STXXL_CHECK_ORDER_IN_SORTS value_type last_elem; #endif diff_type rest = length; //elements still to merge for this output block while (rest > 0) { value_type min_last = cmp.min_value(); // minimum of the sequences' last elements diff_type total_size = 0; for (unsigned_type i = 0; i < seqs.size(); ++i) { diff_type seq_i_size = seqs[i].second - seqs[i].first; if (seq_i_size > 0) { total_size += seq_i_size; if (inv_cmp(*(seqs[i].second - 1), min_last)) min_last = *(seqs[i].second - 1); STXXL_VERBOSE1("front block of seq " << i << ": front=" << *(seqs[i].first) << " back=" << *(seqs[i].second - 1) << " len=" << seq_i_size); } else { STXXL_VERBOSE1("front block of seq " << i << ": empty"); } } assert(total_size > 0); assert(!is_sentinel(min_last)); STXXL_VERBOSE1("min_last " << min_last << " total size " << total_size << " num_seq " << seqs.size()); diff_type less_equal_than_min_last = 0; //locate this element in all sequences for (unsigned_type i = 0; i < seqs.size(); ++i) { //assert(seqs[i].first < seqs[i].second); typename block_type::iterator position = std::upper_bound(seqs[i].first, seqs[i].second, min_last, inv_cmp); //no element larger than min_last is merged STXXL_VERBOSE1("seq " << i << ": " << (position - seqs[i].first) << " greater equal than " << min_last); less_equal_than_min_last += (position - seqs[i].first); } diff_type output_size = STXXL_MIN(less_equal_than_min_last, rest); // at most rest elements STXXL_VERBOSE1("output_size=" << output_size << " = min(leq_t_ml=" << less_equal_than_min_last << ", rest=" << rest << ")"); assert(output_size > 0); //main call begin = parallel::multiway_merge(seqs.begin(), seqs.end(), begin, inv_cmp, output_size); //sequence iterators are progressed appropriately rest -= output_size; size_ -= output_size; for (unsigned_type i = 0; i < seqs.size(); ++i) { sequence_state& state = states[orig_seq_index[i]]; state.current = seqs[i].first - state.block->begin(); assert(seqs[i].first <= seqs[i].second); if (seqs[i].first == seqs[i].second) //has run empty { assert(state.current == state.block->size); if (state.bids == NULL || state.bids->empty()) // if there is no next block { STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) it was the last block in the sequence "); state.make_inf(); } else { #if STXXL_CHECK_ORDER_IN_SORTS last_elem = *(seqs[i].second - 1); #endif STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) there is another block "); bid_type bid = state.bids->front(); state.bids->pop_front(); pool->hint(bid); if (!(state.bids->empty())) { STXXL_VERBOSE2("seq " << i << ": ext_merger::multi_merge(...) more blocks exist, hinting the next"); pool->hint(state.bids->front()); } pool->read(state.block, bid)->wait(); STXXL_VERBOSE1("seq " << i << ": first element of read block " << bid << " " << *(state.block->begin()) << " cached in " << state.block); if (!(state.bids->empty())) pool->hint(state.bids->front()); // re-hint, reading might have made a block free state.current = 0; seqs[i] = std::make_pair(state.block->begin() + state.current, state.block->end()); block_manager::get_instance()->delete_block(bid); #if STXXL_CHECK_ORDER_IN_SORTS STXXL_VERBOSE1("before " << last_elem << " after " << *seqs[i].first << " newly loaded block " << bid); if (!stxxl::is_sorted(seqs[i].first, seqs[i].second, inv_cmp)) { STXXL_VERBOSE1("length " << i << " " << (seqs[i].second - seqs[i].first)); for (value_type* v = seqs[i].first + 1; v < seqs[i].second; ++v) { if (inv_cmp(*v, *(v - 1))) { STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs[i].first - 1) << "/" << (v - seqs[i].first) << " " << *(v - 1) << " " << *v); } if (is_sentinel(*v)) { STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs[i].first)); } } assert(false); } #endif } } } } //while (rest > 1) for (unsigned_type i = 0; i < seqs.size(); ++i) { unsigned_type seg = orig_seq_index[i]; if (is_segment_empty(seg)) { STXXL_VERBOSE1("deallocated " << seg); deallocate_segment(seg); } } #else // STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL //Hint first non-internal (actually second) block of each sequence. for (unsigned_type i = 0; i < k; ++i) { if (states[i].bids != NULL && !states[i].bids->empty()) pool->hint(states[i].bids->front()); } switch (log_k) { case 0: assert(k == 1); assert(entry[0].index == 0); assert(free_segments.empty()); //memcpy(target, states[0], length * sizeof(value_type)); //std::copy(states[0],states[0]+length,target); for (int_type i = 0; i < length; ++i, ++(states[0]), ++begin) *begin = *(states[0]); entry[0].key = **states; if (is_segment_empty(0)) deallocate_segment(0); break; case 1: assert(k == 2); merge_iterator(states[0], states[1], begin, length, cmp); rebuild_loser_tree(); if (is_segment_empty(0) && is_segment_allocated(0)) deallocate_segment(0); if (is_segment_empty(1) && is_segment_allocated(1)) deallocate_segment(1); break; case 2: assert(k == 4); if (is_segment_empty(3)) merge3_iterator(states[0], states[1], states[2], begin, length, cmp); else merge4_iterator(states[0], states[1], states[2], states[3], begin, length, cmp); rebuild_loser_tree(); if (is_segment_empty(0) && is_segment_allocated(0)) deallocate_segment(0); if (is_segment_empty(1) && is_segment_allocated(1)) deallocate_segment(1); if (is_segment_empty(2) && is_segment_allocated(2)) deallocate_segment(2); if (is_segment_empty(3) && is_segment_allocated(3)) deallocate_segment(3); break; case 3: multi_merge_f(begin, end); break; case 4: multi_merge_f(begin, end); break; case 5: multi_merge_f(begin, end); break; case 6: multi_merge_f(begin, end); break; case 7: multi_merge_f(begin, end); break; case 8: multi_merge_f(begin, end); break; case 9: multi_merge_f(begin, end); break; case 10: multi_merge_f(begin, end); break; default: multi_merge_k(begin, end); break; } size_ -= length; #endif // compact tree if it got considerably smaller { const unsigned_type num_segments_used = std::min(arity, k) - free_segments.size(); const unsigned_type num_segments_trigger = k - (3 * k / 5); // using k/2 would be worst case inefficient (for large k) // for k \in {2, 4, 8} the trigger is k/2 which is good // because we have special mergers for k \in {1, 2, 4} // there is also a special 3-way-merger, that will be // triggered if k == 4 && is_segment_empty(3) STXXL_VERBOSE3("ext_merger compact? k=" << k << " #used=" << num_segments_used << " <= #trigger=" << num_segments_trigger << " ==> " << ((k > 1 && num_segments_used <= num_segments_trigger) ? "yes" : "no ") << " || " << ((k == 4 && !free_segments.empty() && !is_segment_empty(3)) ? "yes" : "no ") << " #free=" << free_segments.size()); if (k > 1 && ((num_segments_used <= num_segments_trigger) || (k == 4 && !free_segments.empty() && !is_segment_empty(3)))) { compact_tree(); } } } private: #if STXXL_PQ_EXTERNAL_LOSER_TREE // multi-merge for arbitrary K template void multi_merge_k(OutputIterator begin, OutputIterator end) { Entry* current_pos; value_type current_key; unsigned_type current_index; // leaf pointed to by current entry unsigned_type kReg = k; OutputIterator done = end; OutputIterator target = begin; unsigned_type winner_index = entry[0].index; value_type winner_key = entry[0].key; while (target != done) { // write result *target = *(states[winner_index]); // advance winner segment ++(states[winner_index]); winner_key = *(states[winner_index]); // remove winner segment if empty now if (is_sentinel(winner_key)) // deallocate_segment(winner_index); // go up the entry-tree for (unsigned_type i = (winner_index + kReg) >> 1; i > 0; i >>= 1) { current_pos = entry + i; current_key = current_pos->key; if (cmp(winner_key, current_key)) { current_index = current_pos->index; current_pos->key = winner_key; current_pos->index = winner_index; winner_key = current_key; winner_index = current_index; } } ++target; } entry[0].index = winner_index; entry[0].key = winner_key; } template void multi_merge_f(OutputIterator begin, OutputIterator end) { OutputIterator done = end; OutputIterator target = begin; unsigned_type winner_index = entry[0].index; Entry* reg_entry = entry; sequence_state* reg_states = states; value_type winner_key = entry[0].key; assert(log_k >= LogK); while (target != done) { // write result *target = *(reg_states[winner_index]); // advance winner segment ++(reg_states[winner_index]); winner_key = *(reg_states[winner_index]); // remove winner segment if empty now if (is_sentinel(winner_key)) deallocate_segment(winner_index); ++target; // update loser tree #define TreeStep(L) \ if (1 << LogK >= 1 << L) { \ Entry* pos ## L = reg_entry + ((winner_index + (1 << LogK)) >> (((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0)); \ value_type key ## L = pos ## L->key; \ if (cmp(winner_key, key ## L)) { \ unsigned_type index ## L = pos ## L->index; \ pos ## L->key = winner_key; \ pos ## L->index = winner_index; \ winner_key = key ## L; \ winner_index = index ## L; \ } \ } TreeStep(10); TreeStep(9); TreeStep(8); TreeStep(7); TreeStep(6); TreeStep(5); TreeStep(4); TreeStep(3); TreeStep(2); TreeStep(1); #undef TreeStep } reg_entry[0].index = winner_index; reg_entry[0].key = winner_key; } #endif //STXXL_PQ_EXTERNAL_LOSER_TREE public: bool is_space_available() const // for new segment { return k < arity || !free_segments.empty(); } // insert segment beginning at target // require: is_space_available() == 1 template void insert_segment(Merger& another_merger, size_type segment_size) { STXXL_VERBOSE1("ext_merger::insert_segment(merger,...)" << this); if (segment_size > 0) { // get a free slot if (free_segments.empty()) { // tree is too small double_k(); } assert(!free_segments.empty()); unsigned_type free_slot = free_segments.top(); free_segments.pop(); // link new segment assert(segment_size); unsigned_type nblocks = (unsigned_type)(segment_size / block_type::size); //assert(nblocks); // at least one block STXXL_VERBOSE1("ext_merger::insert_segment nblocks=" << nblocks); if (nblocks == 0) { STXXL_VERBOSE1("ext_merger::insert_segment(merger,...) WARNING: inserting a segment with " << nblocks << " blocks"); STXXL_VERBOSE1("THIS IS INEFFICIENT: TRY TO CHANGE PRIORITY QUEUE PARAMETERS"); } unsigned_type first_size = (unsigned_type)(segment_size % block_type::size); if (first_size == 0) { first_size = block_type::size; --nblocks; } block_manager* bm = block_manager::get_instance(); std::list* bids = new std::list(nblocks); bm->new_blocks(alloc_strategy(), bids->begin(), bids->end()); block_type* first_block = new block_type; another_merger.multi_merge( first_block->begin() + (block_type::size - first_size), first_block->end()); STXXL_VERBOSE1("last element of first block " << *(first_block->end() - 1)); assert(!cmp(*(first_block->begin() + (block_type::size - first_size)), *(first_block->end() - 1))); assert(pool->size_write() > 0); for (typename std::list::iterator curbid = bids->begin(); curbid != bids->end(); ++curbid) { block_type* b = pool->steal(); another_merger.multi_merge(b->begin(), b->end()); STXXL_VERBOSE1("first element of following block " << *curbid << " " << *(b->begin())); STXXL_VERBOSE1("last element of following block " << *curbid << " " << *(b->end() - 1)); assert(!cmp(*(b->begin()), *(b->end() - 1))); pool->write(b, *curbid); STXXL_VERBOSE1("written to block " << *curbid << " cached in " << b); } insert_segment(bids, first_block, first_size, free_slot); size_ += segment_size; #if STXXL_PQ_EXTERNAL_LOSER_TREE // propagate new information up the tree value_type dummyKey; unsigned_type dummyIndex; unsigned_type dummyMask; update_on_insert((free_slot + k) >> 1, *(states[free_slot]), free_slot, &dummyKey, &dummyIndex, &dummyMask); #endif //STXXL_PQ_EXTERNAL_LOSER_TREE } else { // deallocate memory ? STXXL_VERBOSE1("Merged segment with zero size."); } } size_type size() const { return size_; } protected: /*! \param bidlist list of blocks to insert \param first_block the first block of the sequence, before bidlist \param first_size number of elements in the first block \param slot slot to insert into */ void insert_segment(std::list* bidlist, block_type* first_block, unsigned_type first_size, unsigned_type slot) { STXXL_VERBOSE1("ext_merger::insert_segment(bidlist,...) " << this << " " << bidlist->size() << " " << slot); assert(!is_segment_allocated(slot)); assert(first_size > 0); sequence_state& new_sequence = states[slot]; new_sequence.current = block_type::size - first_size; std::swap(new_sequence.block, first_block); delete first_block; std::swap(new_sequence.bids, bidlist); if (bidlist) // the old list { assert(bidlist->empty()); delete bidlist; } new_sequence.allocated = true; assert(is_segment_allocated(slot)); } // free an empty segment . void deallocate_segment(unsigned_type slot) { STXXL_VERBOSE1("ext_merger::deallocate_segment() deleting segment " << slot << " allocated=" << int(is_segment_allocated(slot))); assert(is_segment_allocated(slot)); states[slot].allocated = false; states[slot].make_inf(); // push on the stack of free segment indices free_segments.push(slot); } // is this segment empty ? bool is_segment_empty(unsigned_type slot) const { return is_sentinel(*(states[slot])); } // Is this segment allocated? Otherwise it's empty, // already on the stack of free segment indices and can be reused. bool is_segment_allocated(unsigned_type slot) const { return states[slot].allocated; } }; // class ext_merger } // namespace priority_queue_local //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/stack.h000644 001411 000144 00000101377 12411366426 021704 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/stack.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003-2004 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_STACK_HEADER #define STXXL_CONTAINERS_STACK_HEADER #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \defgroup stlcont_stack stack //! \ingroup stlcont //! External stack implementations //! \{ template struct stack_config_generator { typedef ValueType value_type; enum { blocks_per_page = BlocksPerPage }; typedef AllocStr alloc_strategy; enum { block_size = BlockSize }; typedef SizeType size_type; }; //! External stack container. //! Introduction to stack container: see \ref tutorial_stack tutorial. \n //! Design and Internals of stack container: see \ref design_stack //! Conservative implementation. Fits best if your access pattern consists of irregularly mixed //! push'es and pop's. //! For semantics of the methods see documentation of the STL \c std::stack.
    //! To gain full bandwidth of disks \c StackConfig::BlocksPerPage must >= number of disks
    //! \internal template class normal_stack : private noncopyable { public: typedef StackConfig cfg; //! type of the elements stored in the stack typedef typename cfg::value_type value_type; typedef typename cfg::alloc_strategy alloc_strategy_type; //! type for sizes (64-bit) typedef typename cfg::size_type size_type; enum { blocks_per_page = cfg::blocks_per_page, block_size = cfg::block_size }; //! type of block used in disk-memory transfers typedef typed_block block_type; typedef BID bid_type; private: size_type m_size; unsigned_type cache_offset; value_type* current_element; simple_vector cache; typename simple_vector::iterator front_page; typename simple_vector::iterator back_page; std::vector bids; alloc_strategy_type alloc_strategy; public: //! \name Constructors/Destructors //! \{ //! Default constructor: creates empty stack. normal_stack() : m_size(0), cache_offset(0), current_element(NULL), cache(blocks_per_page * 2), front_page(cache.begin() + blocks_per_page), back_page(cache.begin()), bids(0) { bids.reserve(blocks_per_page); } //! \name Accessor Functions //! \{ void swap(normal_stack& obj) { std::swap(m_size, obj.m_size); std::swap(cache_offset, obj.cache_offset); std::swap(current_element, obj.current_element); std::swap(cache, obj.cache); std::swap(front_page, obj.front_page); std::swap(back_page, obj.back_page); std::swap(bids, obj.bids); std::swap(alloc_strategy, obj.alloc_strategy); } //! \} //! \name Constructors/Destructors //! \{ //! Copy-construction from a another stack of any type. //! \param stack_ stack object (could be external or internal, important is that it must //! have a copy constructor, \c top() and \c pop() methods ) template normal_stack(const StackType& stack_) : m_size(0), cache_offset(0), current_element(NULL), cache(blocks_per_page * 2), front_page(cache.begin() + blocks_per_page), back_page(cache.begin()), bids(0) { bids.reserve(blocks_per_page); StackType stack_copy = stack_; size_t sz = stack_copy.size(); std::vector tmp(sz); for (size_t i = 0; i < sz; ++i) { tmp[sz - i - 1] = stack_copy.top(); stack_copy.pop(); } for (size_t i = 0; i < sz; ++i) push(tmp[i]); } virtual ~normal_stack() { STXXL_VERBOSE(STXXL_PRETTY_FUNCTION_NAME); block_manager::get_instance()->delete_blocks(bids.begin(), bids.end()); } //! \} //! \name Capacity //! \{ //! Returns the number of elements contained in the stack size_type size() const { return m_size; } //! Returns true if the stack is empty. bool empty() const { return (!m_size); } //! \} //! \name Accessor Functions //! \{ //! Return mutable reference to the element at the top of the //! stack. Precondition: stack is not empty(). value_type & top() { assert(m_size > 0); return (*current_element); } //! Return constant reference to the element at the top of the //! stack. Precondition: stack is not empty(). const value_type & top() const { assert(m_size > 0); return (*current_element); } //! Inserts an element at the top of the stack. Postconditions: size() is //! incremented by 1, and top() is the inserted element. void push(const value_type& val) { assert(cache_offset <= 2 * blocks_per_page * block_type::size); //assert(cache_offset >= 0); if (UNLIKELY(cache_offset == 2 * blocks_per_page * block_type::size)) // cache overflow { STXXL_VERBOSE2("growing, size: " << m_size); bids.resize(bids.size() + blocks_per_page); typename std::vector::iterator cur_bid = bids.end() - blocks_per_page; block_manager::get_instance()->new_blocks(alloc_strategy, cur_bid, bids.end(), cur_bid - bids.begin()); simple_vector requests(blocks_per_page); for (int i = 0; i < blocks_per_page; ++i, ++cur_bid) { requests[i] = (back_page + i)->write(*cur_bid); } std::swap(back_page, front_page); bids.reserve(bids.size() + blocks_per_page); cache_offset = blocks_per_page * block_type::size + 1; current_element = &((*front_page)[0]); ++m_size; wait_all(requests.begin(), blocks_per_page); *current_element = val; return; } current_element = element(cache_offset); *current_element = val; ++m_size; ++cache_offset; } //! Removes the element at the top of the stack. Precondition: stack is not //! empty(). Postcondition: size() is decremented. void pop() { assert(cache_offset <= 2 * blocks_per_page * block_type::size); assert(cache_offset > 0); assert(m_size > 0); if (UNLIKELY(cache_offset == 1 && bids.size() >= blocks_per_page)) { STXXL_VERBOSE2("shrinking, size: " << m_size); simple_vector requests(blocks_per_page); { typename std::vector::const_iterator cur_bid = bids.end(); for (int i = blocks_per_page - 1; i >= 0; --i) { requests[i] = (front_page + i)->read(*(--cur_bid)); } } std::swap(front_page, back_page); cache_offset = blocks_per_page * block_type::size; --m_size; current_element = &((*(back_page + (blocks_per_page - 1)))[block_type::size - 1]); wait_all(requests.begin(), blocks_per_page); block_manager::get_instance()->delete_blocks(bids.end() - blocks_per_page, bids.end()); bids.resize(bids.size() - blocks_per_page); return; } --m_size; current_element = element((--cache_offset) - 1); } //! \} private: value_type * element(unsigned_type offset) { if (offset < blocks_per_page * block_type::size) return &((*(back_page + offset / block_type::size))[offset % block_type::size]); unsigned_type unbiased_offset = offset - blocks_per_page * block_type::size; return &((*(front_page + unbiased_offset / block_type::size))[unbiased_offset % block_type::size]); } }; //! Efficient implementation that uses prefetching and overlapping using internal buffers. //! //! Use it if your access pattern consists of many repeated push'es and pop's //! For semantics of the methods see documentation of the STL \c std::stack. //! \warning The amortized complexity of operation is not O(1/DB), rather O(DB) template class grow_shrink_stack : private noncopyable { public: typedef StackConfig cfg; //! type of the elements stored in the stack typedef typename cfg::value_type value_type; typedef typename cfg::alloc_strategy alloc_strategy_type; //! type for sizes (64-bit) typedef typename cfg::size_type size_type; enum { blocks_per_page = cfg::blocks_per_page, block_size = cfg::block_size }; //! type of block used in disk-memory transfers typedef typed_block block_type; typedef BID bid_type; private: size_type m_size; unsigned_type cache_offset; value_type* current_element; simple_vector cache; typename simple_vector::iterator cache_buffers; typename simple_vector::iterator overlap_buffers; simple_vector requests; std::vector bids; alloc_strategy_type alloc_strategy; public: //! \name Constructors/Destructor //! \{ //! Default constructor: creates empty stack. grow_shrink_stack() : m_size(0), cache_offset(0), current_element(NULL), cache(blocks_per_page * 2), cache_buffers(cache.begin()), overlap_buffers(cache.begin() + blocks_per_page), requests(blocks_per_page), bids(0) { bids.reserve(blocks_per_page); } //! \} //! \name Accessor Functions //! \{ void swap(grow_shrink_stack& obj) { std::swap(m_size, obj.m_size); std::swap(cache_offset, obj.cache_offset); std::swap(current_element, obj.current_element); std::swap(cache, obj.cache); std::swap(cache_buffers, obj.cache_buffers); std::swap(overlap_buffers, obj.overlap_buffers); std::swap(requests, obj.requests); std::swap(bids, obj.bids); std::swap(alloc_strategy, obj.alloc_strategy); } //! \} //! \name Constructors/Destructors //! \{ //! Copy-construction from a another stack of any type. //! \param stack_ stack object (could be external or internal, important is that it must //! have a copy constructor, \c top() and \c pop() methods ) template grow_shrink_stack(const StackType& stack_) : m_size(0), cache_offset(0), current_element(NULL), cache(blocks_per_page * 2), cache_buffers(cache.begin()), overlap_buffers(cache.begin() + blocks_per_page), requests(blocks_per_page), bids(0) { bids.reserve(blocks_per_page); StackType stack_copy = stack_; size_t sz = stack_copy.size(); std::vector tmp(sz); for (size_t i = 0; i < sz; ++i) { tmp[sz - i - 1] = stack_copy.top(); stack_copy.pop(); } for (size_t i = 0; i < sz; ++i) push(tmp[i]); } virtual ~grow_shrink_stack() { STXXL_VERBOSE(STXXL_PRETTY_FUNCTION_NAME); try { if (requests[0].get()) wait_all(requests.begin(), blocks_per_page); } catch (const io_error&) { } block_manager::get_instance()->delete_blocks(bids.begin(), bids.end()); } //! \} //! \name Capacity //! \{ //! Returns the number of elements contained in the stack size_type size() const { return m_size; } //! Returns true if the stack is empty. bool empty() const { return (!m_size); } //! \} //! \name Accessor Functions //! \{ //! Return mutable reference to the element at the top of the //! stack. Precondition: stack is not empty(). value_type & top() { assert(m_size > 0); return (*current_element); } //! Return constant reference to the element at the top of the //! stack. Precondition: stack is not empty(). const value_type & top() const { assert(m_size > 0); return (*current_element); } //! Inserts an element at the top of the stack. Postconditions: size() is //! incremented by 1, and top() is the inserted element. void push(const value_type& val) { assert(cache_offset <= blocks_per_page * block_type::size); //assert(cache_offset >= 0); if (UNLIKELY(cache_offset == blocks_per_page * block_type::size)) // cache overflow { STXXL_VERBOSE2("growing, size: " << m_size); bids.resize(bids.size() + blocks_per_page); typename std::vector::iterator cur_bid = bids.end() - blocks_per_page; block_manager::get_instance()->new_blocks(alloc_strategy, cur_bid, bids.end(), cur_bid - bids.begin()); for (int i = 0; i < blocks_per_page; ++i, ++cur_bid) { if (requests[i].get()) requests[i]->wait(); requests[i] = (cache_buffers + i)->write(*cur_bid); } std::swap(cache_buffers, overlap_buffers); bids.reserve(bids.size() + blocks_per_page); cache_offset = 1; current_element = &((*cache_buffers)[0]); ++m_size; *current_element = val; return; } current_element = &((*(cache_buffers + cache_offset / block_type::size))[cache_offset % block_type::size]); *current_element = val; ++m_size; ++cache_offset; } //! Removes the element at the top of the stack. Precondition: stack is not //! empty(). Postcondition: size() is decremented. void pop() { assert(cache_offset <= blocks_per_page * block_type::size); assert(cache_offset > 0); assert(m_size > 0); if (UNLIKELY(cache_offset == 1 && bids.size() >= blocks_per_page)) { STXXL_VERBOSE2("shrinking, size: " << m_size); if (requests[0].get()) wait_all(requests.begin(), blocks_per_page); std::swap(cache_buffers, overlap_buffers); if (bids.size() > blocks_per_page) { STXXL_VERBOSE2("prefetching, size: " << m_size); typename std::vector::const_iterator cur_bid = bids.end() - blocks_per_page; for (int i = blocks_per_page - 1; i >= 0; --i) requests[i] = (overlap_buffers + i)->read(*(--cur_bid)); } block_manager::get_instance()->delete_blocks(bids.end() - blocks_per_page, bids.end()); bids.resize(bids.size() - blocks_per_page); cache_offset = blocks_per_page * block_type::size; --m_size; current_element = &((*(cache_buffers + (blocks_per_page - 1)))[block_type::size - 1]); return; } --m_size; unsigned_type cur_offset = (--cache_offset) - 1; current_element = &((*(cache_buffers + cur_offset / block_type::size))[cur_offset % block_type::size]); } //! \} }; //! Efficient implementation that uses prefetching and overlapping using (shared) buffers pools. //! \warning This is a single buffer stack! Each direction change (push() followed by pop() or vice versa) may cause one I/O. template class grow_shrink_stack2 : private noncopyable { public: typedef StackConfig cfg; //! type of the elements stored in the stack typedef typename cfg::value_type value_type; typedef typename cfg::alloc_strategy alloc_strategy_type; //! type for sizes (64-bit) typedef typename cfg::size_type size_type; enum { blocks_per_page = cfg::blocks_per_page, // stack of this type has only one page block_size = cfg::block_size }; //! type of block used in disk-memory transfers typedef typed_block block_type; typedef BID bid_type; private: typedef read_write_pool pool_type; size_type m_size; unsigned_type cache_offset; block_type* cache; std::vector bids; alloc_strategy_type alloc_strategy; unsigned_type pref_aggr; pool_type* owned_pool; pool_type* pool; public: //! \name Constructors/Destructors //! \{ //! Default constructor: creates empty stack. The stack will use the //! read_write_pool for prefetching and buffered writing. //! \param pool_ block write/prefetch pool //! \param prefetch_aggressiveness number of blocks that will be used from prefetch pool grow_shrink_stack2(pool_type& pool_, unsigned_type prefetch_aggressiveness = 0) : m_size(0), cache_offset(0), cache(new block_type), pref_aggr(prefetch_aggressiveness), owned_pool(NULL), pool(&pool_) { STXXL_VERBOSE2("grow_shrink_stack2::grow_shrink_stack2(...)"); } //! Default constructor: creates empty stack. The stack will use the pair //! of prefetch_pool and write_pool for prefetching and buffered writing. //! This constructor is deprecated in favor of the read_write_pool //! constructor. //! //! \param p_pool_ prefetch pool, that will be used for block prefetching //! \param w_pool_ write pool, that will be used for block writing //! \param prefetch_aggressiveness number of blocks that will be used from prefetch pool STXXL_DEPRECATED( grow_shrink_stack2(prefetch_pool& p_pool_, write_pool& w_pool_, unsigned_type prefetch_aggressiveness = 0) ) : m_size(0), cache_offset(0), cache(new block_type), pref_aggr(prefetch_aggressiveness), owned_pool(new pool_type(p_pool_, w_pool_)), pool(owned_pool) { STXXL_VERBOSE2("grow_shrink_stack2::grow_shrink_stack2(...)"); } //! \} //! \name Accessor Functions //! \{ void swap(grow_shrink_stack2& obj) { std::swap(m_size, obj.m_size); std::swap(cache_offset, obj.cache_offset); std::swap(cache, obj.cache); std::swap(bids, obj.bids); std::swap(alloc_strategy, obj.alloc_strategy); std::swap(pref_aggr, obj.pref_aggr); std::swap(owned_pool, obj.owned_pool); std::swap(pool, obj.pool); } //! \} //! \name Constructors/Destructors //! \{ virtual ~grow_shrink_stack2() { try { STXXL_VERBOSE2("grow_shrink_stack2::~grow_shrink_stack2()"); const int_type bids_size = bids.size(); const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr), (int_type)0); int_type i; for (i = bids_size - 1; i >= last_pref; --i) { // clean the prefetch buffer pool->invalidate(bids[i]); } typename std::vector::iterator cur = bids.begin(); typename std::vector::const_iterator end = bids.end(); for ( ; cur != end; ++cur) { // FIXME: read_write_pool needs something like cancel_write(bid) block_type* b = NULL; // w_pool.steal(*cur); if (b) { pool->add(cache); // return buffer cache = b; } } delete cache; } catch (const io_error&) { } block_manager::get_instance()->delete_blocks(bids.begin(), bids.end()); delete owned_pool; } //! \} //! \name Capacity //! \{ //! Returns the number of elements contained in the stack size_type size() const { return m_size; } //! Returns true if the stack is empty. bool empty() const { return (!m_size); } //! \} //! \name Accessor Functions //! \{ //! Inserts an element at the top of the stack. Postconditions: size() is //! incremented by 1, and top() is the inserted element. void push(const value_type& val) { STXXL_VERBOSE3("grow_shrink_stack2::push(" << val << ")"); assert(cache_offset <= block_type::size); if (UNLIKELY(cache_offset == block_type::size)) { STXXL_VERBOSE2("grow_shrink_stack2::push(" << val << ") growing, size: " << m_size); bids.resize(bids.size() + 1); typename std::vector::iterator cur_bid = bids.end() - 1; block_manager::get_instance()->new_blocks(alloc_strategy, cur_bid, bids.end(), cur_bid - bids.begin()); pool->write(cache, bids.back()); cache = pool->steal(); const int_type bids_size = bids.size(); const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr) - 1, (int_type)0); for (int_type i = bids_size - 2; i >= last_pref; --i) { // clean prefetch buffers pool->invalidate(bids[i]); } cache_offset = 0; } (*cache)[cache_offset] = val; ++m_size; ++cache_offset; assert(cache_offset > 0); assert(cache_offset <= block_type::size); } //! Return mutable reference to the element at the top of the //! stack. Precondition: stack is not empty(). value_type & top() { assert(m_size > 0); assert(cache_offset > 0); assert(cache_offset <= block_type::size); return (*cache)[cache_offset - 1]; } //! Return constant reference to the element at the top of the //! stack. Precondition: stack is not empty(). const value_type & top() const { assert(m_size > 0); assert(cache_offset > 0); assert(cache_offset <= block_type::size); return (*cache)[cache_offset - 1]; } //! Removes the element at the top of the stack. Precondition: stack is not //! empty(). Postcondition: size() is decremented. void pop() { STXXL_VERBOSE3("grow_shrink_stack2::pop()"); assert(m_size > 0); assert(cache_offset > 0); assert(cache_offset <= block_type::size); if (UNLIKELY(cache_offset == 1 && (!bids.empty()))) { STXXL_VERBOSE2("grow_shrink_stack2::pop() shrinking, size = " << m_size); bid_type last_block = bids.back(); bids.pop_back(); pool->read(cache, last_block)->wait(); block_manager::get_instance()->delete_block(last_block); rehint(); cache_offset = block_type::size + 1; } --cache_offset; --m_size; } //! \} //! \name Miscellaneous //! \{ //! Sets level of prefetch aggressiveness (number of blocks from the //! prefetch pool used for prefetching). //! \param new_p new value for the prefetch aggressiveness void set_prefetch_aggr(unsigned_type new_p) { if (pref_aggr > new_p) { const int_type bids_size = bids.size(); const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr), (int_type)0); for (int_type i = bids_size - new_p - 1; i >= last_pref; --i) { // clean prefetch buffers pool->invalidate(bids[i]); } } pref_aggr = new_p; rehint(); } //! Returns number of blocks used for prefetching. unsigned_type get_prefetch_aggr() const { return pref_aggr; } //! \} private: //! hint the last pref_aggr external blocks. void rehint() { const int_type bids_size = bids.size(); const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr), (int_type)0); for (int_type i = bids_size - 1; i >= last_pref; --i) { pool->hint(bids[i]); // prefetch } } }; //! A stack that migrates from internal memory to external when its size exceeds a certain threshold. //! //! For semantics of the methods see documentation of the STL \c std::stack. template class migrating_stack : private noncopyable { public: typedef typename ExternalStack::cfg cfg; //! type of the elements stored in the stack typedef typename cfg::value_type value_type; //! type for sizes (64-bit) typedef typename cfg::size_type size_type; enum { blocks_per_page = cfg::blocks_per_page, block_size = cfg::block_size }; typedef InternalStack int_stack_type; typedef ExternalStack ext_stack_type; private: enum { critical_size = CritSize }; int_stack_type* int_impl; ext_stack_type* ext_impl; //! Copy-construction from a another stack of any type. //! \warning not implemented yet! template migrating_stack(const StackType& stack_); public: //! \name Constructors/Destructors //! \{ //! Default constructor: creates empty stack. migrating_stack() : int_impl(new int_stack_type()), ext_impl(NULL) { } virtual ~migrating_stack() { delete int_impl; delete ext_impl; } //! \} //! \name Accessor Functions //! \{ void swap(migrating_stack& obj) { std::swap(int_impl, obj.int_impl); std::swap(ext_impl, obj.ext_impl); } //! \} //! \name Miscellaneous //! \{ //! Returns true if current implementation is internal, otherwise false. bool internal() const { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); return (int_impl != NULL); } //! Returns true if current implementation is external, otherwise false. bool external() const { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); return (ext_impl != NULL); } //! \} //! \name Capacity //! \{ //! Returns true if the stack is empty. bool empty() const { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); return (int_impl) ? int_impl->empty() : ext_impl->empty(); } //! Returns the number of elements contained in the stack size_type size() const { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); return (int_impl) ? size_type(int_impl->size()) : ext_impl->size(); } //! \} //! \name Accessor Functions //! \{ //! Return mutable reference to the element at the top of the //! stack. Precondition: stack is not empty(). value_type & top() { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); return (int_impl) ? int_impl->top() : ext_impl->top(); } //! Return constant reference to the element at the top of the //! stack. Precondition: stack is not empty(). const value_type & top() const { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); return (int_impl) ? int_impl->top() : ext_impl->top(); } //! Inserts an element at the top of the stack. Postconditions: size() is //! incremented by 1, and top() is the inserted element. void push(const value_type& val) { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); if (int_impl) { int_impl->push(val); if (UNLIKELY(int_impl->size() == critical_size)) { // migrate to external stack ext_impl = new ext_stack_type(*int_impl); delete int_impl; int_impl = NULL; } } else ext_impl->push(val); } //! Removes the element at the top of the stack. Precondition: stack is not //! empty(). Postcondition: size() is decremented. void pop() { assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); if (int_impl) int_impl->pop(); else ext_impl->pop(); } //! \} }; enum stack_externality { external, migrating, internal }; enum stack_behaviour { normal, grow_shrink, grow_shrink2 }; //! Stack type generator \n //! Introduction to stack container: see \ref tutorial_stack tutorial. \n //! Design and Internals of stack container: see \ref design_stack. //! //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! //! \tparam Externality selects stack implementation, default: \b external. One of //! - \c external, external container, implementation is chosen according to \c Behaviour parameter. //! - \c migrating, migrates from internal implementation given by \c IntStackType parameter //! to external implementation given by \c Behaviour parameter when size exceeds \c MigrCritSize //! - \c internal, choses \c IntStackType implementation //! //! \tparam Behaviour chooses \b external implementation, default: \b stxxl::normal_stack. One of: //! - \c normal, conservative version, implemented in \c stxxl::normal_stack //! - \c grow_shrink, efficient version, implemented in \c stxxl::grow_shrink_stack //! - \c grow_shrink2, efficient version, implemented in \c stxxl::grow_shrink_stack2 //! //! \tparam BlocksPerPage defines how many blocks has one page of internal cache of an //! \b external implementation, default is \b 4. All \b external implementations have //! \b two pages. //! //! \tparam BlockSize external block size in bytes, default is 2 MiB. //! //! \tparam IntStackType type of internal stack used for some implementations, default: \b std::stack. //! //! \tparam MigrCritSize threshold value for number of elements when //! stxxl::migrating_stack migrates to the external memory, default: 2 x BlocksPerPage x BlockSize. //! //! \tparam AllocStr one of allocation strategies: striping, RC, SR, or FR. Default is \b RC. //! //! \tparam SizeType size type, default is \b stxxl::uint64. //! //! The configured stack type is available as STACK_GENERATOR<>::result. //! template < class ValueType, stack_externality Externality = external, stack_behaviour Behaviour = normal, unsigned BlocksPerPage = 4, unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), class IntStackType = std::stack, unsigned_type MigrCritSize = (2* BlocksPerPage* BlockSize), class AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY, class SizeType = stxxl::uint64 > class STACK_GENERATOR { typedef stack_config_generator cfg; typedef typename IF, grow_shrink_stack2 >::result GrShrTp; typedef typename IF, GrShrTp>::result ExtStackType; typedef typename IF, ExtStackType>::result MigrOrNotStackType; public: typedef typename IF::result result; }; //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::normal_stack& a, stxxl::normal_stack& b) { a.swap(b); } template void swap(stxxl::grow_shrink_stack& a, stxxl::grow_shrink_stack& b) { a.swap(b); } template void swap(stxxl::grow_shrink_stack2& a, stxxl::grow_shrink_stack2& b) { a.swap(b); } template void swap(stxxl::migrating_stack& a, stxxl::migrating_stack& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_STACK_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/btree/leaf.h000644 001411 000144 00000056475 12411366426 022617 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/btree/leaf.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_BTREE_LEAF_HEADER #define STXXL_CONTAINERS_BTREE_LEAF_HEADER #include #include STXXL_BEGIN_NAMESPACE namespace btree { template class node_cache; template class normal_leaf : private noncopyable { public: typedef normal_leaf self_type; friend class node_cache; typedef KeyType key_type; typedef DataType data_type; typedef KeyCmp key_compare; typedef std::pair value_type; typedef value_type& reference; typedef const value_type& const_reference; enum { raw_size = RawSize }; typedef BID bid_type; struct metainfo_type { bid_type me, pred, succ; unsigned cur_size; }; typedef typed_block block_type; enum { nelements = block_type::size - 1, max_size = nelements, min_size = nelements / 2 }; typedef BTreeType btree_type; typedef typename btree_type::size_type size_type; typedef btree_iterator_base iterator_base; typedef btree_iterator iterator; typedef btree_const_iterator const_iterator; typedef node_cache leaf_cache_type; public: struct value_compare : public std::binary_function { key_compare comp; value_compare(key_compare c) : comp(c) { } bool operator () (const value_type& x, const value_type& y) const { return comp(x.first, y.first); } }; private: block_type* m_block; btree_type* m_btree; key_compare m_cmp; value_compare m_vcmp; void split(std::pair& splitter) { bid_type new_bid; m_btree->m_leaf_cache.get_new_node(new_bid); // new (left) leaf normal_leaf* new_leaf = m_btree->m_leaf_cache.get_node(new_bid, true); assert(new_leaf); // update links new_leaf->succ() = my_bid(); normal_leaf* pred_leaf = NULL; if (pred().valid()) { new_leaf->pred() = pred(); pred_leaf = m_btree->m_leaf_cache.get_node(pred()); assert(pred_leaf); assert(m_vcmp(pred_leaf->back(), front())); pred_leaf->succ() = new_bid; } pred() = new_bid; typedef std::vector iterators2fix_type; iterators2fix_type iterators2fix; m_btree->m_iterator_map.find(my_bid(), 0, size(), iterators2fix); const unsigned end_of_smaller_part = size() / 2; splitter.first = ((*m_block)[end_of_smaller_part - 1]).first; splitter.second = new_bid; const unsigned old_size = size(); // copy the smaller part std::copy(m_block->begin(), m_block->begin() + end_of_smaller_part, new_leaf->m_block->begin()); new_leaf->m_block->info.cur_size = end_of_smaller_part; // copy the larger part std::copy(m_block->begin() + end_of_smaller_part, m_block->begin() + old_size, m_block->begin()); m_block->info.cur_size = old_size - end_of_smaller_part; assert(size() + new_leaf->size() == old_size); // fix iterators for (typename iterators2fix_type::iterator it2fix = iterators2fix.begin(); it2fix != iterators2fix.end(); ++it2fix) { m_btree->m_iterator_map.unregister_iterator(**it2fix); if ((*it2fix)->pos < end_of_smaller_part) // belongs to the smaller part (*it2fix)->bid = new_bid; else (*it2fix)->pos -= end_of_smaller_part; m_btree->m_iterator_map.register_iterator(**it2fix); } STXXL_VERBOSE1("btree::normal_leaf split leaf " << this << " splitter: " << splitter.first); #if STXXL_VERBOSE_LEVEL >= 1 if (pred_leaf) { STXXL_VERBOSE1("btree::normal_leaf pred_part.smallest = " << pred_leaf->front().first); STXXL_VERBOSE1("btree::normal_leaf pred_part.largest = " << pred_leaf->back().first); } #endif STXXL_VERBOSE1("btree::normal_leaf smaller_part.smallest = " << new_leaf->front().first); STXXL_VERBOSE1("btree::normal_leaf smaller_part.largest = " << new_leaf->back().first); STXXL_VERBOSE1("btree::normal_leaf larger_part.smallest = " << front().first); STXXL_VERBOSE1("btree::normal_leaf larger_part.largest = " << back().first); m_btree->m_leaf_cache.unfix_node(new_bid); } public: virtual ~normal_leaf() { delete m_block; } normal_leaf(btree_type* btree, key_compare cmp) : m_block(new block_type), m_btree(btree), m_cmp(cmp), m_vcmp(cmp) { assert(min_nelements() >= 2); assert(2 * min_nelements() - 1 <= max_nelements()); assert(max_nelements() <= nelements); assert(unsigned(block_type::size) >= nelements + 1); // extra space for an overflow } bool overflows() const { return m_block->info.cur_size > max_nelements(); } bool underflows() const { return m_block->info.cur_size < min_nelements(); } static unsigned max_nelements() { return max_size; } static unsigned min_nelements() { return min_size; } bid_type & succ() { return m_block->info.succ; } bid_type & pred() { return m_block->info.pred; } const bid_type & succ() const { return m_block->info.succ; } const bid_type & pred() const { return m_block->info.pred; } /* template normal_leaf(InputIterator begin_, InputIterator end_, btree_type * btree, key_compare cmp): m_block(new block_type), m_btree(btree), m_cmp(cmp), m_vcmp(cmp) { assert(min_nelements() >=2); assert(2*min_nelements() - 1 <= max_nelements()); assert(max_nelements() <= nelements); assert(unsigned(block_type::size) >= nelements +1); // extra space for an overflow element unsigned new_size = end_ - begin_; assert(new_size <= max_nelements()); assert(new_size >= min_nelements()); std::copy(begin_,end_,m_block->begin()); assert(stxxl::is_sorted(m_block->begin(),m_block->begin() + new_size, m_vcmp)); m_block->info.cur_size = new_size; }*/ unsigned size() const { return m_block->info.cur_size; } const bid_type & my_bid() const { return m_block->info.me; } void save() { request_ptr req = m_block->write(my_bid()); req->wait(); } request_ptr load(const bid_type& bid) { request_ptr req = m_block->read(bid); req->wait(); assert(bid == my_bid()); return req; } request_ptr prefetch(const bid_type& bid) { return m_block->read(bid); } void init(const bid_type& my_bid_) { m_block->info.me = my_bid_; m_block->info.succ = bid_type(); m_block->info.pred = bid_type(); m_block->info.cur_size = 0; } reference operator [] (unsigned_type i) { return (*m_block)[i]; } const_reference operator [] (unsigned_type i) const { return (*m_block)[i]; } reference back() { return (*m_block)[size() - 1]; } reference front() { return *(m_block->begin()); } const_reference back() const { return (*m_block)[size() - 1]; } const_reference front() const { return *(m_block->begin()); } void dump() { STXXL_VERBOSE2("Dump of leaf " << this); for (unsigned i = 0; i < size(); ++i) STXXL_VERBOSE2((*this)[i].first << " " << (*this)[i].second); } std::pair insert( const value_type& x, std::pair& splitter) { assert(size() <= max_nelements()); splitter.first = key_compare::max_value(); typename block_type::iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), x, m_vcmp); if (!(m_vcmp(*it, x) || m_vcmp(x, *it)) && it != (m_block->begin() + size())) // *it == x { // already exists return std::pair( iterator(m_btree, my_bid(), unsigned(it - m_block->begin())), false); } typename block_type::iterator cur = m_block->begin() + size() - 1; for ( ; cur >= it; --cur) *(cur + 1) = *cur; // move elements to make space for the new element *it = x; std::vector iterators2fix; m_btree->m_iterator_map.find(my_bid(), unsigned(it - m_block->begin()), size(), iterators2fix); typename std::vector::iterator it2fix = iterators2fix.begin(); for ( ; it2fix != iterators2fix.end(); ++it2fix) { m_btree->m_iterator_map.unregister_iterator(**it2fix); ++((*it2fix)->pos); // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } ++(m_block->info.cur_size); std::pair result(iterator(m_btree, my_bid(), unsigned(it - m_block->begin())), true); if (size() <= max_nelements()) { // no overflow dump(); return result; } // overflow split(splitter); return result; } iterator begin() { return iterator(m_btree, my_bid(), 0); } const_iterator begin() const { return const_iterator(m_btree, my_bid(), 0); } iterator end() { return iterator(m_btree, my_bid(), size()); } void increment_iterator(iterator_base& it) const { assert(it.bid == my_bid()); assert(it.pos != size()); m_btree->m_iterator_map.unregister_iterator(it); ++(it.pos); if (it.pos == size() && succ().valid()) { // run to the end of the leaf STXXL_VERBOSE1("btree::normal_leaf jumping to the next block"); it.pos = 0; it.bid = succ(); } // increment of pos from 0 to 1 else if (it.pos == 1 && m_btree->m_prefetching_enabled) { // prefetch the succ leaf if (succ().valid()) m_btree->m_leaf_cache.prefetch_node(succ()); } m_btree->m_iterator_map.register_iterator(it); } void decrement_iterator(iterator_base& it) const { assert(it.bid == my_bid()); m_btree->m_iterator_map.unregister_iterator(it); if (it.pos == 0) { assert(pred().valid()); it.bid = pred(); normal_leaf const* pred_leaf = m_btree->m_leaf_cache.get_const_node(pred(), true); assert(pred_leaf); it.pos = pred_leaf->size() - 1; // prefetch the pred leaf of pred_leaf if (m_btree->m_prefetching_enabled && pred_leaf->pred().valid()) m_btree->m_leaf_cache.prefetch_node(pred_leaf->pred()); m_btree->m_leaf_cache.unfix_node(pred()); } else --it.pos; m_btree->m_iterator_map.register_iterator(it); } iterator find(const key_type& k) { value_type search_val(k, data_type()); typename block_type::iterator lb = std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); if (lb == m_block->begin() + size() || lb->first != k) return m_btree->end(); return iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); } const_iterator find(const key_type& k) const { value_type search_val(k, data_type()); typename block_type::iterator lb = std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); if (lb == m_block->begin() + size() || lb->first != k) return m_btree->end(); return const_iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); } iterator lower_bound(const key_type& k) { value_type search_val(k, data_type()); typename block_type::iterator lb = std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); // lower_bound is in the succ block if (lb == m_block->begin() + size() && succ().valid()) { return iterator(m_btree, succ(), 0); } return iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); } const_iterator lower_bound(const key_type& k) const { value_type search_val(k, data_type()); typename block_type::iterator lb = std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); // lower_bound is in the succ block if (lb == m_block->begin() + size() && succ().valid()) { return iterator(m_btree, succ(), 0); } return const_iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); } iterator upper_bound(const key_type& k) { value_type search_val(k, data_type()); typename block_type::iterator lb = std::upper_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); // upper_bound is in the succ block if (lb == m_block->begin() + size() && succ().valid()) { return iterator(m_btree, succ(), 0); } return iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); } const_iterator upper_bound(const key_type& k) const { value_type search_val(k, data_type()); typename block_type::iterator lb = std::upper_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); // upper_bound is in the succ block if (lb == m_block->begin() + size() && succ().valid()) { return const_iterator(m_btree, succ(), 0); } return const_iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); } size_type erase(const key_type& k) { value_type search_val(k, data_type()); typename block_type::iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); if (it == m_block->begin() + size() || it->first != k) return 0; // no such element // move elements one position left std::copy(it + 1, m_block->begin() + size(), it); std::vector iterators2fix; m_btree->m_iterator_map.find(my_bid(), unsigned(it + 1 - m_block->begin()), size(), iterators2fix); typename std::vector::iterator it2fix = iterators2fix.begin(); for ( ; it2fix != iterators2fix.end(); ++it2fix) { STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (pos--)"); m_btree->m_iterator_map.unregister_iterator(**it2fix); --((*it2fix)->pos); // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } --(m_block->info.cur_size); return 1; } void fuse(const normal_leaf& src) { STXXL_VERBOSE1("btree::normal_leaf Fusing"); assert(m_vcmp(src.back(), front())); const unsigned src_size = src.size(); typename block_type::iterator cur = m_block->begin() + size() - 1; typename block_type::const_iterator begin = m_block->begin(); for ( ; cur >= begin; --cur) *(cur + src_size) = *cur; // move elements to make space for Src elements // copy Src to *this leaf std::copy(src.m_block->begin(), src.m_block->begin() + src_size, m_block->begin()); std::vector iterators2fix; m_btree->m_iterator_map.find(my_bid(), 0, size(), iterators2fix); typename std::vector::iterator it2fix = iterators2fix.begin(); for ( ; it2fix != iterators2fix.end(); ++it2fix) { STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (pos+" << src_size << ")"); m_btree->m_iterator_map.unregister_iterator(**it2fix); ((*it2fix)->pos) += src_size; // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } iterators2fix.clear(); m_btree->m_iterator_map.find(src.my_bid(), 0, src_size, iterators2fix); for (it2fix = iterators2fix.begin(); it2fix != iterators2fix.end(); ++it2fix) { STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (bid=" << my_bid() << ")"); m_btree->m_iterator_map.unregister_iterator(**it2fix); ((*it2fix)->bid) = my_bid(); // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } m_block->info.cur_size += src_size; // update links pred() = src.pred(); if (pred().valid()) { // update successor link normal_leaf* new_pred = m_btree->m_leaf_cache.get_node(pred()); assert(new_pred); new_pred->succ() = my_bid(); } } key_type balance(normal_leaf& left) { STXXL_VERBOSE1("btree::normal_leaf Balancing leaves with bids " << left.my_bid() << " and " << my_bid()); const unsigned total_size = left.size() + size(); unsigned new_left_size = total_size / 2; assert(new_left_size <= left.max_nelements()); assert(new_left_size >= left.min_nelements()); unsigned new_right_size = total_size - new_left_size; assert(new_right_size <= max_nelements()); assert(new_right_size >= min_nelements()); assert(m_vcmp(left.back(), front()) || size() == 0); if (new_left_size < left.size()) { // #elements to move from left to *this const unsigned nEl2Move = left.size() - new_left_size; typename block_type::iterator cur = m_block->begin() + size() - 1; typename block_type::const_iterator begin = m_block->begin(); for ( ; cur >= begin; --cur) *(cur + nEl2Move) = *cur; // move elements to make space for Src elements // copy Left to *this leaf std::copy(left.m_block->begin() + new_left_size, left.m_block->begin() + left.size(), m_block->begin()); std::vector iterators2fix1; std::vector iterators2fix2; m_btree->m_iterator_map.find(my_bid(), 0, size(), iterators2fix1); m_btree->m_iterator_map.find(left.my_bid(), new_left_size, left.size(), iterators2fix2); typename std::vector::iterator it2fix = iterators2fix1.begin(); for ( ; it2fix != iterators2fix1.end(); ++it2fix) { STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (pos+" << nEl2Move << ")"); m_btree->m_iterator_map.unregister_iterator(**it2fix); ((*it2fix)->pos) += nEl2Move; // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } it2fix = iterators2fix2.begin(); for ( ; it2fix != iterators2fix2.end(); ++it2fix) { STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (pos-" << new_left_size << " bid=" << my_bid() << ")"); m_btree->m_iterator_map.unregister_iterator(**it2fix); ((*it2fix)->bid) = my_bid(); // fixing iterators ((*it2fix)->pos) -= new_left_size; // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } } else { assert(new_right_size < size()); const unsigned nEl2Move = size() - new_right_size; // #elements to move from *this to Left // copy *this to Left std::copy(m_block->begin(), m_block->begin() + nEl2Move, left.m_block->begin() + left.size()); // move elements in *this std::copy(m_block->begin() + nEl2Move, m_block->begin() + size(), m_block->begin()); std::vector iterators2fix1; std::vector iterators2fix2; m_btree->m_iterator_map.find(my_bid(), nEl2Move, size(), iterators2fix1); m_btree->m_iterator_map.find(my_bid(), 0, nEl2Move - 1, iterators2fix2); typename std::vector::iterator it2fix = iterators2fix1.begin(); for ( ; it2fix != iterators2fix1.end(); ++it2fix) { STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (pos-" << nEl2Move << ")"); m_btree->m_iterator_map.unregister_iterator(**it2fix); ((*it2fix)->pos) -= nEl2Move; // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } it2fix = iterators2fix2.begin(); for ( ; it2fix != iterators2fix2.end(); ++it2fix) { STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (pos+" << left.size() << " bid=" << left.my_bid() << ")"); m_btree->m_iterator_map.unregister_iterator(**it2fix); ((*it2fix)->bid) = left.my_bid(); // fixing iterators ((*it2fix)->pos) += left.size(); // fixing iterators m_btree->m_iterator_map.register_iterator(**it2fix); } } m_block->info.cur_size = new_right_size; // update size left.m_block->info.cur_size = new_left_size; // update size return left.back().first; } void push_back(const value_type& x) { (*this)[size()] = x; ++(m_block->info.cur_size); } }; } // namespace btree STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_BTREE_LEAF_HEADER stxxl-1.4.1/include/stxxl/bits/containers/btree/node_cache.h000644 001411 000144 00000045050 12411366426 023743 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/btree/node_cache.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_BTREE_NODE_CACHE_HEADER #define STXXL_CONTAINERS_BTREE_NODE_CACHE_HEADER #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE #define STXXL_BTREE_CACHE_VERBOSE STXXL_VERBOSE2 // TODO: speedup BID2node_ access using search result iterator in the methods namespace btree { template class node_cache : private noncopyable { public: typedef BTreeType btree_type; typedef NodeType node_type; typedef typename node_type::block_type block_type; typedef typename node_type::bid_type bid_type; typedef typename btree_type::key_compare key_compare; typedef typename btree_type::alloc_strategy_type alloc_strategy_type; typedef stxxl::lru_pager<> pager_type; private: btree_type* m_btree; key_compare m_cmp; /* struct bid_comp { bool operator () (const bid_type & a, const bid_type & b) const { return (a.storage < b.storage) || ( a.storage == b.storage && a.offset < b.offset); } }; */ struct bid_hash { size_t operator () (const bid_type& bid) const { size_t result = longhash1(bid.offset + reinterpret_cast(bid.storage)); return result; } #if STXXL_MSVC bool operator () (const bid_type& a, const bid_type& b) const { return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset); } enum { // parameters for hash table bucket_size = 4, // 0 < bucket_size min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N }; #endif }; std::vector m_nodes; std::vector m_reqs; std::vector m_fixed; std::vector m_dirty; std::vector m_free_nodes; typedef typename compat_hash_map::result hash_map_type; //typedef std::map BID2node_type; typedef hash_map_type bid2node_type; bid2node_type m_bid2node; pager_type m_pager; block_manager* m_bm; alloc_strategy_type m_alloc_strategy; int64 n_found; int64 n_not_found; int64 n_created; int64 n_deleted; int64 n_read; int64 n_written; int64 n_clean_forced; // changes btree pointer in all contained iterators void change_btree_pointers(btree_type* b) { for (typename std::vector::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it) { (*it)->m_btree = b; } } public: node_cache(unsigned_type cache_size_in_bytes, btree_type* btree, key_compare cmp) : m_btree(btree), m_cmp(cmp), m_bm(block_manager::get_instance()), n_found(0), n_not_found(0), n_created(0), n_deleted(0), n_read(0), n_written(0), n_clean_forced(0) { const unsigned_type nnodes = cache_size_in_bytes / block_type::raw_size; STXXL_BTREE_CACHE_VERBOSE("btree::node_cache constructor nodes=" << nnodes); if (nnodes < 3) { STXXL_THROW2(std::runtime_error, "btree::node_cache::node_cache", "Too few memory for a node cache (<3)"); } m_nodes.reserve(nnodes); m_reqs.resize(nnodes); m_free_nodes.reserve(nnodes); m_fixed.resize(nnodes, false); m_dirty.resize(nnodes, true); for (unsigned_type i = 0; i < nnodes; ++i) { m_nodes.push_back(new node_type(m_btree, m_cmp)); m_free_nodes.push_back(i); } pager_type tmp_pager(nnodes); std::swap(m_pager, tmp_pager); } unsigned_type size() const { return m_nodes.size(); } // returns the number of fixed pages unsigned_type nfixed() const { typename bid2node_type::const_iterator i = m_bid2node.begin(); typename bid2node_type::const_iterator end = m_bid2node.end(); unsigned_type cnt = 0; for ( ; i != end; ++i) { if (m_fixed[(*i).second]) ++cnt; } return cnt; } ~node_cache() { STXXL_BTREE_CACHE_VERBOSE("btree::node_cache destructor addr=" << this); typename bid2node_type::const_iterator i = m_bid2node.begin(); typename bid2node_type::const_iterator end = m_bid2node.end(); for ( ; i != end; ++i) { const unsigned_type p = (*i).second; if (m_reqs[p].valid()) m_reqs[p]->wait(); if (m_dirty[p]) m_nodes[p]->save(); } for (unsigned_type i = 0; i < size(); ++i) delete m_nodes[i]; } node_type * get_new_node(bid_type& new_bid) { ++n_created; if (m_free_nodes.empty()) { // need to kick a node int_type node2kick; unsigned_type i = 0; const unsigned_type max_tries = size() + 1; do { ++i; node2kick = m_pager.kick(); if (i == max_tries) { STXXL_ERRMSG( "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); STXXL_ERRMSG("Returning NULL node."); return NULL; } m_pager.hit(node2kick); } while (m_fixed[node2kick]); if (m_reqs[node2kick].valid()) m_reqs[node2kick]->wait(); node_type& node = *(m_nodes[node2kick]); if (m_dirty[node2kick]) { node.save(); ++n_written; } else ++n_clean_forced; //reqs_[node2kick] = request_ptr(); // reset request assert(m_bid2node.find(node.my_bid()) != m_bid2node.end()); m_bid2node.erase(node.my_bid()); m_bm->new_block(m_alloc_strategy, new_bid); m_bid2node[new_bid] = node2kick; node.init(new_bid); m_dirty[node2kick] = true; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_new_node, need to kick node " << node2kick); return &node; } int_type free_node = m_free_nodes.back(); m_free_nodes.pop_back(); assert(m_fixed[free_node] == false); m_bm->new_block(m_alloc_strategy, new_bid); m_bid2node[new_bid] = free_node; node_type& node = *(m_nodes[free_node]); node.init(new_bid); // assert(!(reqs_[free_node].valid())); m_pager.hit(free_node); m_dirty[free_node] = true; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_new_node, free node " << free_node << "available"); return &node; } node_type * get_node(const bid_type& bid, bool fix = false) { typename bid2node_type::const_iterator it = m_bid2node.find(bid); ++n_read; if (it != m_bid2node.end()) { // the node is in cache const int_type nodeindex = it->second; STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, the node " << nodeindex << "is in cache , fix=" << fix); m_fixed[nodeindex] = fix; m_pager.hit(nodeindex); m_dirty[nodeindex] = true; if (m_reqs[nodeindex].valid() && !m_reqs[nodeindex]->poll()) m_reqs[nodeindex]->wait(); ++n_found; return m_nodes[nodeindex]; } ++n_not_found; // the node is not in cache if (m_free_nodes.empty()) { // need to kick a node int_type node2kick; unsigned_type i = 0; const unsigned_type max_tries = size() + 1; do { ++i; node2kick = m_pager.kick(); if (i == max_tries) { STXXL_ERRMSG( "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); STXXL_ERRMSG("Returning NULL node."); return NULL; } m_pager.hit(node2kick); } while (m_fixed[node2kick]); if (m_reqs[node2kick].valid()) m_reqs[node2kick]->wait(); node_type& node = *(m_nodes[node2kick]); if (m_dirty[node2kick]) { node.save(); ++n_written; } else ++n_clean_forced; m_bid2node.erase(node.my_bid()); m_reqs[node2kick] = node.load(bid); m_bid2node[bid] = node2kick; m_fixed[node2kick] = fix; m_dirty[node2kick] = true; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, need to kick node" << node2kick << " fix=" << fix); return &node; } int_type free_node = m_free_nodes.back(); m_free_nodes.pop_back(); assert(m_fixed[free_node] == false); node_type& node = *(m_nodes[free_node]); m_reqs[free_node] = node.load(bid); m_bid2node[bid] = free_node; m_pager.hit(free_node); m_fixed[free_node] = fix; m_dirty[free_node] = true; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, free node " << free_node << "available, fix=" << fix); return &node; } node_type const * get_const_node(const bid_type& bid, bool fix = false) { typename bid2node_type::const_iterator it = m_bid2node.find(bid); ++n_read; if (it != m_bid2node.end()) { // the node is in cache const int_type nodeindex = it->second; STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, the node " << nodeindex << "is in cache , fix=" << fix); m_fixed[nodeindex] = fix; m_pager.hit(nodeindex); if (m_reqs[nodeindex].valid() && !m_reqs[nodeindex]->poll()) m_reqs[nodeindex]->wait(); ++n_found; return m_nodes[nodeindex]; } ++n_not_found; // the node is not in cache if (m_free_nodes.empty()) { // need to kick a node int_type node2kick; unsigned_type i = 0; const unsigned_type max_tries = size() + 1; do { ++i; node2kick = m_pager.kick(); if (i == max_tries) { STXXL_ERRMSG( "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); STXXL_ERRMSG("Returning NULL node."); return NULL; } m_pager.hit(node2kick); } while (m_fixed[node2kick]); if (m_reqs[node2kick].valid()) m_reqs[node2kick]->wait(); node_type& node = *(m_nodes[node2kick]); if (m_dirty[node2kick]) { node.save(); ++n_written; } else ++n_clean_forced; m_bid2node.erase(node.my_bid()); m_reqs[node2kick] = node.load(bid); m_bid2node[bid] = node2kick; m_fixed[node2kick] = fix; m_dirty[node2kick] = false; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, need to kick node" << node2kick << " fix=" << fix); return &node; } int_type free_node = m_free_nodes.back(); m_free_nodes.pop_back(); assert(m_fixed[free_node] == false); node_type& node = *(m_nodes[free_node]); m_reqs[free_node] = node.load(bid); m_bid2node[bid] = free_node; m_pager.hit(free_node); m_fixed[free_node] = fix; m_dirty[free_node] = false; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, free node " << free_node << "available, fix=" << fix); return &node; } void delete_node(const bid_type& bid) { typename bid2node_type::const_iterator it = m_bid2node.find(bid); try { if (it != m_bid2node.end()) { // the node is in the cache const int_type nodeindex = it->second; STXXL_BTREE_CACHE_VERBOSE("btree::node_cache delete_node " << nodeindex << " from cache."); if (m_reqs[nodeindex].valid()) m_reqs[nodeindex]->wait(); //reqs_[nodeindex] = request_ptr(); // reset request m_free_nodes.push_back(nodeindex); m_bid2node.erase(bid); m_fixed[nodeindex] = false; } ++n_deleted; } catch (const io_error& ex) { m_bm->delete_block(bid); throw io_error(ex.what()); } m_bm->delete_block(bid); } void prefetch_node(const bid_type& bid) { if (m_bid2node.find(bid) != m_bid2node.end()) return; // the node is not in cache if (m_free_nodes.empty()) { // need to kick a node int_type node2kick; unsigned_type i = 0; const unsigned_type max_tries = size() + 1; do { ++i; node2kick = m_pager.kick(); if (i == max_tries) { STXXL_ERRMSG( "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); STXXL_ERRMSG("Returning NULL node."); return; } m_pager.hit(node2kick); } while (m_fixed[node2kick]); if (m_reqs[node2kick].valid()) m_reqs[node2kick]->wait(); node_type& node = *(m_nodes[node2kick]); if (m_dirty[node2kick]) { node.save(); ++n_written; } else ++n_clean_forced; m_bid2node.erase(node.my_bid()); m_reqs[node2kick] = node.prefetch(bid); m_bid2node[bid] = node2kick; m_fixed[node2kick] = false; m_dirty[node2kick] = false; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache prefetch_node, need to kick node" << node2kick << " "); return; } int_type free_node = m_free_nodes.back(); m_free_nodes.pop_back(); assert(m_fixed[free_node] == false); node_type& node = *(m_nodes[free_node]); m_reqs[free_node] = node.prefetch(bid); m_bid2node[bid] = free_node; m_pager.hit(free_node); m_fixed[free_node] = false; m_dirty[free_node] = false; assert(size() == m_bid2node.size() + m_free_nodes.size()); STXXL_BTREE_CACHE_VERBOSE("btree::node_cache prefetch_node, free node " << free_node << "available"); return; } void unfix_node(const bid_type& bid) { assert(m_bid2node.find(bid) != m_bid2node.end()); m_fixed[m_bid2node[bid]] = false; STXXL_BTREE_CACHE_VERBOSE("btree::node_cache unfix_node, node " << m_bid2node[bid]); } void swap(node_cache& obj) { std::swap(m_cmp, obj.m_cmp); std::swap(m_nodes, obj.m_nodes); std::swap(m_reqs, obj.m_reqs); change_btree_pointers(m_btree); obj.change_btree_pointers(obj.m_btree); std::swap(m_fixed, obj.m_fixed); std::swap(m_free_nodes, obj.m_free_nodes); std::swap(m_bid2node, obj.m_bid2node); std::swap(m_pager, obj.m_pager); std::swap(m_alloc_strategy, obj.m_alloc_strategy); std::swap(n_found, obj.n_found); std::swap(n_not_found, obj.n_found); std::swap(n_created, obj.n_created); std::swap(n_deleted, obj.n_deleted); std::swap(n_read, obj.n_read); std::swap(n_written, obj.n_written); std::swap(n_clean_forced, obj.n_clean_forced); } void print_statistics(std::ostream& o) const { if (n_read) o << "Found blocks : " << n_found << " (" << 100. * double(n_found) / double(n_read) << "%)" << std::endl; else o << "Found blocks : " << n_found << " (" << 100 << "%)" << std::endl; o << "Not found blocks : " << n_not_found << std::endl; o << "Created in the cache blocks : " << n_created << std::endl; o << "Deleted blocks : " << n_deleted << std::endl; o << "Read blocks : " << n_read << std::endl; o << "Written blocks : " << n_written << std::endl; o << "Clean blocks forced from the cache: " << n_clean_forced << std::endl; } void reset_statistics() { n_found = 0; n_not_found = 0; n_created = 0; n_deleted = 0; n_read = 0; n_written = 0; n_clean_forced = 0; } }; } // namespace btree STXXL_END_NAMESPACE namespace std { template void swap(stxxl::btree::node_cache& a, stxxl::btree::node_cache& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_BTREE_NODE_CACHE_HEADER stxxl-1.4.1/include/stxxl/bits/containers/btree/node.h000644 001411 000144 00000063125 12411366426 022623 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/btree/node.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_BTREE_NODE_HEADER #define STXXL_CONTAINERS_BTREE_NODE_HEADER #include #include STXXL_BEGIN_NAMESPACE namespace btree { template class node_cache; template class normal_node : private noncopyable { public: typedef normal_node self_type; friend class node_cache; typedef KeyType key_type; typedef KeyCmp key_compare; enum { raw_size = RawSize }; typedef BID bid_type; typedef bid_type node_bid_type; typedef self_type node_type; typedef std::pair value_type; typedef value_type& reference; typedef const value_type& const_reference; struct metainfo_type { bid_type me; unsigned cur_size; }; typedef typed_block block_type; enum { nelements = block_type::size - 1, max_size = nelements, min_size = nelements / 2 }; typedef typename block_type::iterator block_iterator; typedef typename block_type::const_iterator block_const_iterator; typedef BTreeType btree_type; typedef typename btree_type::size_type size_type; typedef typename btree_type::iterator iterator; typedef typename btree_type::const_iterator const_iterator; typedef typename btree_type::value_type btree_value_type; typedef typename btree_type::leaf_bid_type leaf_bid_type; typedef typename btree_type::leaf_type leaf_type; typedef node_cache node_cache_type; private: struct value_compare : public std::binary_function { key_compare comp; value_compare(key_compare c) : comp(c) { } bool operator () (const value_type& x, const value_type& y) const { return comp(x.first, y.first); } }; block_type* m_block; btree_type* m_btree; key_compare m_cmp; value_compare m_vcmp; std::pair insert(const std::pair& splitter, const block_iterator& place2insert) { std::pair result(key_compare::max_value(), bid_type()); // splitter != *place2insert assert(m_vcmp(*place2insert, splitter) || m_vcmp(splitter, *place2insert)); block_iterator cur = m_block->begin() + size() - 1; for ( ; cur >= place2insert; --cur) *(cur + 1) = *cur; // copy elements to make space for the new element *place2insert = splitter; // insert ++(m_block->info.cur_size); if (size() > max_nelements()) // overflow! need to split { STXXL_VERBOSE1("btree::normal_node::insert overflow happened, splitting"); bid_type new_bid; m_btree->m_node_cache.get_new_node(new_bid); // new (left) node normal_node* new_node = m_btree->m_node_cache.get_node(new_bid, true); assert(new_node); const unsigned end_of_smaller_part = size() / 2; result.first = ((*m_block)[end_of_smaller_part - 1]).first; result.second = new_bid; const unsigned old_size = size(); // copy the smaller part std::copy(m_block->begin(), m_block->begin() + end_of_smaller_part, new_node->m_block->begin()); new_node->m_block->info.cur_size = end_of_smaller_part; // copy the larger part std::copy(m_block->begin() + end_of_smaller_part, m_block->begin() + old_size, m_block->begin()); m_block->info.cur_size = old_size - end_of_smaller_part; assert(size() + new_node->size() == old_size); m_btree->m_node_cache.unfix_node(new_bid); STXXL_VERBOSE1("btree::normal_node split leaf " << this << " splitter: " << result.first); } return result; } template void fuse_or_balance(block_iterator UIt, CacheType& cache) { typedef typename CacheType::node_type local_node_type; typedef typename local_node_type::bid_type local_bid_type; block_iterator leftIt, rightIt; if (UIt == (m_block->begin() + size() - 1)) // UIt is the last entry in the root { assert(UIt != m_block->begin()); rightIt = UIt; leftIt = --UIt; } else { leftIt = UIt; rightIt = ++UIt; assert(rightIt != (m_block->begin() + size())); } // now fuse or balance nodes pointed by leftIt and rightIt local_bid_type left_bid = (local_bid_type)leftIt->second; local_bid_type right_bid = (local_bid_type)rightIt->second; local_node_type* left_node = cache.get_node(left_bid, true); local_node_type* right_node = cache.get_node(right_bid, true); const unsigned total_size = left_node->size() + right_node->size(); if (total_size <= right_node->max_nelements()) { // --- fuse --- // add the content of left_node to right_node right_node->fuse(*left_node); cache.unfix_node(right_bid); // 'delete_node' unfixes left-bid also cache.delete_node(left_bid); // delete left BID from the root std::copy(leftIt + 1, m_block->begin() + size(), leftIt); --(m_block->info.cur_size); } else { // --- balance --- key_type new_splitter = right_node->balance(*left_node); // change key leftIt->first = new_splitter; assert(m_vcmp(*leftIt, *rightIt)); cache.unfix_node(left_bid); cache.unfix_node(right_bid); } } public: virtual ~normal_node() { delete m_block; } normal_node(btree_type* btree, key_compare cmp) : m_block(new block_type), m_btree(btree), m_cmp(cmp), m_vcmp(cmp) { assert(min_nelements() >= 2); assert(2 * min_nelements() - 1 <= max_nelements()); assert(max_nelements() <= nelements); // extra space for an overflow assert(unsigned(block_type::size) >= nelements + 1); } block_type & block() { return *m_block; } bool overflows() const { return m_block->info.cur_size > max_nelements(); } bool underflows() const { return m_block->info.cur_size < min_nelements(); } static unsigned max_nelements() { return max_size; } static unsigned min_nelements() { return min_size; } /* template normal_node(InputIterator begin_, InputIterator end_, btree_type * btree, key_compare cmp): m_block(new block_type), m_btree(btree), m_cmp(cmp), m_vcmp(cmp) { assert(min_nelements() >=2); assert(2*min_nelements() - 1 <= max_nelements()); assert(max_nelements() <= nelements); assert(unsigned(block_type::size) >= nelements +1); // extra space for an overflow unsigned new_size = end_ - begin_; assert(new_size <= max_nelements()); assert(new_size >= min_nelements()); std::copy(begin_,end_,m_block->begin()); assert(stxxl::is_sorted(m_block->begin(),m_block->begin() + new_size, m_vcmp)); m_block->info.cur_size = new_size; }*/ unsigned size() const { return m_block->info.cur_size; } bid_type my_bid() const { return m_block->info.me; } void save() { request_ptr req = m_block->write(my_bid()); req->wait(); } request_ptr load(const bid_type& bid) { request_ptr req = m_block->read(bid); req->wait(); assert(bid == my_bid()); return req; } request_ptr prefetch(const bid_type& bid) { return m_block->read(bid); } void init(const bid_type& my_bid_) { m_block->info.me = my_bid_; m_block->info.cur_size = 0; } reference operator [] (int i) { return (*m_block)[i]; } const_reference operator [] (int i) const { return (*m_block)[i]; } reference back() { return (*m_block)[size() - 1]; } reference front() { return *(m_block->begin()); } const_reference back() const { return (*m_block)[size() - 1]; } const_reference front() const { return *(m_block->begin()); } std::pair insert(const btree_value_type& x, unsigned height, std::pair& splitter) { assert(size() <= max_nelements()); splitter.first = key_compare::max_value(); value_type key2search(x.first, bid_type()); block_iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); //bid_type found_bid = it->second; if (height == 2) // found_bid points to a leaf { STXXL_VERBOSE1("btree::normal_node Inserting new value into a leaf"); leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)it->second, true); assert(leaf); std::pair bot_splitter; std::pair result = leaf->insert(x, bot_splitter); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)it->second); //if(key_compare::max_value() == BotSplitter.first) if (!(m_cmp(key_compare::max_value(), bot_splitter.first) || m_cmp(bot_splitter.first, key_compare::max_value()))) return result; // no overflow/splitting happened STXXL_VERBOSE1("btree::normal_node Inserting new value in *this"); splitter = insert(std::make_pair(bot_splitter.first, bid_type(bot_splitter.second)), it); return result; } else { // found_bid points to a node STXXL_VERBOSE1("btree::normal_node Inserting new value into a node"); node_type* node = m_btree->m_node_cache.get_node((node_bid_type)it->second, true); assert(node); std::pair bot_splitter; std::pair result = node->insert(x, height - 1, bot_splitter); m_btree->m_node_cache.unfix_node((node_bid_type)it->second); //if(key_compare::max_value() == BotSplitter.first) if (!(m_cmp(key_compare::max_value(), bot_splitter.first) || m_cmp(bot_splitter.first, key_compare::max_value()))) return result; // no overflow/splitting happened STXXL_VERBOSE1("btree::normal_node Inserting new value in *this"); splitter = insert(bot_splitter, it); return result; } } iterator begin(unsigned height) { bid_type first_bid = m_block->begin()->second; if (height == 2) // FirstBid points to a leaf { assert(size() > 1); STXXL_VERBOSE1("btree::node retrieveing begin() from the first leaf"); leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)first_bid); assert(leaf); return leaf->begin(); } else { // FirstBid points to a node STXXL_VERBOSE1("btree: retrieveing begin() from the first node"); node_type* node = m_btree->m_node_cache.get_node((node_bid_type)first_bid, true); assert(node); iterator result = node->begin(height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)first_bid); return result; } } const_iterator begin(unsigned height) const { bid_type FirstBid = m_block->begin()->second; if (height == 2) // FirstBid points to a leaf { assert(size() > 1); STXXL_VERBOSE1("btree::node retrieveing begin() from the first leaf"); const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)FirstBid); assert(leaf); return leaf->begin(); } else { // FirstBid points to a node STXXL_VERBOSE1("btree: retrieveing begin() from the first node"); const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)FirstBid, true); assert(node); const_iterator result = node->begin(height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)FirstBid); return result; } } iterator find(const key_type& k, unsigned height) { value_type key2search(k, bid_type()); block_iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); bid_type found_bid = it->second; if (height == 2) // found_bid points to a leaf { STXXL_VERBOSE1("Searching in a leaf"); leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); assert(leaf); iterator result = leaf->find(k); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); return result; } // found_bid points to a node STXXL_VERBOSE1("Searching in a node"); node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); assert(node); iterator result = node->find(k, height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); return result; } const_iterator find(const key_type& k, unsigned height) const { value_type key2search(k, bid_type()); block_iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); bid_type found_bid = it->second; if (height == 2) // found_bid points to a leaf { STXXL_VERBOSE1("Searching in a leaf"); const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)found_bid, true); assert(leaf); const_iterator result = leaf->find(k); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); return result; } // found_bid points to a node STXXL_VERBOSE1("Searching in a node"); const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)found_bid, true); assert(node); const_iterator result = node->find(k, height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); return result; } iterator lower_bound(const key_type& k, unsigned height) { value_type key2search(k, bid_type()); assert(!m_vcmp(back(), key2search)); block_iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); bid_type found_bid = it->second; if (height == 2) // found_bid points to a leaf { STXXL_VERBOSE1("Searching lower bound in a leaf"); leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); assert(leaf); iterator result = leaf->lower_bound(k); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); return result; } // found_bid points to a node STXXL_VERBOSE1("Searching lower bound in a node"); node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); assert(node); iterator result = node->lower_bound(k, height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); return result; } const_iterator lower_bound(const key_type& k, unsigned height) const { value_type key2search(k, bid_type()); assert(!m_vcmp(back(), key2search)); block_iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); bid_type found_bid = it->second; if (height == 2) // found_bid points to a leaf { STXXL_VERBOSE1("Searching lower bound in a leaf"); const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)found_bid, true); assert(leaf); const_iterator result = leaf->lower_bound(k); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); return result; } // found_bid points to a node STXXL_VERBOSE1("Searching lower bound in a node"); const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)found_bid, true); assert(node); const_iterator result = node->lower_bound(k, height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); return result; } iterator upper_bound(const key_type& k, unsigned height) { value_type key2search(k, bid_type()); assert(m_vcmp(key2search, back())); block_iterator it = std::upper_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); bid_type found_bid = it->second; if (height == 2) // found_bid points to a leaf { STXXL_VERBOSE1("Searching upper bound in a leaf"); leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); assert(leaf); iterator result = leaf->upper_bound(k); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); return result; } // found_bid points to a node STXXL_VERBOSE1("Searching upper bound in a node"); node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); assert(node); iterator result = node->upper_bound(k, height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); return result; } const_iterator upper_bound(const key_type& k, unsigned height) const { value_type key2search(k, bid_type()); assert(m_vcmp(key2search, back())); block_iterator it = std::upper_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); bid_type found_bid = it->second; if (height == 2) // found_bid points to a leaf { STXXL_VERBOSE1("Searching upper bound in a leaf"); const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)found_bid, true); assert(leaf); const_iterator result = leaf->upper_bound(k); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); return result; } // found_bid points to a node STXXL_VERBOSE1("Searching upper bound in a node"); const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)found_bid, true); assert(node); const_iterator result = node->upper_bound(k, height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); return result; } void fuse(const normal_node& src) { assert(m_vcmp(src.back(), front())); const unsigned src_size = src.size(); block_iterator cur = m_block->begin() + size() - 1; block_const_iterator begin = m_block->begin(); for ( ; cur >= begin; --cur) *(cur + src_size) = *cur; // move elements to make space for Src elements // copy Src to *this leaf std::copy(src.m_block->begin(), src.m_block->begin() + src_size, m_block->begin()); m_block->info.cur_size += src_size; } key_type balance(normal_node& left, bool check_constraints = true) { const unsigned total_size = left.size() + size(); unsigned new_left_size = total_size / 2; STXXL_ASSERT(!check_constraints || new_left_size <= left.max_nelements()); STXXL_ASSERT(!check_constraints || new_left_size >= left.min_nelements()); unsigned new_right_size = total_size - new_left_size; STXXL_ASSERT(!check_constraints || new_right_size <= max_nelements()); STXXL_ASSERT(!check_constraints || new_right_size >= min_nelements()); assert(m_vcmp(left.back(), front()) || size() == 0); if (new_left_size < left.size()) { // #elements to move from left to *this const unsigned nEl2Move = left.size() - new_left_size; block_iterator cur = m_block->begin() + size() - 1; block_const_iterator begin = m_block->begin(); for ( ; cur >= begin; --cur) *(cur + nEl2Move) = *cur; // move elements to make space for Src elements // copy left to *this leaf std::copy(left.m_block->begin() + new_left_size, left.m_block->begin() + left.size(), m_block->begin()); } else { assert(new_right_size < size()); // #elements to move from *this to left const unsigned nEl2Move = size() - new_right_size; // copy *this to left std::copy(m_block->begin(), m_block->begin() + nEl2Move, left.m_block->begin() + left.size()); // move elements in *this std::copy(m_block->begin() + nEl2Move, m_block->begin() + size(), m_block->begin()); } m_block->info.cur_size = new_right_size; // update size left.m_block->info.cur_size = new_left_size; // update size return left.back().first; } size_type erase(const key_type& k, unsigned height) { value_type key2search(k, bid_type()); block_iterator it = std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); assert(it != (m_block->begin() + size())); bid_type found_bid = it->second; assert(size() >= 2); if (height == 2) // 'found_bid' points to a leaf { STXXL_VERBOSE1("btree::normal_node Deleting key from a leaf"); leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); assert(leaf); size_type result = leaf->erase(k); m_btree->m_leaf_cache.unfix_node((leaf_bid_type)it->second); if (!leaf->underflows()) return result; // no underflow or root has a special degree 1 (too few elements) STXXL_VERBOSE1("btree::normal_node Fusing or rebalancing a leaf"); fuse_or_balance(it, m_btree->m_leaf_cache); return result; } // 'found_bid' points to a node STXXL_VERBOSE1("btree::normal_node Deleting key from a node"); node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); assert(node); size_type result = node->erase(k, height - 1); m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); if (!node->underflows()) return result; // no underflow happened STXXL_VERBOSE1("btree::normal_node Fusing or rebalancing a node"); fuse_or_balance(it, m_btree->m_node_cache); return result; } void deallocate_children(unsigned height) { if (height == 2) { // we have children leaves here for (block_const_iterator it = block().begin(); it != block().begin() + size(); ++it) { // delete from leaf cache and deallocate bid m_btree->m_leaf_cache.delete_node((leaf_bid_type)it->second); } } else { for (block_const_iterator it = block().begin(); it != block().begin() + size(); ++it) { node_type* node = m_btree->m_node_cache.get_node((node_bid_type)it->second); assert(node); node->deallocate_children(height - 1); // delete from node cache and deallocate bid m_btree->m_node_cache.delete_node((node_bid_type)it->second); } } } void push_back(const value_type& x) { (*this)[size()] = x; ++(m_block->info.cur_size); } }; } // namespace btree STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_BTREE_NODE_HEADER stxxl-1.4.1/include/stxxl/bits/containers/btree/btree.h000644 001411 000144 00000123175 12411366426 023001 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/btree/btree.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006, 2008 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_BTREE_BTREE_HEADER #define STXXL_CONTAINERS_BTREE_BTREE_HEADER #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE namespace btree { template class btree : private noncopyable { public: typedef KeyType key_type; typedef DataType data_type; typedef CompareType key_compare; typedef btree self_type; typedef PDAllocStrategy alloc_strategy_type; typedef stxxl::uint64 size_type; typedef stxxl::int64 difference_type; typedef std::pair value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* pointer; typedef value_type const* const_pointer; // leaf type declarations typedef normal_leaf leaf_type; friend class normal_leaf; typedef typename leaf_type::block_type leaf_block_type; typedef typename leaf_type::bid_type leaf_bid_type; typedef node_cache leaf_cache_type; friend class node_cache; // iterator types typedef btree_iterator iterator; typedef btree_const_iterator const_iterator; friend class btree_iterator_base; // iterator map type typedef iterator_map iterator_map_type; // node type declarations typedef normal_node node_type; typedef typename node_type::block_type node_block_type; friend class normal_node; typedef typename node_type::bid_type node_bid_type; typedef node_cache node_cache_type; friend class node_cache; typedef typename leaf_type::value_compare value_compare; enum { min_node_size = node_type::min_size, max_node_size = node_type::max_size, min_leaf_size = leaf_type::min_size, max_leaf_size = leaf_type::max_size }; private: key_compare m_key_compare; mutable node_cache_type m_node_cache; mutable leaf_cache_type m_leaf_cache; iterator_map_type m_iterator_map; size_type m_size; unsigned int m_height; bool m_prefetching_enabled; block_manager* m_bm; alloc_strategy_type m_alloc_strategy; typedef std::map root_node_type; typedef typename root_node_type::iterator root_node_iterator_type; typedef typename root_node_type::const_iterator root_node_const_iterator_type; typedef std::pair root_node_pair_type; root_node_type m_root_node; iterator m_end_iterator; void insert_into_root(const std::pair& splitter) { std::pair result = m_root_node.insert(splitter); STXXL_ASSERT(result.second == true); if (m_root_node.size() > max_node_size) // root overflow { STXXL_VERBOSE1("btree::insert_into_root, overflow happened, splitting"); node_bid_type left_bid; node_type* left_node = m_node_cache.get_new_node(left_bid); assert(left_node); node_bid_type right_bid; node_type* right_node = m_node_cache.get_new_node(right_bid); assert(right_node); const unsigned_type old_size = m_root_node.size(); const unsigned_type half = m_root_node.size() / 2; unsigned_type i = 0; root_node_iterator_type it = m_root_node.begin(); typename node_block_type::iterator block_it = left_node->block().begin(); while (i < half) // copy smaller part { *block_it = *it; ++i; ++block_it; ++it; } left_node->block().info.cur_size = (unsigned int)half; key_type left_key = (left_node->block()[half - 1]).first; block_it = right_node->block().begin(); while (i < old_size) // copy larger part { *block_it = *it; ++i; ++block_it; ++it; } unsigned_type right_size = right_node->block().info.cur_size = (unsigned int)(old_size - half); key_type right_key = (right_node->block()[right_size - 1]).first; assert(old_size == right_node->size() + left_node->size()); // create new root node m_root_node.clear(); m_root_node.insert(root_node_pair_type(left_key, left_bid)); m_root_node.insert(root_node_pair_type(right_key, right_bid)); ++m_height; STXXL_VERBOSE1("btree Increasing height to " << m_height); if (m_node_cache.size() < (m_height - 1)) { STXXL_THROW2(std::runtime_error, "btree::bulk_construction", "The height of the tree (" << m_height << ") has exceeded the required capacity (" << (m_node_cache.size() + 1) << ") of the node cache. Increase the node cache size."); } } } template void fuse_or_balance(root_node_iterator_type uit, CacheType& cache) { typedef typename CacheType::node_type local_node_type; typedef typename local_node_type::bid_type local_bid_type; root_node_iterator_type left_it, right_it; if (uit->first == key_compare::max_value()) { // uit is the last entry in the root assert(uit != m_root_node.begin()); right_it = uit; left_it = --uit; } else { left_it = uit; right_it = ++uit; assert(right_it != m_root_node.end()); } // now fuse or balance nodes pointed by leftIt and rightIt local_bid_type left_bid = (local_bid_type)left_it->second; local_bid_type right_bid = (local_bid_type)right_it->second; local_node_type* left_node = cache.get_node(left_bid, true); local_node_type* right_node = cache.get_node(right_bid, true); const unsigned_type total_size = left_node->size() + right_node->size(); if (total_size <= right_node->max_nelements()) { // --- fuse --- // add the content of left_node to right_node right_node->fuse(*left_node); cache.unfix_node(right_bid); // 'delete_node' unfixes left_bid also cache.delete_node(left_bid); // delete left BID from the root m_root_node.erase(left_it); } else { // --- balance --- key_type new_splitter = right_node->balance(*left_node); // delete left BID from the root m_root_node.erase(left_it); // reinsert with the new key m_root_node.insert(root_node_pair_type(new_splitter, (node_bid_type)left_bid)); cache.unfix_node(left_bid); cache.unfix_node(right_bid); } } void create_empty_leaf() { leaf_bid_type new_bid; leaf_type* new_leaf = m_leaf_cache.get_new_node(new_bid); assert(new_leaf); m_end_iterator = new_leaf->end(); // initialize end() iterator m_root_node.insert( root_node_pair_type(key_compare::max_value(), (node_bid_type)new_bid) ); } void deallocate_children() { if (m_height == 2) { // we have children leaves here for (root_node_const_iterator_type it = m_root_node.begin(); it != m_root_node.end(); ++it) { // delete from leaf cache and deallocate bid m_leaf_cache.delete_node((leaf_bid_type)it->second); } } else { for (root_node_const_iterator_type it = m_root_node.begin(); it != m_root_node.end(); ++it) { node_type* node = m_node_cache.get_node((node_bid_type)it->second); assert(node); node->deallocate_children(m_height - 1); // delete from node cache and deallocate bid m_node_cache.delete_node((node_bid_type)it->second); } } } template void bulk_construction(InputIterator begin, InputIterator end, double node_fill_factor, double leaf_fill_factor) { assert(node_fill_factor >= 0.5); assert(leaf_fill_factor >= 0.5); key_type last_key = key_compare::max_value(); typedef std::pair key_bid_pair; typedef typename stxxl::VECTOR_GENERATOR< key_bid_pair, 1, 1, node_block_type::raw_size >::result key_bid_vector_type; key_bid_vector_type bids; leaf_bid_type new_bid; leaf_type* leaf = m_leaf_cache.get_new_node(new_bid); const unsigned_type max_leaf_elements = unsigned_type( (double)leaf->max_nelements() * leaf_fill_factor ); while (begin != end) { // write data in leaves // if *b not equal to the last element if (m_key_compare(begin->first, last_key) || m_key_compare(last_key, begin->first)) { ++m_size; if (leaf->size() == max_leaf_elements) { // overflow, need a new block bids.push_back(key_bid_pair(leaf->back().first, (node_bid_type)new_bid)); leaf_type* new_leaf = m_leaf_cache.get_new_node(new_bid); assert(new_leaf); // Setting links leaf->succ() = new_leaf->my_bid(); new_leaf->pred() = leaf->my_bid(); leaf = new_leaf; } leaf->push_back(*begin); last_key = begin->first; } ++begin; } // rebalance the last leaf if (leaf->underflows() && !bids.empty()) { leaf_type* left_leaf = m_leaf_cache.get_node((leaf_bid_type)(bids.back().second)); assert(left_leaf); if (left_leaf->size() + leaf->size() <= leaf->max_nelements()) { // can fuse leaf->fuse(*left_leaf); m_leaf_cache.delete_node((leaf_bid_type)(bids.back().second)); bids.pop_back(); assert(!leaf->overflows() && !leaf->underflows()); } else { // need to rebalance const key_type new_splitter = leaf->balance(*left_leaf); bids.back().first = new_splitter; assert(!left_leaf->overflows() && !left_leaf->underflows()); } } assert(!leaf->overflows() && (!leaf->underflows() || m_size <= max_leaf_size)); m_end_iterator = leaf->end(); // initialize end() iterator bids.push_back(key_bid_pair(key_compare::max_value(), (node_bid_type)new_bid)); const unsigned_type max_node_elements = unsigned_type( double(max_node_size) * node_fill_factor ); //-tb fixes bug with only one child remaining in m_root_node while (bids.size() > node_type::max_nelements()) { key_bid_vector_type parent_bids; stxxl::uint64 nparents = div_ceil(bids.size(), max_node_elements); assert(nparents >= 2); STXXL_VERBOSE1("btree bulk construct" << " bids.size=" << bids.size() << " nparents=" << nparents << " max_node_elements=" << max_node_elements << " node_type::max_nelements=" << node_type::max_nelements()); for (typename key_bid_vector_type::const_iterator it = bids.begin(); it != bids.end(); ) { node_bid_type new_bid; node_type* node = m_node_cache.get_new_node(new_bid); assert(node); for (unsigned_type cnt = 0; cnt < max_node_elements && it != bids.end(); ++cnt, ++it) { node->push_back(*it); } STXXL_VERBOSE1("btree bulk construct node size : " << node->size() << " limits: " << node->min_nelements() << " " << node->max_nelements() << " max_node_elements: " << max_node_elements); if (node->underflows()) { // this can happen only at the end assert(it == bids.end()); assert(!parent_bids.empty()); node_type* left_node = m_node_cache.get_node(parent_bids.back().second); assert(left_node); if (left_node->size() + node->size() <= node->max_nelements()) { // can fuse STXXL_VERBOSE1("btree bulk construct fuse last nodes:" << " left_node.size=" << left_node->size() << " node.size=" << node->size()); node->fuse(*left_node); m_node_cache.delete_node(parent_bids.back().second); parent_bids.pop_back(); } else { // need to rebalance STXXL_VERBOSE1("btree bulk construct rebalance last nodes:" << " left_node.size=" << left_node->size() << " node.size=" << node->size()); const key_type new_splitter = node->balance(*left_node, false); parent_bids.back().first = new_splitter; STXXL_VERBOSE1("btree bulk construct after rebalance:" << " left_node.size=" << left_node->size() << " node.size=" << node->size()); assert(!left_node->overflows() && !left_node->underflows()); } } assert(!node->overflows() && !node->underflows()); parent_bids.push_back(key_bid_pair(node->back().first, new_bid)); } STXXL_VERBOSE1("btree parent_bids.size()=" << parent_bids.size() << " bids.size()=" << bids.size()); std::swap(parent_bids, bids); assert(nparents == bids.size() || (nparents - 1) == bids.size()); ++m_height; STXXL_VERBOSE1("Increasing height to " << m_height); if (m_node_cache.size() < (m_height - 1)) { STXXL_THROW2(std::runtime_error, "btree::bulk_construction", "The height of the tree (" << m_height << ") has exceeded the required capacity (" << (m_node_cache.size() + 1) << ") of the node cache. Increase the node cache size."); } } m_root_node.insert(bids.begin(), bids.end()); STXXL_VERBOSE1("btree bulk root_node_.size()=" << m_root_node.size()); } public: btree(unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes) : m_node_cache(node_cache_size_in_bytes, this, m_key_compare), m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), m_iterator_map(this), m_size(0), m_height(2), m_prefetching_enabled(true), m_bm(block_manager::get_instance()) { STXXL_VERBOSE1("Creating a btree, addr=" << this); STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); STXXL_VERBOSE1(" elements in a node: " << node_block_type::size); STXXL_VERBOSE1(" elements in a leaf: " << leaf_block_type::size); STXXL_VERBOSE1(" size of a node element: " << sizeof(typename node_block_type::value_type)); STXXL_VERBOSE1(" size of a leaf element: " << sizeof(typename leaf_block_type::value_type)); create_empty_leaf(); } btree(const key_compare& key_compare, unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes) : m_key_compare(key_compare), m_node_cache(node_cache_size_in_bytes, this, m_key_compare), m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), m_iterator_map(this), m_size(0), m_height(2), m_prefetching_enabled(true), m_bm(block_manager::get_instance()) { STXXL_VERBOSE1("Creating a btree, addr=" << this); STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); create_empty_leaf(); } virtual ~btree() { try { deallocate_children(); } catch (...) { // no exceptions in destructor } } size_type size() const { return m_size; } size_type max_size() const { return std::numeric_limits::max(); } bool empty() const { return !m_size; } std::pair insert(const value_type& x) { root_node_iterator_type it = m_root_node.lower_bound(x.first); assert(!m_root_node.empty()); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Inserting new value into a leaf"); leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); assert(leaf); std::pair splitter; std::pair result = leaf->insert(x, splitter); if (result.second) ++m_size; m_leaf_cache.unfix_node((leaf_bid_type)it->second); //if(key_compare::max_value() == Splitter.first) if (!(m_key_compare(key_compare::max_value(), splitter.first) || m_key_compare(splitter.first, key_compare::max_value()))) return result; // no overflow/splitting happened STXXL_VERBOSE1("Inserting new value into root node"); insert_into_root(std::make_pair(splitter.first, node_bid_type(splitter.second))); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Inserting new value into a node"); node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); assert(node); std::pair splitter; std::pair result = node->insert(x, m_height - 1, splitter); if (result.second) ++m_size; m_node_cache.unfix_node((node_bid_type)it->second); //if(key_compare::max_value() == Splitter.first) if (!(m_key_compare(key_compare::max_value(), splitter.first) || m_key_compare(splitter.first, key_compare::max_value()))) return result; // no overflow/splitting happened STXXL_VERBOSE1("Inserting new value into root node"); insert_into_root(splitter); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } iterator begin() { root_node_iterator_type it = m_root_node.begin(); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("btree: retrieving begin() from the first leaf"); leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second); assert(leaf); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return leaf->begin(); } // 'it' points to a node STXXL_VERBOSE1("btree: retrieving begin() from the first node"); node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); assert(node); iterator result = node->begin(m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } const_iterator begin() const { root_node_const_iterator_type it = m_root_node.begin(); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("btree: retrieving begin() from the first leaf"); const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second); assert(leaf); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return leaf->begin(); } // 'it' points to a node STXXL_VERBOSE1("btree: retrieving begin() from the first node"); const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); assert(node); const_iterator result = node->begin(m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } iterator end() { return m_end_iterator; } const_iterator end() const { return m_end_iterator; } data_type& operator [] (const key_type& k) { return (*((insert(value_type(k, data_type()))).first)).second; } iterator find(const key_type& k) { root_node_iterator_type it = m_root_node.lower_bound(k); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Searching in a leaf"); leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); assert(leaf); iterator result = leaf->find(k); m_leaf_cache.unfix_node((leaf_bid_type)it->second); assert(result == end() || result->first == k); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Searching in a node"); node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); assert(node); iterator result = node->find(k, m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(result == end() || result->first == k); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } const_iterator find(const key_type& k) const { root_node_const_iterator_type it = m_root_node.lower_bound(k); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Searching in a leaf"); const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second, true); assert(leaf); const_iterator result = leaf->find(k); m_leaf_cache.unfix_node((leaf_bid_type)it->second); assert(result == end() || result->first == k); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Searching in a node"); const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); assert(node); const_iterator result = node->find(k, m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(result == end() || result->first == k); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } iterator lower_bound(const key_type& k) { root_node_iterator_type it = m_root_node.lower_bound(k); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Searching lower bound in a leaf"); leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); assert(leaf); iterator result = leaf->lower_bound(k); m_leaf_cache.unfix_node((leaf_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Searching lower bound in a node"); node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); assert(node); iterator result = node->lower_bound(k, m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } const_iterator lower_bound(const key_type& k) const { root_node_const_iterator_type it = m_root_node.lower_bound(k); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Searching lower bound in a leaf"); const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second, true); assert(leaf); const_iterator result = leaf->lower_bound(k); m_leaf_cache.unfix_node((leaf_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Searching lower bound in a node"); const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); assert(node); const_iterator result = node->lower_bound(k, m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } iterator upper_bound(const key_type& k) { root_node_iterator_type it = m_root_node.upper_bound(k); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Searching upper bound in a leaf"); leaf_type* Leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); assert(Leaf); iterator result = Leaf->upper_bound(k); m_leaf_cache.unfix_node((leaf_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Searching upper bound in a node"); node_type* Node = m_node_cache.get_node((node_bid_type)it->second, true); assert(Node); iterator result = Node->upper_bound(k, m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } const_iterator upper_bound(const key_type& k) const { root_node_const_iterator_type it = m_root_node.upper_bound(k); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Searching upper bound in a leaf"); const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second, true); assert(leaf); const_iterator result = leaf->upper_bound(k); m_leaf_cache.unfix_node((leaf_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Searching upper bound in a node"); const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); assert(node); const_iterator result = node->upper_bound(k, m_height - 1); m_node_cache.unfix_node((node_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } std::pair equal_range(const key_type& k) { // l->first >= k iterator l = lower_bound(k); // if (k < l->first) if (l == end() || m_key_compare(k, l->first)) // then upper_bound == lower_bound return std::pair(l, l); iterator u = l; // only one element ==k can exist ++u; assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); // then upper_bound == (lower_bound+1) return std::pair(l, u); } std::pair equal_range(const key_type& k) const { // l->first >= k const_iterator l = lower_bound(k); // if (k < l->first) if (l == end() || m_key_compare(k, l->first)) // then upper_bound == lower_bound return std::pair(l, l); const_iterator u = l; // only one element ==k can exist ++u; assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); // then upper_bound == (lower_bound+1) return std::pair(l, u); } size_type erase(const key_type& k) { root_node_iterator_type it = m_root_node.lower_bound(k); assert(it != m_root_node.end()); if (m_height == 2) // 'it' points to a leaf { STXXL_VERBOSE1("Deleting key from a leaf"); leaf_type* Leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); assert(Leaf); size_type result = Leaf->erase(k); m_size -= result; m_leaf_cache.unfix_node((leaf_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); if ((!Leaf->underflows()) || m_root_node.size() == 1) return result; // no underflow or root has a special degree 1 (too few elements) STXXL_VERBOSE1("btree: Fusing or rebalancing a leaf"); fuse_or_balance(it, m_leaf_cache); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } // 'it' points to a node STXXL_VERBOSE1("Deleting key from a node"); assert(m_root_node.size() >= 2); node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); assert(node); size_type result = node->erase(k, m_height - 1); m_size -= result; m_node_cache.unfix_node((node_bid_type)it->second); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); if (!node->underflows()) return result; // no underflow happened STXXL_VERBOSE1("Fusing or rebalancing a node"); fuse_or_balance(it, m_node_cache); if (m_root_node.size() == 1) { STXXL_VERBOSE1("btree Root has size 1 and height > 2"); STXXL_VERBOSE1("btree Deallocate root and decrease height"); it = m_root_node.begin(); node_bid_type root_bid = it->second; assert(it->first == key_compare::max_value()); node_type* root_node = m_node_cache.get_node(root_bid); assert(root_node); assert(root_node->back().first == key_compare::max_value()); m_root_node.clear(); m_root_node.insert(root_node->block().begin(), root_node->block().begin() + root_node->size()); m_node_cache.delete_node(root_bid); --m_height; STXXL_VERBOSE1("btree Decreasing height to " << m_height); } assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return result; } size_type count(const key_type& k) { if (find(k) == end()) return 0; return 1; } void erase(iterator pos) { assert(pos != end()); #ifndef NDEBUG size_type old_size = size(); #endif erase(pos->first); assert(size() == old_size - 1); } iterator insert(iterator /*pos*/, const value_type& x) { // pos ignored in the current version return insert(x).first; } void clear() { deallocate_children(); m_root_node.clear(); m_size = 0; m_height = 2, create_empty_leaf(); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); } template void insert(InputIterator b, InputIterator e) { while (b != e) { insert(*(b++)); } } template btree(InputIterator begin, InputIterator end, const key_compare& c_, unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes, bool range_sorted = false, double node_fill_factor = 0.75, double leaf_fill_factor = 0.6) : m_key_compare(c_), m_node_cache(node_cache_size_in_bytes, this, m_key_compare), m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), m_iterator_map(this), m_size(0), m_height(2), m_prefetching_enabled(true), m_bm(block_manager::get_instance()) { STXXL_VERBOSE1("Creating a btree, addr=" << this); STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); if (range_sorted == false) { create_empty_leaf(); insert(begin, end); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return; } bulk_construction(begin, end, node_fill_factor, leaf_fill_factor); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); } template btree(InputIterator begin, InputIterator end, unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes, bool range_sorted = false, double node_fill_factor = 0.75, double leaf_fill_factor = 0.6) : m_node_cache(node_cache_size_in_bytes, this, m_key_compare), m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), m_iterator_map(this), m_size(0), m_height(2), m_prefetching_enabled(true), m_bm(block_manager::get_instance()) { STXXL_VERBOSE1("Creating a btree, addr=" << this); STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); if (range_sorted == false) { create_empty_leaf(); insert(begin, end); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); return; } bulk_construction(begin, end, node_fill_factor, leaf_fill_factor); assert(m_leaf_cache.nfixed() == 0); assert(m_node_cache.nfixed() == 0); } void erase(iterator first, iterator last) { if (first == begin() && last == end()) clear(); else while (first != last) erase(first++); } key_compare key_comp() const { return m_key_compare; } value_compare value_comp() const { return value_compare(m_key_compare); } void swap(btree& obj) { std::swap(m_key_compare, obj.m_key_compare); // OK std::swap(m_node_cache, obj.m_node_cache); // OK std::swap(m_leaf_cache, obj.m_leaf_cache); // OK std::swap(m_iterator_map, obj.m_iterator_map); // must update all iterators std::swap(m_end_iterator, obj.m_end_iterator); std::swap(m_size, obj.m_size); std::swap(m_height, obj.m_height); std::swap(m_alloc_strategy, obj.m_alloc_strategy); std::swap(m_root_node, obj.m_root_node); } void enable_prefetching() { m_prefetching_enabled = true; } void disable_prefetching() { m_prefetching_enabled = false; } bool prefetching_enabled() { return m_prefetching_enabled; } void print_statistics(std::ostream& o) const { o << "Node cache statistics:" << std::endl; m_node_cache.print_statistics(o); o << "Leaf cache statistics:" << std::endl; m_leaf_cache.print_statistics(o); } void reset_statistics() { m_node_cache.reset_statistics(); m_leaf_cache.reset_statistics(); } }; template inline bool operator == (const btree& a, const btree& b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } template inline bool operator != (const btree& a, const btree& b) { return !(a == b); } template inline bool operator < (const btree& a, const btree& b) { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } template inline bool operator > (const btree& a, const btree& b) { return b < a; } template inline bool operator <= (const btree& a, const btree& b) { return !(b < a); } template inline bool operator >= (const btree& a, const btree& b) { return !(a < b); } } // namespace btree STXXL_END_NAMESPACE namespace std { template void swap(stxxl::btree::btree& a, stxxl::btree::btree& b) { if (&a != &b) a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_BTREE_BTREE_HEADER stxxl-1.4.1/include/stxxl/bits/containers/btree/iterator.h000644 001411 000144 00000023541 12411366426 023525 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/btree/iterator.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_BTREE_ITERATOR_HEADER #define STXXL_CONTAINERS_BTREE_ITERATOR_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE namespace btree { template class iterator_map; template class btree_iterator; template class btree_const_iterator; template class normal_leaf; template class btree_iterator_base { public: typedef BTreeType btree_type; typedef typename btree_type::leaf_bid_type bid_type; typedef typename btree_type::value_type value_type; typedef typename btree_type::reference reference; typedef typename btree_type::const_reference const_reference; typedef std::bidirectional_iterator_tag iterator_category; typedef typename btree_type::difference_type difference_type; typedef typename btree_type::leaf_type leaf_type; friend class iterator_map; template friend class normal_leaf; template friend bool operator == (const btree_iterator& a, const btree_const_iterator& b); template friend bool operator != (const btree_iterator& a, const btree_const_iterator& b); protected: btree_type* btree; bid_type bid; unsigned_type pos; btree_iterator_base() { STXXL_VERBOSE3("btree_iterator_base def construct addr=" << this); make_invalid(); } btree_iterator_base(btree_type* _btree, const bid_type& _bid, unsigned_type _pos) : btree(_btree), bid(_bid), pos(_pos) { STXXL_VERBOSE3("btree_iterator_base parameter construct addr=" << this); btree->m_iterator_map.register_iterator(*this); } void make_invalid() { btree = NULL; pos = 0; } btree_iterator_base(const btree_iterator_base& obj) { STXXL_VERBOSE3("btree_iterator_base constr from" << (&obj) << " to " << this); btree = obj.btree; bid = obj.bid; pos = obj.pos; if (btree) btree->m_iterator_map.register_iterator(*this); } btree_iterator_base& operator = (const btree_iterator_base& obj) { STXXL_VERBOSE3("btree_iterator_base copy from" << (&obj) << " to " << this); if (&obj != this) { if (btree) btree->m_iterator_map.unregister_iterator(*this); btree = obj.btree; bid = obj.bid; pos = obj.pos; if (btree) btree->m_iterator_map.register_iterator(*this); } return *this; } reference non_const_access() { assert(btree); leaf_type* leaf = btree->m_leaf_cache.get_node(bid); assert(leaf); return (reference)((*leaf)[pos]); } const_reference const_access() const { assert(btree); leaf_type const* leaf = btree->m_leaf_cache.get_const_node(bid); assert(leaf); return (reference)((*leaf)[pos]); } bool operator == (const btree_iterator_base& obj) const { return bid == obj.bid && pos == obj.pos && btree == obj.btree; } bool operator != (const btree_iterator_base& obj) const { return bid != obj.bid || pos != obj.pos || btree != obj.btree; } btree_iterator_base & increment() { assert(btree); bid_type cur_bid = bid; const leaf_type* leaf = btree->m_leaf_cache.get_const_node(bid, true); assert(leaf); leaf->increment_iterator(*this); btree->m_leaf_cache.unfix_node(cur_bid); return *this; } btree_iterator_base & decrement() { assert(btree); bid_type cur_bid = bid; const leaf_type* leaf = btree->m_leaf_cache.get_const_node(bid, true); assert(leaf); leaf->decrement_iterator(*this); btree->m_leaf_cache.unfix_node(cur_bid); return *this; } public: virtual ~btree_iterator_base() { STXXL_VERBOSE3("btree_iterator_base deconst " << this); if (btree) btree->m_iterator_map.unregister_iterator(*this); } }; template class btree_iterator : public btree_iterator_base { public: typedef BTreeType btree_type; typedef typename btree_type::leaf_bid_type bid_type; typedef typename btree_type::value_type value_type; typedef typename btree_type::reference reference; typedef typename btree_type::const_reference const_reference; typedef typename btree_type::pointer pointer; typedef btree_iterator_base base_type; template friend class normal_leaf; using base_type::non_const_access; btree_iterator() : base_type() { } btree_iterator(const btree_iterator& obj) : base_type(obj) { } btree_iterator& operator = (const btree_iterator& obj) { base_type::operator = (obj); return *this; } reference operator * () { return non_const_access(); } pointer operator -> () { return &(non_const_access()); } bool operator == (const btree_iterator& obj) const { return base_type::operator == (obj); } bool operator != (const btree_iterator& obj) const { return base_type::operator != (obj); } btree_iterator& operator ++ () { assert(*this != base_type::btree->end()); base_type::increment(); return *this; } btree_iterator& operator -- () { base_type::decrement(); return *this; } btree_iterator operator ++ (int) { assert(*this != base_type::btree->end()); btree_iterator result(*this); base_type::increment(); return result; } btree_iterator operator -- (int) { btree_iterator result(*this); base_type::decrement(); return result; } private: btree_iterator(btree_type* _btree, const bid_type& _bid, unsigned_type _pos) : base_type(_btree, _bid, _pos) { } }; template class btree_const_iterator : public btree_iterator_base { public: typedef btree_iterator iterator; typedef BTreeType btree_type; typedef typename btree_type::leaf_bid_type bid_type; typedef typename btree_type::value_type value_type; typedef typename btree_type::const_reference reference; typedef typename btree_type::const_pointer pointer; typedef btree_iterator_base base_type; template friend class normal_leaf; using base_type::const_access; btree_const_iterator() : base_type() { } btree_const_iterator(const btree_const_iterator& obj) : base_type(obj) { } btree_const_iterator(const iterator& obj) : base_type(obj) { } btree_const_iterator& operator = (const btree_const_iterator& obj) { base_type::operator = (obj); return *this; } reference operator * () { return const_access(); } pointer operator -> () { return &(const_access()); } bool operator == (const iterator& obj) const { return base_type::operator == (obj); } bool operator != (const iterator& obj) const { return base_type::operator != (obj); } bool operator == (const btree_const_iterator& obj) const { return base_type::operator == (obj); } bool operator != (const btree_const_iterator& obj) const { return base_type::operator != (obj); } btree_const_iterator& operator ++ () { assert(*this != base_type::btree->end()); base_type::increment(); return *this; } btree_const_iterator& operator -- () { base_type::decrement(); return *this; } btree_const_iterator operator ++ (int) { assert(*this != base_type::btree->end()); btree_const_iterator result(*this); base_type::increment(); return result; } btree_const_iterator operator -- (int) { btree_const_iterator result(*this); base_type::decrement(); return result; } private: btree_const_iterator(btree_type* _btree, const bid_type& _bid, unsigned_type _pos) : base_type(_btree, _bid, _pos) { } }; template inline bool operator == (const btree_iterator& a, const btree_const_iterator& b) { return a.btree_iterator_base::operator == (b); } template inline bool operator != (const btree_iterator& a, const btree_const_iterator& b) { return a.btree_iterator_base::operator != (b); } } // namespace btree STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_BTREE_ITERATOR_HEADER stxxl-1.4.1/include/stxxl/bits/containers/btree/iterator_map.h000644 001411 000144 00000011211 12411366426 024351 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/btree/iterator_map.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_BTREE_ITERATOR_MAP_HEADER #define STXXL_CONTAINERS_BTREE_ITERATOR_MAP_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE namespace btree { template class iterator_map : private noncopyable { public: typedef BTreeType btree_type; typedef typename btree_type::leaf_bid_type bid_type; typedef btree_iterator_base iterator_base; private: struct Key { bid_type bid; unsigned_type pos; Key() { } Key(const bid_type& b, unsigned_type p) : bid(b), pos(p) { } }; struct bid_comp { bool operator () (const bid_type& a, const bid_type& b) const { return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset); } }; struct KeyCmp { bid_comp BIDComp; bool operator () (const Key& a, const Key& b) const { return BIDComp(a.bid, b.bid) || (a.bid == b.bid && a.pos < b.pos); } }; typedef std::multimap multimap_type; multimap_type m_it2addr; btree_type* m_btree; typedef typename multimap_type::value_type pair_type; typedef typename multimap_type::iterator mmiterator_type; typedef typename multimap_type::const_iterator mmconst_iterator_type; //! changes btree pointer in all contained iterators void change_btree_pointers(btree_type* b) { for (mmconst_iterator_type it = m_it2addr.begin(); it != m_it2addr.end(); ++it) { (it->second)->btree = b; } } public: iterator_map(btree_type* b) : m_btree(b) { } void register_iterator(iterator_base& it) { STXXL_VERBOSE2("btree::iterator_map register_iterator addr=" << &it << " BID: " << it.bid << " POS: " << it.pos); m_it2addr.insert(pair_type(Key(it.bid, it.pos), &it)); } void unregister_iterator(iterator_base& it) { STXXL_VERBOSE2("btree::iterator_map unregister_iterator addr=" << &it << " BID: " << it.bid << " POS: " << it.pos); assert(!m_it2addr.empty()); Key key(it.bid, it.pos); std::pair range = m_it2addr.equal_range(key); assert(range.first != range.second); mmiterator_type i = range.first; for ( ; i != range.second; ++i) { assert(it.bid == i->first.bid); assert(it.pos == i->first.pos); if (i->second == &it) { // found it m_it2addr.erase(i); return; } } STXXL_THROW2(std::runtime_error, "btree::iterator_map::unregister_iterator", "Panic in btree::iterator_map, can not find and unregister iterator"); } template void find(const bid_type& bid, unsigned_type first_pos, unsigned_type last_pos, OutputContainer& out) { Key firstkey(bid, first_pos); Key lastkey(bid, last_pos); mmconst_iterator_type begin = m_it2addr.lower_bound(firstkey); mmconst_iterator_type end = m_it2addr.upper_bound(lastkey); for (mmconst_iterator_type i = begin; i != end; ++i) { assert(bid == i->first.bid); out.push_back(i->second); } } virtual ~iterator_map() { for (mmconst_iterator_type it = m_it2addr.begin(); it != m_it2addr.end(); ++it) { it->second->make_invalid(); } } void swap(iterator_map& obj) { std::swap(m_it2addr, obj.m_it2addr); change_btree_pointers(m_btree); obj.change_btree_pointers(obj.m_btree); } }; } // namespace btree STXXL_END_NAMESPACE namespace std { template void swap(stxxl::btree::iterator_map& a, stxxl::btree::iterator_map& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_BTREE_ITERATOR_MAP_HEADER stxxl-1.4.1/include/stxxl/bits/containers/btree/root_node.h000644 001411 000144 00000001570 12411303010 023636 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/btree/root_node.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_BTREE_ROOT_NODE_HEADER #define STXXL_CONTAINERS_BTREE_ROOT_NODE_HEADER #include STXXL_BEGIN_NAMESPACE namespace btree { template class root_node { // a place for a future custom root node tree implementation }; } // namespace btree STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_BTREE_ROOT_NODE_HEADER stxxl-1.4.1/include/stxxl/bits/containers/deque.h000644 001411 000144 00000044647 12411366426 021710 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/deque.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_DEQUE_HEADER #define STXXL_CONTAINERS_DEQUE_HEADER #include #include STXXL_BEGIN_NAMESPACE template class deque; template class const_deque_iterator; template class deque_iterator { public: typedef DequeType deque_type; typedef typename deque_type::vector_type vector_type; typedef typename deque_type::value_type value_type; typedef typename deque_type::pointer pointer; typedef typename deque_type::const_pointer const_pointer; typedef typename deque_type::reference reference; typedef typename deque_type::const_reference const_reference; typedef typename deque_type::size_type size_type; typedef typename deque_type::difference_type difference_type; typedef deque_iterator iterator; typedef const_deque_iterator const_iterator; typedef std::random_access_iterator_tag iterator_category; friend class const_deque_iterator; friend class deque; protected: typedef deque_iterator self_type; deque_type* m_deque; size_type m_offset; deque_iterator(deque_type* deque, size_type offset) : m_deque(deque), m_offset(offset) { } public: deque_iterator() : m_deque(NULL), m_offset(0) { } difference_type operator - (const self_type& a) const { size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? m_offset : (m_deque->m_vector.size() + m_offset); size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? a.m_offset : (m_deque->m_vector.size() + a.m_offset); return SelfAbsOffset - aAbsOffset; } difference_type operator - (const const_iterator& a) const { size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? m_offset : (m_deque->m_vector.size() + m_offset); size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? a.m_offset : (m_deque->m_vector.size() + a.m_offset); return SelfAbsOffset - aAbsOffset; } self_type operator - (size_type op) const { return self_type(m_deque, (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size()); } self_type operator + (size_type op) const { return self_type(m_deque, (m_offset + op) % m_deque->m_vector.size()); } self_type& operator -= (size_type op) { m_offset = (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size(); return *this; } self_type& operator += (size_type op) { m_offset = (m_offset + op) % m_deque->m_vector.size(); return *this; } reference operator * () { return m_deque->m_vector[m_offset]; } pointer operator -> () { return &(m_deque->m_vector[m_offset]); } const_reference operator * () const { return m_deque->m_vector[m_offset]; } const_pointer operator -> () const { return &(m_deque->m_vector[m_offset]); } reference operator [] (size_type op) { return m_deque->m_vector[(m_offset + op) % m_deque->m_vector.size()]; } const_reference operator [] (size_type op) const { return m_deque->m_vector[(m_offset + op) % m_deque->m_vector.size()]; } self_type& operator ++ () { m_offset = (m_offset + 1) % m_deque->m_vector.size(); return *this; } self_type operator ++ (int) { self_type tmp = *this; m_offset = (m_offset + 1) % m_deque->m_vector.size(); return tmp; } self_type& operator -- () { m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); return *this; } self_type operator -- (int) { self_type tmp = *this; m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); return tmp; } bool operator == (const self_type& a) const { assert(m_deque == a.m_deque); return m_offset == a.m_offset; } bool operator != (const self_type& a) const { assert(m_deque == a.m_deque); return m_offset != a.m_offset; } bool operator < (const self_type& a) const { assert(m_deque == a.m_deque); return (a - (*this)) > 0; } bool operator > (const self_type& a) const { return a < (*this); } bool operator <= (const self_type& a) const { return !((*this) > a); } bool operator >= (const self_type& a) const { return !((*this) < a); } bool operator == (const const_iterator& a) const { assert(m_deque == a.m_deque); return m_offset == a.m_offset; } bool operator != (const const_iterator& a) const { assert(m_deque == a.m_deque); return m_offset != a.m_offset; } bool operator < (const const_iterator& a) const { assert(m_deque == a.m_deque); return (a - (*this)) > 0; } bool operator > (const const_iterator& a) const { return a < (*this); } bool operator <= (const const_iterator& a) const { return !((*this) > a); } bool operator >= (const const_iterator& a) const { return !((*this) < a); } }; template class const_deque_iterator { public: typedef DequeType deque_type; typedef typename deque_type::vector_type vector_type; typedef typename deque_type::value_type value_type; typedef typename deque_type::const_pointer pointer; typedef typename deque_type::const_pointer const_pointer; typedef typename deque_type::const_reference reference; typedef typename deque_type::const_reference const_reference; typedef typename deque_type::size_type size_type; typedef typename deque_type::difference_type difference_type; typedef deque_iterator iterator; typedef const_deque_iterator const_iterator; typedef std::random_access_iterator_tag iterator_category; friend class deque_iterator; friend class deque; protected: typedef const_deque_iterator self_type; const deque_type* m_deque; size_type m_offset; const_deque_iterator(const deque_type* deque, size_type offset) : m_deque(deque), m_offset(offset) { } public: const_deque_iterator() : m_deque(NULL), m_offset(0) { } const_deque_iterator(const deque_iterator& it) : m_deque(it.m_deque), m_offset(it.m_offset) { } difference_type operator - (const self_type& a) const { size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? m_offset : (m_deque->m_vector.size() + m_offset); size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? a.m_offset : (m_deque->m_vector.size() + a.m_offset); return SelfAbsOffset - aAbsOffset; } difference_type operator - (const iterator& a) const { size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? m_offset : (m_deque->m_vector.size() + m_offset); size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? a.m_offset : (m_deque->m_vector.size() + a.m_offset); return SelfAbsOffset - aAbsOffset; } self_type operator - (size_type op) const { return self_type(m_deque, (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size()); } self_type operator + (size_type op) const { return self_type(m_deque, (m_offset + op) % m_deque->m_vector.size()); } self_type& operator -= (size_type op) { m_offset = (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size(); return *this; } self_type& operator += (size_type op) { m_offset = (m_offset + op) % m_deque->m_vector.size(); return *this; } const_reference operator * () const { return m_deque->m_vector[m_offset]; } const_pointer operator -> () const { return &(m_deque->m_vector[m_offset]); } const_reference operator [] (size_type op) const { return m_deque->m_vector[(m_offset + op) % m_deque->m_vector.size()]; } self_type& operator ++ () { m_offset = (m_offset + 1) % m_deque->m_vector.size(); return *this; } self_type operator ++ (int) { self_type tmp = *this; m_offset = (m_offset + 1) % m_deque->m_vector.size(); return tmp; } self_type& operator -- () { m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); return *this; } self_type operator -- (int) { self_type tmp = *this; m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); return tmp; } bool operator == (const self_type& a) const { assert(m_deque == a.m_deque); return m_offset == a.m_offset; } bool operator != (const self_type& a) const { assert(m_deque == a.m_deque); return m_offset != a.m_offset; } bool operator < (const self_type& a) const { assert(m_deque == a.m_deque); return (a - (*this)) > 0; } bool operator > (const self_type& a) const { return a < (*this); } bool operator <= (const self_type& a) const { return !((*this) > a); } bool operator >= (const self_type& a) const { return !((*this) < a); } bool operator == (const iterator& a) const { assert(m_deque == a.m_deque); return m_offset == a.m_offset; } bool operator != (const iterator& a) const { assert(m_deque == a.m_deque); return m_offset != a.m_offset; } bool operator < (const iterator& a) const { assert(m_deque == a.m_deque); return (a - (*this)) > 0; } bool operator > (const iterator& a) const { return a < (*this); } bool operator <= (const iterator& a) const { return !((*this) > a); } bool operator >= (const iterator& a) const { return !((*this) < a); } }; //! \addtogroup stlcont //! \{ //! A deque container. \n //! Introduction to deque container: see \ref tutorial_deque tutorial. \n //! Design and Internals of deque container: see \ref design_deque //! //! It is an adaptor of the \c VectorType. //! The implementation wraps the elements around //! the end of the \c VectorType circularly. //! \tparam ValueType type of the contained objects (POD with no references to internal memory) //! \tparam VectorType the type of the underlying vector container, //! the default is \c stxxl::vector template > class deque : private noncopyable { typedef deque self_type; public: typedef typename VectorType::size_type size_type; typedef typename VectorType::difference_type difference_type; typedef VectorType vector_type; typedef ValueType value_type; typedef ValueType* pointer; typedef const value_type* const_pointer; typedef ValueType& reference; typedef const ValueType& const_reference; typedef deque_iterator iterator; typedef const_deque_iterator const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; friend class deque_iterator; friend class const_deque_iterator; private: vector_type m_vector; size_type m_begin, m_end, m_size; void double_array() { const size_type old_size = m_vector.size(); m_vector.resize(2 * old_size); if (m_begin > m_end) { // copy data to the new end of the vector const size_type new_begin = old_size + m_begin; std::copy(m_vector.begin() + m_begin, m_vector.begin() + old_size, m_vector.begin() + new_begin); m_begin = new_begin; } } public: //! \name Constructors/Destructors //! \{ deque() : m_vector((STXXL_DEFAULT_BLOCK_SIZE(T)) / sizeof(value_type)), m_begin(0), m_end(0), m_size(0) { } deque(size_type n) : m_vector(STXXL_MAX(STXXL_DEFAULT_BLOCK_SIZE(ValueType) / sizeof(value_type), 2 * n)), m_begin(0), m_end(n), m_size(n) { } ~deque() // empty so far { } //! \} //! \name Iterators //! \{ iterator begin() { return iterator(this, m_begin); } iterator end() { return iterator(this, m_end); } const_iterator begin() const { return const_iterator(this, m_begin); } const_iterator cbegin() const { return begin(); } const_iterator end() const { return const_iterator(this, m_end); } const_iterator cend() const { return end(); } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } //! \} //! \name Capacity //! \{ size_type size() const { return m_size; } size_type max_size() const { return std::numeric_limits::max() / 2 - 1; } bool empty() const { return m_size == 0; } //! \} //! \name Operators //! \{ reference operator [] (size_type n) { assert(n < size()); return m_vector[(m_begin + n) % m_vector.size()]; } const_reference operator [] (size_type n) const { assert(n < size()); return m_vector[(m_begin + n) % m_vector.size()]; } reference front() { assert(!empty()); return m_vector[m_begin]; } const_reference front() const { assert(!empty()); return m_vector[m_begin]; } reference back() { assert(!empty()); return m_vector[(m_end + m_vector.size() - 1) % m_vector.size()]; } const_reference back() const { assert(!empty()); return m_vector[(m_end + m_vector.size() - 1) % m_vector.size()]; } //! \} //! \name Modifiers //! \{ void push_front(const value_type& el) { if ((m_begin + m_vector.size() - 1) % m_vector.size() == m_end) { // an overflow will occur: resize the array double_array(); } m_begin = (m_begin + m_vector.size() - 1) % m_vector.size(); m_vector[m_begin] = el; ++m_size; } void push_back(const value_type& el) { if ((m_end + 1) % m_vector.size() == m_begin) { // an overflow will occur: resize the array double_array(); } m_vector[m_end] = el; m_end = (m_end + 1) % m_vector.size(); ++m_size; } void pop_front() { assert(!empty()); m_begin = (m_begin + 1) % m_vector.size(); --m_size; } void pop_back() { assert(!empty()); m_end = (m_end + m_vector.size() - 1) % m_vector.size(); --m_size; } //! \} //! \name Modifiers //! \{ void swap(deque& obj) { std::swap(m_vector, obj.m_vector); std::swap(m_begin, obj.m_begin); std::swap(m_end, obj.m_end); std::swap(m_size, obj.m_size); } void clear() { m_vector.clear(); m_vector.resize((STXXL_DEFAULT_BLOCK_SIZE(T)) / sizeof(value_type)); m_begin = 0; m_end = 0; m_size = 0; } //! \} //! \name Capacity //! \{ void resize(size_type n) { if (n < size()) { do { pop_back(); } while (n < size()); } else { if (n + 1 > m_vector.size()) { // need to resize const size_type old_size = m_vector.size(); m_vector.resize(2 * n); if (m_begin > m_end) { // copy data to the new end of the vector const size_type new_begin = m_vector.size() - old_size + m_begin; std::copy(m_vector.begin() + m_begin, m_vector.begin() + old_size, m_vector.begin() + new_begin); m_begin = new_begin; } } m_end = (m_end + n - size()) % m_vector.size(); m_size = n; } } //! \} }; template bool operator == (const deque& a, const deque& b) { return std::equal(a.begin(), a.end(), b.begin()); } template bool operator < (const deque& a, const deque& b) { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::deque& a, stxxl::deque& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_DEQUE_HEADER stxxl-1.4.1/include/stxxl/bits/containers/sorter.h000644 001411 000144 00000016717 12411366426 022120 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/sorter.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_SORTER_HEADER #define STXXL_CONTAINERS_SORTER_HEADER #include #include STXXL_BEGIN_NAMESPACE #ifndef STXXL_VERBOSE_SORTER #define STXXL_VERBOSE_SORTER STXXL_VERBOSE2 #endif //! \addtogroup stlcont //! \{ //! External sorter container. \n //! Introduction to sorter container: see \ref tutorial_sorter tutorial. \n //! Design and Internals of sorter container: see \ref design_sorter /** * External Sorter: use stream package objects to keep a sorted container. * * This sorter container combines the two functions of runs_creator and * runs_merger from the stream packages into a two-phase container. * * In the first phase the container is filled with unordered items via push(), * which are presorted internally into runs of size M. When the internal memory * overflows a runs is written to external memory in blocks of block_size. * * When sort() is called the container enters the output phase and push() is * disallowed. After calling sort() the items can be read in sorted order using * operator*() to get the top item, operator++() to advance to the next one and * empty() to check for end of stream. This is exactly the stream interface. * * In the output phase the sorter can be returned to the beginning of the * stream using rewind() and everything is read again in sorted order. * * Using clear() the object can be reset into input state and all items are * destroyed. * * Added in STXXL 1.4 * * \tparam ValueType type of the contained objects (POD with no references to internal memory) * \tparam CompareType type of comparison object used for sorting the runs * \tparam BlockSize size of the external memory block in bytes, default is \c STXXL_DEFAULT_BLOCK_SIZE(ValTp) * \tparam AllocStr parallel disk allocation strategy, default is \c STXXL_DEFAULT_ALLOC_STRATEGY */ template class sorter : private noncopyable { public: // *** Template Parameters typedef ValueType value_type; typedef CompareType cmp_type; enum { block_size = BlockSize }; typedef AllocStrategy alloc_strategy_type; // *** Constructed Types //! runs creator type with push() method typedef stream::runs_creator, cmp_type, block_size, alloc_strategy_type> runs_creator_type; //! corresponding runs merger type typedef stream::runs_merger runs_merger_type; //! size type typedef typename runs_merger_type::size_type size_type; protected: // *** Object Attributes //! current state of sorter enum { STATE_INPUT, STATE_OUTPUT } m_state; //! runs creator object holding all items runs_creator_type m_runs_creator; //! runs merger reading items when in STATE_OUTPUT runs_merger_type m_runs_merger; public: //! \name Constructors //! \{ //! Constructor allocation memory_to_use bytes in ram for sorted runs. sorter(const cmp_type& cmp, unsigned_type memory_to_use) : m_state(STATE_INPUT), m_runs_creator(cmp, memory_to_use), m_runs_merger(cmp, memory_to_use) { } //! Constructor variant with differently sizes runs_creator and runs_merger sorter(const cmp_type& cmp, unsigned_type creator_memory_to_use, unsigned_type merger_memory_to_use) : m_state(STATE_INPUT), m_runs_creator(cmp, creator_memory_to_use), m_runs_merger(cmp, merger_memory_to_use) { } //! \} //! \name Modifiers //! \{ //! Remove all items and return to input state. void clear() { if (m_state == STATE_OUTPUT) m_runs_merger.deallocate(); m_runs_creator.allocate(); m_state = STATE_INPUT; } //! Push another item (only callable during input state). void push(const value_type& val) { assert(m_state == STATE_INPUT); m_runs_creator.push(val); } //! \} //! \name Modus //! \{ //! Finish push input state and deallocate input buffer. void finish() { if (m_state == STATE_OUTPUT) { m_runs_merger.deallocate(); } m_runs_creator.deallocate(); } //! Deallocate buffers and clear result. void finish_clear() { if (m_state == STATE_OUTPUT) { m_runs_merger.deallocate(); m_runs_creator.result()->clear(); } m_runs_creator.deallocate(); } //! \} //! \name Modifiers //! \{ //! Switch to output state, rewind() in case the output was already sorted. void sort() { if (m_state == STATE_OUTPUT) { m_runs_merger.deallocate(); } m_runs_creator.deallocate(); m_runs_merger.initialize(m_runs_creator.result()); m_state = STATE_OUTPUT; } //! Switch to output state, rewind() in case the output was already sorted. void sort(unsigned_type merger_memory_to_use) { m_runs_merger.set_memory_to_use(merger_memory_to_use); sort(); } //! \} //! \name Modus //! \{ //! Switch to output state, rewind() in case the output was already sorted. void sort_reuse() { assert(m_state == STATE_INPUT); m_runs_merger.initialize(m_runs_creator.result()); m_state = STATE_OUTPUT; } //! Rewind output stream to beginning. void rewind() { assert(m_state == STATE_OUTPUT); m_runs_merger.deallocate(); m_state = STATE_INPUT; return sort(); } //! \} //! Change runs_merger memory usage void set_merger_memory_to_use(unsigned_type merger_memory_to_use) { m_runs_merger.set_memory_to_use(merger_memory_to_use); } //! \} //! \name Capacity //! \{ //! Number of items pushed or items remaining to be read. size_type size() const { if (m_state == STATE_INPUT) return m_runs_creator.size(); else return m_runs_merger.size(); } //! Standard stream method bool empty() const { assert(m_state == STATE_OUTPUT); return m_runs_merger.empty(); } //! \} //! \name Operators //! \{ //! Standard stream method const value_type& operator * () const { assert(m_state == STATE_OUTPUT); return *m_runs_merger; } //! Standard stream method const value_type* operator -> () const { return &(operator * ()); } //! Standard stream method (preincrement operator) sorter& operator ++ () { assert(m_state == STATE_OUTPUT); ++m_runs_merger; return *this; } //! \} }; //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_SORTER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/pq_losertree.h000644 001411 000144 00000063170 12414452316 023276 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/pq_losertree.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 1999 Peter Sanders * Copyright (C) 2003, 2004, 2007 Roman Dementiev * Copyright (C) 2007-2009 Johannes Singler * Copyright (C) 2007, 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_PQ_LOSERTREE_HEADER #define STXXL_CONTAINERS_PQ_LOSERTREE_HEADER #include STXXL_BEGIN_NAMESPACE //! \addtogroup stlcontinternals //! //! \{ /*! \internal */ namespace priority_queue_local { ////////////////////////////////////////////////////////////////////// // The data structure from Knuth, "Sorting and Searching", Section 5.4.1 /*! * Loser tree from Knuth, "Sorting and Searching", Section 5.4.1 * \param MaxArity maximum arity of loser tree, has to be a power of two */ template class loser_tree : private noncopyable { public: typedef ValueType value_type; typedef CompareType comparator_type; typedef value_type Element; enum { max_arity = MaxArity }; private: #if STXXL_PQ_INTERNAL_LOSER_TREE struct Entry { value_type key; // Key of Loser element (winner for 0) unsigned_type index; // number of losing segment }; #endif //STXXL_PQ_INTERNAL_LOSER_TREE comparator_type cmp; // stack of free segment indices internal_bounded_stack free_slots; unsigned_type size_; // total number of elements stored unsigned_type logK; // log of current tree size unsigned_type k; // invariant (k == 1 << logK), always a power of two Element sentinel; // target of free segment pointers #if STXXL_PQ_INTERNAL_LOSER_TREE // upper levels of loser trees // entry[0] contains the winner info Entry entry[MaxArity]; #endif //STXXL_PQ_INTERNAL_LOSER_TREE // leaf information // note that Knuth uses indices k..k-1 // while we use 0..k-1 Element* current[MaxArity]; // pointer to current element Element* current_end[MaxArity]; // pointer to end of block for current element Element* segment[MaxArity]; // start of Segments unsigned_type segment_size[MaxArity]; // just to count the internal memory consumption, in bytes unsigned_type mem_cons_; // private member functions unsigned_type initWinner(unsigned_type root); void update_on_insert(unsigned_type node, const Element& newKey, unsigned_type newIndex, Element* winnerKey, unsigned_type* winnerIndex, unsigned_type* mask); void deallocate_segment(unsigned_type slot); void doubleK(); void compactTree(); void rebuildLoserTree(); bool is_segment_empty(unsigned_type slot); void multi_merge_k(Element* target, unsigned_type length); #if STXXL_PQ_INTERNAL_LOSER_TREE template void multi_merge_f(Element* target, unsigned_type length) { //Entry *currentPos; //Element currentKey; //int currentIndex; // leaf pointed to by current entry Element* done = target + length; Entry* regEntry = entry; Element** regStates = current; unsigned_type winnerIndex = regEntry[0].index; Element winnerKey = regEntry[0].key; Element* winnerPos; //Element sup = sentinel; // supremum assert(logK >= LogK); while (target != done) { winnerPos = regStates[winnerIndex]; // write result *target = winnerKey; // advance winner segment ++winnerPos; regStates[winnerIndex] = winnerPos; winnerKey = *winnerPos; // remove winner segment if empty now if (is_sentinel(winnerKey)) { deallocate_segment(winnerIndex); } ++target; // update loser tree #define TreeStep(L) \ if (1 << LogK >= 1 << L) { \ Entry* pos ## L = regEntry + ((winnerIndex + (1 << LogK)) >> ((LogK - L + 1 >= 0) ? (LogK - L + 1) : 0)); \ Element key ## L = pos ## L->key; \ if (cmp(winnerKey, key ## L)) { \ unsigned_type index ## L = pos ## L->index; \ pos ## L->key = winnerKey; \ pos ## L->index = winnerIndex; \ winnerKey = key ## L; \ winnerIndex = index ## L; \ } \ } TreeStep(10); TreeStep(9); TreeStep(8); TreeStep(7); TreeStep(6); TreeStep(5); TreeStep(4); TreeStep(3); TreeStep(2); TreeStep(1); #undef TreeStep } regEntry[0].index = winnerIndex; regEntry[0].key = winnerKey; } #endif //STXXL_PQ_INTERNAL_LOSER_TREE public: bool is_sentinel(const Element& a) { return !(cmp(cmp.min_value(), a)); } bool not_sentinel(const Element& a) { return cmp(cmp.min_value(), a); } public: loser_tree(); ~loser_tree(); void init(); void swap(loser_tree& obj) { std::swap(cmp, obj.cmp); std::swap(free_slots, obj.free_slots); std::swap(size_, obj.size_); std::swap(logK, obj.logK); std::swap(k, obj.k); std::swap(sentinel, obj.sentinel); #if STXXL_PQ_INTERNAL_LOSER_TREE swap_1D_arrays(entry, obj.entry, MaxArity); #endif //STXXL_PQ_INTERNAL_LOSER_TREE swap_1D_arrays(current, obj.current, MaxArity); swap_1D_arrays(current_end, obj.current_end, MaxArity); swap_1D_arrays(segment, obj.segment, MaxArity); swap_1D_arrays(segment_size, obj.segment_size, MaxArity); std::swap(mem_cons_, obj.mem_cons_); } void multi_merge(Element* begin, Element* end) { multi_merge(begin, end - begin); } void multi_merge(Element*, unsigned_type length); unsigned_type mem_cons() const { return mem_cons_; } bool is_space_available() const // for new segment { return (k < MaxArity) || !free_slots.empty(); } //! insert segment beginning at target void insert_segment(Element * target, unsigned_type length); unsigned_type size() const { return size_; } }; ///////////////////////// LoserTree /////////////////////////////////// template loser_tree::loser_tree() : size_(0), logK(0), k(1), mem_cons_(0) { free_slots.push(0); segment[0] = NULL; current[0] = &sentinel; current_end[0] = &sentinel; // entry and sentinel are initialized by init // since they need the value of supremum init(); } template void loser_tree::init() { assert(!cmp(cmp.min_value(), cmp.min_value())); // verify strict weak ordering sentinel = cmp.min_value(); rebuildLoserTree(); #if STXXL_PQ_INTERNAL_LOSER_TREE assert(current[entry[0].index] == &sentinel); #endif //STXXL_PQ_INTERNAL_LOSER_TREE } // rebuild loser tree information from the values in current template void loser_tree::rebuildLoserTree() { #if STXXL_PQ_INTERNAL_LOSER_TREE // MaxArity needs to be a power of two assert(LOG2::floor == LOG2::ceil); unsigned_type winner = initWinner(1); entry[0].index = winner; entry[0].key = *(current[winner]); #endif //STXXL_PQ_INTERNAL_LOSER_TREE } #if STXXL_PQ_INTERNAL_LOSER_TREE // given any values in the leaves this // routing recomputes upper levels of the tree // from scratch in linear time // initialize entry[root].index and the subtree rooted there // return winner index template unsigned_type loser_tree::initWinner(unsigned_type root) { if (root >= k) { // leaf reached return root - k; } else { unsigned_type left = initWinner(2 * root); unsigned_type right = initWinner(2 * root + 1); Element lk = *(current[left]); Element rk = *(current[right]); if (!(cmp(lk, rk))) { // right subtree loses entry[root].index = right; entry[root].key = rk; return left; } else { entry[root].index = left; entry[root].key = lk; return right; } } } // first go up the tree all the way to the root // hand down old winner for the respective subtree // based on new value, and old winner and loser // update each node on the path to the root top down. // This is implemented recursively template void loser_tree::update_on_insert( unsigned_type node, const Element& newKey, unsigned_type newIndex, Element* winnerKey, unsigned_type* winnerIndex, // old winner unsigned_type* mask) // 1 << (ceil(log KNK) - dist-from-root) { if (node == 0) { // winner part of root *mask = (unsigned_type)(1) << (logK - 1); *winnerKey = entry[0].key; *winnerIndex = entry[0].index; if (cmp(entry[node].key, newKey)) { entry[node].key = newKey; entry[node].index = newIndex; } } else { update_on_insert(node >> 1, newKey, newIndex, winnerKey, winnerIndex, mask); Element loserKey = entry[node].key; unsigned_type loserIndex = entry[node].index; if ((*winnerIndex & *mask) != (newIndex & *mask)) { // different subtrees if (cmp(loserKey, newKey)) { // newKey will have influence here if (cmp(*winnerKey, newKey)) { // old winner loses here entry[node].key = *winnerKey; entry[node].index = *winnerIndex; } else { // new entry loses here entry[node].key = newKey; entry[node].index = newIndex; } } *winnerKey = loserKey; *winnerIndex = loserIndex; } // note that nothing needs to be done if // the winner came from the same subtree // a) newKey <= winnerKey => even more reason for the other tree to lose // b) newKey > winnerKey => the old winner will beat the new // entry further down the tree // also the same old winner is handed down the tree *mask >>= 1; // next level } } #endif //STXXL_PQ_INTERNAL_LOSER_TREE // make the tree two times as wide template void loser_tree::doubleK() { STXXL_VERBOSE3("loser_tree::doubleK (before) k=" << k << " logK=" << logK << " MaxArity=" << MaxArity << " #free=" << free_slots.size()); assert(k > 0); assert(k < MaxArity); assert(free_slots.empty()); // stack was free (probably not needed) // make all new entries free // and push them on the free stack for (unsigned_type i = 2 * k - 1; i >= k; i--) // backwards { current[i] = &sentinel; current_end[i] = &sentinel; segment[i] = NULL; free_slots.push(i); } // double the size k *= 2; logK++; STXXL_VERBOSE3("loser_tree::doubleK (after) k=" << k << " logK=" << logK << " MaxArity=" << MaxArity << " #free=" << free_slots.size()); assert(!free_slots.empty()); // recompute loser tree information rebuildLoserTree(); } // compact nonempty segments in the left half of the tree template void loser_tree::compactTree() { STXXL_VERBOSE3("loser_tree::compactTree (before) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); assert(logK > 0); // compact all nonempty segments to the left unsigned_type pos = 0; unsigned_type last_empty = 0; for ( ; pos < k; pos++) { if (not_sentinel(*(current[pos]))) { segment_size[last_empty] = segment_size[pos]; current[last_empty] = current[pos]; current_end[last_empty] = current_end[pos]; segment[last_empty] = segment[pos]; last_empty++; } /* else { if(segment[pos]) { STXXL_VERBOSE2("loser_tree::compactTree() deleting segment "< 1) && ((k / 2) >= last_empty)) { k /= 2; logK--; } // overwrite garbage and compact the stack of free segment indices free_slots.clear(); // none free for ( ; last_empty < k; last_empty++) { current[last_empty] = &sentinel; current_end[last_empty] = &sentinel; free_slots.push(last_empty); } STXXL_VERBOSE3("loser_tree::compactTree (after) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); // recompute loser tree information rebuildLoserTree(); } // insert segment beginning at target // require: is_space_available() == 1 template void loser_tree:: insert_segment(Element* target, unsigned_type length) { STXXL_VERBOSE2("loser_tree::insert_segment(" << target << "," << length << ")"); //std::copy(target,target + length,std::ostream_iterator(std::cout, "\n")); if (length > 0) { assert(not_sentinel(target[0])); assert(not_sentinel(target[length - 1])); assert(is_sentinel(target[length])); // get a free slot if (free_slots.empty()) { // tree is too small doubleK(); } assert(!free_slots.empty()); unsigned_type index = free_slots.top(); free_slots.pop(); // link new segment current[index] = segment[index] = target; current_end[index] = target + length; segment_size[index] = (length + 1) * sizeof(value_type); mem_cons_ += (length + 1) * sizeof(value_type); size_ += length; #if STXXL_PQ_INTERNAL_LOSER_TREE // propagate new information up the tree Element dummyKey; unsigned_type dummyIndex; unsigned_type dummyMask; update_on_insert((index + k) >> 1, *target, index, &dummyKey, &dummyIndex, &dummyMask); #endif //STXXL_PQ_INTERNAL_LOSER_TREE } else { // immediately deallocate // this is not only an optimization // but also needed to keep free segments from // clogging up the tree delete[] target; } } template loser_tree::~loser_tree() { STXXL_VERBOSE1("loser_tree::~loser_tree()"); for (unsigned_type i = 0; i < k; ++i) { if (segment[i]) { STXXL_VERBOSE2("loser_tree::~loser_tree() deleting segment " << i); delete[] segment[i]; mem_cons_ -= segment_size[i]; } } // check whether we have not lost any memory assert(mem_cons_ == 0); } // free an empty segment . template void loser_tree:: deallocate_segment(unsigned_type slot) { // reroute current pointer to some empty sentinel segment // with a sentinel key STXXL_VERBOSE2("loser_tree::deallocate_segment() deleting segment " << slot << " address: " << segment[slot] << " size: " << (segment_size[slot] / sizeof(value_type)) - 1); current[slot] = &sentinel; current_end[slot] = &sentinel; // free memory delete[] segment[slot]; segment[slot] = NULL; mem_cons_ -= segment_size[slot]; // push on the stack of free segment indices free_slots.push(slot); } // delete the length smallest elements and write them to target // empty segments are deallocated // require: // - there are at least length elements // - segments are ended by sentinels template void loser_tree:: multi_merge(Element* target, unsigned_type length) { STXXL_VERBOSE3("loser_tree::multi_merge(target=" << target << ", len=" << length << ") k=" << k); if (length == 0) return; assert(k > 0); assert(length <= size_); //This is the place to make statistics about internal multi_merge calls. #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL priority_queue_local::invert_order inv_cmp(cmp); #endif switch (logK) { case 0: assert(k == 1); #if STXXL_PQ_INTERNAL_LOSER_TREE assert(entry[0].index == 0); #endif //STXXL_PQ_INTERNAL_LOSER_TREE assert(free_slots.empty()); memcpy(target, current[0], length * sizeof(Element)); //std::copy(current[0], current[0] + length, target); current[0] += length; #if STXXL_PQ_INTERNAL_LOSER_TREE entry[0].key = **current; #endif //STXXL_PQ_INTERNAL_LOSER_TREE if (is_segment_empty(0)) deallocate_segment(0); break; case 1: assert(k == 2); #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL { std::pair seqs[2] = { std::make_pair(current[0], current_end[0]), std::make_pair(current[1], current_end[1]) }; parallel::multiway_merge_sentinel(seqs, seqs + 2, target, inv_cmp, length); current[0] = seqs[0].first; current[1] = seqs[1].first; } #else merge_iterator(current[0], current[1], target, length, cmp); rebuildLoserTree(); #endif if (is_segment_empty(0)) deallocate_segment(0); if (is_segment_empty(1)) deallocate_segment(1); break; case 2: assert(k == 4); #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL { std::pair seqs[4] = { std::make_pair(current[0], current_end[0]), std::make_pair(current[1], current_end[1]), std::make_pair(current[2], current_end[2]), std::make_pair(current[3], current_end[3]) }; parallel::multiway_merge_sentinel(seqs, seqs + 4, target, inv_cmp, length); current[0] = seqs[0].first; current[1] = seqs[1].first; current[2] = seqs[2].first; current[3] = seqs[3].first; } #else if (is_segment_empty(3)) merge3_iterator(current[0], current[1], current[2], target, length, cmp); else merge4_iterator(current[0], current[1], current[2], current[3], target, length, cmp); rebuildLoserTree(); #endif if (is_segment_empty(0)) deallocate_segment(0); if (is_segment_empty(1)) deallocate_segment(1); if (is_segment_empty(2)) deallocate_segment(2); if (is_segment_empty(3)) deallocate_segment(3); break; #if !(STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL) case 3: multi_merge_f<3>(target, length); break; case 4: multi_merge_f<4>(target, length); break; case 5: multi_merge_f<5>(target, length); break; case 6: multi_merge_f<6>(target, length); break; case 7: multi_merge_f<7>(target, length); break; case 8: multi_merge_f<8>(target, length); break; case 9: multi_merge_f<9>(target, length); break; case 10: multi_merge_f<10>(target, length); break; #endif default: #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL { std::vector > seqs; std::vector orig_seq_index; for (unsigned int i = 0; i < k; ++i) { if (current[i] != current_end[i] && !is_sentinel(*current[i])) { seqs.push_back(std::make_pair(current[i], current_end[i])); orig_seq_index.push_back(i); } } parallel::multiway_merge_sentinel(seqs.begin(), seqs.end(), target, inv_cmp, length); for (unsigned int i = 0; i < seqs.size(); ++i) { int_type seg = orig_seq_index[i]; current[seg] = seqs[i].first; } for (unsigned int i = 0; i < k; ++i) if (is_segment_empty(i)) { STXXL_VERBOSE3("deallocated " << i); deallocate_segment(i); } } #else multi_merge_k(target, length); #endif break; } size_ -= length; // compact tree if it got considerably smaller { const unsigned_type num_segments_used = k - free_slots.size(); const unsigned_type num_segments_trigger = k - (3 * k / 5); // using k/2 would be worst case inefficient (for large k) // for k \in {2, 4, 8} the trigger is k/2 which is good // because we have special mergers for k \in {1, 2, 4} // there is also a special 3-way-merger, that will be // triggered if k == 4 && is_segment_empty(3) STXXL_VERBOSE3("loser_tree compact? k=" << k << " #used=" << num_segments_used << " <= #trigger=" << num_segments_trigger << " ==> " << ((k > 1 && num_segments_used <= num_segments_trigger) ? "yes" : "no ") << " || " << ((k == 4 && !free_slots.empty() && !is_segment_empty(3)) ? "yes" : "no ") << " #free=" << free_slots.size()); if (k > 1 && ((num_segments_used <= num_segments_trigger) || (k == 4 && !free_slots.empty() && !is_segment_empty(3)))) { compactTree(); } } //std::copy(target,target + length,std::ostream_iterator(std::cout, "\n")); } // is this segment empty and does not point to sentinel yet? template inline bool loser_tree:: is_segment_empty(unsigned_type slot) { return (is_sentinel(*(current[slot])) && (current[slot] != &sentinel)); } #if STXXL_PQ_INTERNAL_LOSER_TREE // multi-merge for arbitrary K template void loser_tree:: multi_merge_k(Element* target, unsigned_type length) { Entry* currentPos; Element currentKey; unsigned_type currentIndex; // leaf pointed to by current entry unsigned_type kReg = k; Element* done = target + length; unsigned_type winnerIndex = entry[0].index; Element winnerKey = entry[0].key; Element* winnerPos; while (target != done) { winnerPos = current[winnerIndex]; // write result *target = winnerKey; // advance winner segment ++winnerPos; current[winnerIndex] = winnerPos; winnerKey = *winnerPos; // remove winner segment if empty now if (is_sentinel(winnerKey)) // deallocate_segment(winnerIndex); // go up the entry-tree for (unsigned_type i = (winnerIndex + kReg) >> 1; i > 0; i >>= 1) { currentPos = entry + i; currentKey = currentPos->key; if (cmp(winnerKey, currentKey)) { currentIndex = currentPos->index; currentPos->key = winnerKey; currentPos->index = winnerIndex; winnerKey = currentKey; winnerIndex = currentIndex; } } ++target; } entry[0].index = winnerIndex; entry[0].key = winnerKey; } #endif // STXXL_PQ_INTERNAL_LOSER_TREE } // namespace priority_queue_local //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_PQ_LOSERTREE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/map.h000644 001411 000144 00000042051 12414452316 021342 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/map.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_MAP_HEADER #define STXXL_CONTAINERS_MAP_HEADER #include #include STXXL_BEGIN_NAMESPACE namespace btree { template class btree; } // namespace btree //! \addtogroup stlcont //! \{ //! External associative container (map). \n //! Introduction to map container: see \ref tutorial_map tutorial. \n //! Design and Internals of map container: see \ref design_map //! //! \tparam KeyType key type (POD with no references to internal memory) //! \tparam DataType data type (POD with no references to internal memory) //! \tparam CompareType comparison type used to determine //! whether a key is smaller than another one. //! If CompareType()(x,y) is true, then x is smaller than y. //! CompareType must also provide a static \c max_value method, that returns //! a value of type KeyType that is //! larger than any key stored in map : i.e. for all \b x in map holds //! CompareType()(x,CompareType::max_value()) //! //!
    //! Example: : //! \verbatim //! struct CmpIntGreater //! { //! bool operator () (const int & a, const int & b) const { return a>b; } //! static int max_value() { return std::numeric_limits::min(); } //! }; //! \endverbatim //! Another example: //! \verbatim //! struct CmpIntLess //! { //! bool operator () (const int & a, const int & b) const { return a::max(); } //! }; //! \endverbatim //! Note that CompareType must define a strict weak ordering. //! (
    see what it is) //! \tparam RawNodeSize size of internal nodes of map in bytes (btree implementation). //! \tparam RawLeafSize size of leaves of map in bytes (btree implementation). //! \tparam PDAllocStrategy parallel disk allocation strategy (\c stxxl::SR is recommended and default) //! template class map : private noncopyable { typedef btree::btree impl_type; impl_type impl; public: typedef typename impl_type::node_block_type node_block_type; typedef typename impl_type::leaf_block_type leaf_block_type; typedef typename impl_type::key_type key_type; typedef typename impl_type::data_type data_type; typedef typename impl_type::data_type mapped_type; typedef typename impl_type::value_type value_type; typedef typename impl_type::key_compare key_compare; typedef typename impl_type::value_compare value_compare; typedef typename impl_type::pointer pointer; typedef typename impl_type::const_pointer const_pointer; typedef typename impl_type::reference reference; typedef typename impl_type::const_reference const_reference; typedef typename impl_type::size_type size_type; typedef typename impl_type::difference_type difference_type; typedef typename impl_type::iterator iterator; typedef typename impl_type::const_iterator const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; //! \name Iterators //! \{ iterator begin() { return impl.begin(); } iterator end() { return impl.end(); } const_iterator begin() const { return impl.begin(); } const_iterator end() const { return impl.end(); } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } //! \} //! \name Capacity //! \{ size_type size() const { return impl.size(); } size_type max_size() const { return impl.max_size(); } bool empty() const { return impl.empty(); } //! \} //! \name Observers //! \{ key_compare key_comp() const { return impl.key_comp(); } value_compare value_comp() const { return impl.value_comp(); } //! \} //! \name Constructors/Destructors //! \{ //! A constructor //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) map(unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes ) : impl(node_cache_size_in_bytes, leaf_cache_size_in_bytes) { } //! A constructor //! \param c_ comparator object //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) map(const key_compare& c_, unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes ) : impl(c_, node_cache_size_in_bytes, leaf_cache_size_in_bytes) { } //! Constructs a map from a given input range //! \param b beginning of the range //! \param e end of the range //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) //! \param range_sorted if \c true than the constructor assumes that the range is sorted //! and performs a fast bottom-up bulk construction of the map (btree implementation) //! \param node_fill_factor node fill factor in [0,1] for bulk construction //! \param leaf_fill_factor leaf fill factor in [0,1] for bulk construction template map(InputIterator b, InputIterator e, unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes, bool range_sorted = false, double node_fill_factor = 0.75, double leaf_fill_factor = 0.6 ) : impl(b, e, node_cache_size_in_bytes, leaf_cache_size_in_bytes, range_sorted, node_fill_factor, leaf_fill_factor) { } //! Constructs a map from a given input range //! \param b beginning of the range //! \param e end of the range //! \param c_ comparator object //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) //! \param range_sorted if \c true than the constructor assumes that the range is sorted //! and performs a fast bottom-up bulk construction of the map (btree implementation) //! \param node_fill_factor node fill factor in [0,1] for bulk construction //! \param leaf_fill_factor leaf fill factor in [0,1] for bulk construction template map(InputIterator b, InputIterator e, const key_compare& c_, unsigned_type node_cache_size_in_bytes, unsigned_type leaf_cache_size_in_bytes, bool range_sorted = false, double node_fill_factor = 0.75, double leaf_fill_factor = 0.6 ) : impl(b, e, c_, node_cache_size_in_bytes, leaf_cache_size_in_bytes, range_sorted, node_fill_factor, leaf_fill_factor) { } //! \} //! \name Modifiers //! \{ void swap(map& obj) { std::swap(impl, obj.impl); } std::pair insert(const value_type& x) { return impl.insert(x); } iterator insert(iterator pos, const value_type& x) { return impl.insert(pos, x); } template void insert(InputIterator b, InputIterator e) { impl.insert(b, e); } void erase(iterator pos) { impl.erase(pos); } size_type erase(const key_type& k) { return impl.erase(k); } void erase(iterator first, iterator last) { impl.erase(first, last); } void clear() { impl.clear(); } //! \} //! \name Operations //! \{ iterator find(const key_type& k) { return impl.find(k); } const_iterator find(const key_type& k) const { return impl.find(k); } size_type count(const key_type& k) { return impl.count(k); } iterator lower_bound(const key_type& k) { return impl.lower_bound(k); } const_iterator lower_bound(const key_type& k) const { return impl.lower_bound(k); } iterator upper_bound(const key_type& k) { return impl.upper_bound(k); } const_iterator upper_bound(const key_type& k) const { return impl.upper_bound(k); } std::pair equal_range(const key_type& k) { return impl.equal_range(k); } std::pair equal_range(const key_type& k) const { return impl.equal_range(k); } //! \} //! \name Operators //! \{ data_type& operator [] (const key_type& k) { return impl[k]; } //! \} //! \name Miscellaneous //! \{ //! Enables leaf prefetching during scanning void enable_prefetching() { impl.enable_prefetching(); } //! Disables leaf prefetching during scanning void disable_prefetching() { impl.disable_prefetching(); } //! Returns the status of leaf prefetching during scanning bool prefetching_enabled() { return impl.prefetching_enabled(); } //! Prints cache statistics void print_statistics(std::ostream& o) const { impl.print_statistics(o); } //! Resets cache statistics void reset_statistics() { impl.reset_statistics(); } //! \} ////////////////////////////////////////////////// template friend bool operator == (const map& a, const map& b); ////////////////////////////////////////////////// template friend bool operator < (const map& a, const map& b); ////////////////////////////////////////////////// template friend bool operator > (const map& a, const map& b); ////////////////////////////////////////////////// template friend bool operator != (const map& a, const map& b); ////////////////////////////////////////////////// template friend bool operator <= (const map& a, const map& b); ////////////////////////////////////////////////// template friend bool operator >= (const map& a, const map& b); ////////////////////////////////////////////////// }; template inline bool operator == (const map& a, const map& b) { return a.impl == b.impl; } template inline bool operator < (const map& a, const map& b) { return a.impl < b.impl; } template inline bool operator > (const map& a, const map& b) { return a.impl > b.impl; } template inline bool operator != (const map& a, const map& b) { return a.impl != b.impl; } template inline bool operator <= (const map& a, const map& b) { return a.impl <= b.impl; } template inline bool operator >= (const map& a, const map& b) { return a.impl >= b.impl; } //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::map& a, stxxl::map& b ) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_MAP_HEADER stxxl-1.4.1/include/stxxl/bits/containers/vector.h000644 001411 000144 00000240515 12414452316 022074 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/vector.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2008 Roman Dementiev * Copyright (C) 2007-2009 Johannes Singler * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_VECTOR_HEADER #define STXXL_CONTAINERS_VECTOR_HEADER #include #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE #define STXXL_VERBOSE_VECTOR(msg) STXXL_VERBOSE1("vector[" << static_cast(this) << "]::" << msg) //! \defgroup stlcont Containers //! \ingroup stllayer //! Containers with STL-compatible interface //! \defgroup stlcont_vector vector //! \ingroup stlcont //! Vector and support classes //! \{ template class double_blocked_index { typedef SizeType size_type; static const size_type modulo12 = modulo1 * modulo2; size_type pos; unsigned_type block1, block2, offset; //! \invariant block2 * modulo12 + block1 * modulo1 + offset == pos && 0 <= block1 < modulo2 && 0 <= offset < modulo1 void set(size_type pos) { this->pos = pos; block2 = (int_type)(pos / modulo12); pos -= block2 * modulo12; block1 = (int_type)(pos / modulo1); offset = (int_type)(pos - block1 * modulo1); assert(block2 * modulo12 + block1 * modulo1 + offset == this->pos); assert(/* 0 <= block1 && */ block1 < modulo2); assert(/* 0 <= offset && */ offset < modulo1); } public: double_blocked_index() { set(0); } double_blocked_index(size_type pos) { set(pos); } double_blocked_index(unsigned_type block2, unsigned_type block1, unsigned_type offset) { assert(/* 0 <= block1 && */ block1 < modulo2); assert(/* 0 <= offset && */ offset < modulo1); this->block2 = block2; this->block1 = block1; this->offset = offset; pos = block2 * modulo12 + block1 * modulo1 + offset; } double_blocked_index& operator = (size_type pos) { set(pos); return *this; } //pre-increment operator double_blocked_index& operator ++ () { ++pos; ++offset; if (offset == modulo1) { offset = 0; ++block1; if (block1 == modulo2) { block1 = 0; ++block2; } } assert(block2 * modulo12 + block1 * modulo1 + offset == this->pos); assert(/* 0 <= block1 && */ block1 < modulo2); assert(/* 0 <= offset && */ offset < modulo1); return *this; } //post-increment operator double_blocked_index operator ++ (int) { double_blocked_index former(*this); operator ++ (); return former; } //pre-increment operator double_blocked_index& operator -- () { --pos; if (offset == 0) { offset = modulo1; if (block1 == 0) { block1 = modulo2; --block2; } --block1; } --offset; assert(block2 * modulo12 + block1 * modulo1 + offset == this->pos); assert(/*0 <= block1 &&*/ block1 < modulo2); assert(/*0 <= offset &&*/ offset < modulo1); return *this; } //post-increment operator double_blocked_index operator -- (int) { double_blocked_index former(*this); operator -- (); return former; } double_blocked_index operator + (size_type addend) const { return double_blocked_index(pos + addend); } double_blocked_index& operator += (size_type addend) { set(pos + addend); return *this; } double_blocked_index operator - (size_type addend) const { return double_blocked_index(pos - addend); } size_type operator - (const double_blocked_index& dbi2) const { return pos - dbi2.pos; } double_blocked_index& operator -= (size_type subtrahend) { set(pos - subtrahend); return *this; } bool operator == (const double_blocked_index& dbi2) const { return pos == dbi2.pos; } bool operator != (const double_blocked_index& dbi2) const { return pos != dbi2.pos; } bool operator < (const double_blocked_index& dbi2) const { return pos < dbi2.pos; } bool operator <= (const double_blocked_index& dbi2) const { return pos <= dbi2.pos; } bool operator > (const double_blocked_index& dbi2) const { return pos > dbi2.pos; } bool operator >= (const double_blocked_index& dbi2) const { return pos >= dbi2.pos; } double_blocked_index& operator >>= (size_type shift) { set(pos >> shift); return *this; } size_type get_pos() const { return pos; } const unsigned_type & get_block2() const { return block2; } const unsigned_type & get_block1() const { return block1; } const unsigned_type & get_offset() const { return offset; } }; //////////////////////////////////////////////////////////////////////////// template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> class vector; template class const_vector_iterator; template class vector_bufreader; template class vector_bufreader_reverse; template class vector_bufwriter; //////////////////////////////////////////////////////////////////////////// //! External vector iterator, model of \c ext_random_access_iterator concept. template class vector_iterator { typedef vector_iterator self_type; typedef const_vector_iterator const_self_type; friend class const_vector_iterator; public: //! \name Types //! \{ typedef self_type iterator; typedef const_self_type const_iterator; typedef unsigned block_offset_type; typedef vector vector_type; friend class vector; typedef typename vector_type::bids_container_type bids_container_type; typedef typename bids_container_type::iterator bids_container_iterator; typedef typename bids_container_type::bid_type bid_type; typedef typename vector_type::block_type block_type; typedef typename vector_type::blocked_index_type blocked_index_type; typedef std::random_access_iterator_tag iterator_category; typedef typename vector_type::size_type size_type; typedef typename vector_type::difference_type difference_type; typedef typename vector_type::value_type value_type; typedef typename vector_type::reference reference; typedef typename vector_type::const_reference const_reference; typedef typename vector_type::pointer pointer; typedef typename vector_type::const_pointer const_pointer; //! \} protected: blocked_index_type offset; vector_type* p_vector; private: //! private constructor for initializing other iterators vector_iterator(vector_type* v, size_type o) : offset(o), p_vector(v) { } public: //! constructs invalid iterator vector_iterator() : offset(0), p_vector(NULL) { } //! copy-constructor vector_iterator(const self_type& a) : offset(a.offset), p_vector(a.p_vector) { } //! \name Iterator Properties //! \{ //! return pointer to vector containing iterator vector_type * parent_vector() const { return p_vector; } //! return block offset of current element block_offset_type block_offset() const { return static_cast(offset.get_offset()); } //! return iterator to BID containg current element bids_container_iterator bid() const { return p_vector->bid(offset); } //! \} //! \name Access Operators //! \{ //! return current element reference operator * () { return p_vector->element(offset); } //! return pointer to current element pointer operator -> () { return &(p_vector->element(offset)); } //! return const reference to current element const_reference operator * () const { return p_vector->const_element(offset); } //! return const pointer to current element const_pointer operator -> () const { return &(p_vector->const_element(offset)); } //! return mutable reference to element +i after the current element reference operator [] (size_type i) { return p_vector->element(offset.get_pos() + i); } #ifdef _LIBCPP_VERSION //-tb 2013-11: libc++ defines std::reverse_iterator::operator[] in such a // way that it expects vector_iterator::operator[] to return a (mutable) // reference. Thus to remove confusion about the compiler error, we remove // the operator[] const for libc++. The const_reference actually violates // some version of the STL standard, but works well in gcc's libstdc++. #else //! return const reference to element +i after the current element const_reference operator [] (size_type i) const { return p_vector->const_element(offset.get_pos() + i); } #endif //! \} //! \name Relative Calculation of Iterators //! \{ //! calculate different between two iterator difference_type operator - (const self_type& a) const { return offset - a.offset; } //! calculate different between two iterator difference_type operator - (const const_self_type& a) const { return offset - a.offset; } //! return iterator advanced -i positions in the vector self_type operator - (size_type i) const { return self_type(p_vector, offset.get_pos() - i); } //! return iterator advanced +i positions in the vector self_type operator + (size_type i) const { return self_type(p_vector, offset.get_pos() + i); } //! advance this iterator -i positions in the vector self_type& operator -= (size_type i) { offset -= i; return *this; } //! advance this iterator +i positions in the vector self_type& operator += (size_type i) { offset += i; return *this; } //! advance this iterator to next position in the vector self_type& operator ++ () { offset++; return *this; } //! advance this iterator to next position in the vector self_type operator ++ (int) { self_type tmp = *this; offset++; return tmp; } //! advance this iterator to preceding position in the vector self_type& operator -- () { offset--; return *this; } //! advance this iterator to preceding position in the vector self_type operator -- (int) { self_type tmp = *this; offset--; return tmp; } //! \} //! \name Comparison Operators //! \{ bool operator == (const self_type& a) const { assert(p_vector == a.p_vector); return offset == a.offset; } bool operator != (const self_type& a) const { assert(p_vector == a.p_vector); return offset != a.offset; } bool operator < (const self_type& a) const { assert(p_vector == a.p_vector); return offset < a.offset; } bool operator <= (const self_type& a) const { assert(p_vector == a.p_vector); return offset <= a.offset; } bool operator > (const self_type& a) const { assert(p_vector == a.p_vector); return offset > a.offset; } bool operator >= (const self_type& a) const { assert(p_vector == a.p_vector); return offset >= a.offset; } bool operator == (const const_self_type& a) const { assert(p_vector == a.p_vector); return offset == a.offset; } bool operator != (const const_self_type& a) const { assert(p_vector == a.p_vector); return offset != a.offset; } bool operator < (const const_self_type& a) const { assert(p_vector == a.p_vector); return offset < a.offset; } bool operator <= (const const_self_type& a) const { assert(p_vector == a.p_vector); return offset <= a.offset; } bool operator > (const const_self_type& a) const { assert(p_vector == a.p_vector); return offset > a.offset; } bool operator >= (const const_self_type& a) const { assert(p_vector == a.p_vector); return offset >= a.offset; } //! \} //! \name Flushing Operation //! \{ void block_externally_updated() { p_vector->block_externally_updated(offset); } void flush() { p_vector->flush(); } //! \} }; //////////////////////////////////////////////////////////////////////////// //! Const external vector iterator, model of \c ext_random_access_iterator concept. template class const_vector_iterator { typedef const_vector_iterator self_type; typedef vector_iterator mutable_self_type; friend class vector_iterator; public: //! \name Types //! \{ typedef self_type const_iterator; typedef mutable_self_type iterator; typedef unsigned block_offset_type; typedef vector vector_type; friend class vector; typedef typename vector_type::bids_container_type bids_container_type; typedef typename bids_container_type::iterator bids_container_iterator; typedef typename bids_container_type::bid_type bid_type; typedef typename vector_type::block_type block_type; typedef typename vector_type::blocked_index_type blocked_index_type; typedef std::random_access_iterator_tag iterator_category; typedef typename vector_type::size_type size_type; typedef typename vector_type::difference_type difference_type; typedef typename vector_type::value_type value_type; typedef typename vector_type::const_reference reference; typedef typename vector_type::const_reference const_reference; typedef typename vector_type::const_pointer pointer; typedef typename vector_type::const_pointer const_pointer; //! \} protected: blocked_index_type offset; const vector_type* p_vector; private: //! private constructor for initializing other iterators const_vector_iterator(const vector_type* v, size_type o) : offset(o), p_vector(v) { } public: //! constructs invalid iterator const_vector_iterator() : offset(0), p_vector(NULL) { } //! copy-constructor const_vector_iterator(const self_type& a) : offset(a.offset), p_vector(a.p_vector) { } //! copy-constructor from mutable iterator const_vector_iterator(const mutable_self_type& a) : offset(a.offset), p_vector(a.p_vector) { } //! \name Iterator Properties //! \{ //! return pointer to vector containing iterator const vector_type * parent_vector() const { return p_vector; } //! return block offset of current element block_offset_type block_offset() const { return static_cast(offset.get_offset()); } //! return iterator to BID containg current element bids_container_iterator bid() const { return ((vector_type*)p_vector)->bid(offset); } //! \} //! \name Access Operators //! \{ //! return current element const_reference operator * () const { return p_vector->const_element(offset); } //! return pointer to current element const_pointer operator -> () const { return &(p_vector->const_element(offset)); } //! return const reference to element +i after the current element const_reference operator [] (size_type i) const { return p_vector->const_element(offset.get_pos() + i); } //! \} //! \name Relative Calculation of Iterators //! \{ //! calculate different between two iterator difference_type operator - (const self_type& a) const { return offset - a.offset; } //! calculate different between two iterator difference_type operator - (const mutable_self_type& a) const { return offset - a.offset; } //! return iterator advanced -i positions in the vector self_type operator - (size_type i) const { return self_type(p_vector, offset.get_pos() - i); } //! return iterator advanced +i positions in the vector self_type operator + (size_type i) const { return self_type(p_vector, offset.get_pos() + i); } //! advance this iterator -i positions in the vector self_type& operator -= (size_type i) { offset -= i; return *this; } //! advance this iterator +i positions in the vector self_type& operator += (size_type i) { offset += i; return *this; } //! advance this iterator to next position in the vector self_type& operator ++ () { offset++; return *this; } //! advance this iterator to next position in the vector self_type operator ++ (int) { self_type tmp_ = *this; offset++; return tmp_; } //! advance this iterator to preceding position in the vector self_type& operator -- () { offset--; return *this; } //! advance this iterator to preceding position in the vector self_type operator -- (int) { self_type tmp = *this; offset--; return tmp; } //! \} //! \name Comparison Operators //! \{ bool operator == (const self_type& a) const { assert(p_vector == a.p_vector); return offset == a.offset; } bool operator != (const self_type& a) const { assert(p_vector == a.p_vector); return offset != a.offset; } bool operator < (const self_type& a) const { assert(p_vector == a.p_vector); return offset < a.offset; } bool operator <= (const self_type& a) const { assert(p_vector == a.p_vector); return offset <= a.offset; } bool operator > (const self_type& a) const { assert(p_vector == a.p_vector); return offset > a.offset; } bool operator >= (const self_type& a) const { assert(p_vector == a.p_vector); return offset >= a.offset; } bool operator == (const mutable_self_type& a) const { assert(p_vector == a.p_vector); return offset == a.offset; } bool operator != (const mutable_self_type& a) const { assert(p_vector == a.p_vector); return offset != a.offset; } bool operator < (const mutable_self_type& a) const { assert(p_vector == a.p_vector); return offset < a.offset; } bool operator <= (const mutable_self_type& a) const { assert(p_vector == a.p_vector); return offset <= a.offset; } bool operator > (const mutable_self_type& a) const { assert(p_vector == a.p_vector); return offset > a.offset; } bool operator >= (const mutable_self_type& a) const { assert(p_vector == a.p_vector); return offset >= a.offset; } //! \} //! \name Flushing Operation //! \{ void block_externally_updated() { p_vector->block_externally_updated(offset); } void flush() { p_vector->flush(); } //! \} }; //////////////////////////////////////////////////////////////////////////// //! External vector container. \n //! Introduction to vector container: see \ref tutorial_vector tutorial. \n //! Design and Internals of vector container: see \ref design_vector //! //! For semantics of the methods see documentation of the STL std::vector //! \tparam ValueType type of contained objects (POD with no references to internal memory) //! \tparam PageSize number of blocks in a page //! \tparam PagerType pager type, \c random_pager or \c lru_pager, where x is the default number of pages, //! default is \c lru_pager<8> //! \tparam BlockSize external block size in bytes, default is 2 MiB //! \tparam AllocStr one of allocation strategies: \c striping , \c RC , \c SR , or \c FR //! default is RC //! //! Memory consumption: BlockSize*x*PageSize bytes //! \warning Do not store references to the elements of an external vector. Such references //! might be invalidated during any following access to elements of the vector template < typename ValueType, unsigned PageSize = 4, typename PagerType = lru_pager<8>, unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), typename AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY, typename SizeType = stxxl::uint64 // will be deprecated soon > class vector { public: //! \name Standard Types //! \{ //! The type of elements stored in the vector. typedef ValueType value_type; //! reference to value_type typedef value_type& reference; //! constant reference to value_type typedef const value_type& const_reference; //! pointer to value_type typedef value_type* pointer; //! constant pointer to value_type typedef const value_type* const_pointer; //! an unsigned 64-bit integral type typedef SizeType size_type; typedef stxxl::int64 difference_type; typedef PagerType pager_type; typedef AllocStr alloc_strategy_type; enum constants { block_size = BlockSize, page_size = PageSize, on_disk = -1 }; //! iterator used to iterate through a vector, see \ref design_vector_notes. typedef vector_iterator iterator; friend class vector_iterator; //! constant iterator used to iterate through a vector, see \ref design_vector_notes. typedef const_vector_iterator const_iterator; friend class const_vector_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; //! \} //! \name Extra Types //! \{ //! vector_bufwriter compatible with this vector typedef vector_bufwriter bufwriter_type; //! vector_bufreader compatible with this vector typedef vector_bufreader bufreader_type; //! vector_bufreader compatible with this vector typedef vector_bufreader_reverse bufreader_reverse_type; //! \internal class bid_vector : public std::vector > { public: typedef std::vector > super_type; typedef typename super_type::size_type size_type; typedef typename super_type::value_type bid_type; bid_vector(size_type sz) : super_type(sz) { } }; typedef bid_vector bids_container_type; typedef typename bids_container_type::iterator bids_container_iterator; typedef typename bids_container_type::const_iterator const_bids_container_iterator; //! type of the block used in disk-memory transfers typedef typed_block block_type; //! double-index type to reference individual elements in a block typedef double_blocked_index blocked_index_type; //! \} private: alloc_strategy_type m_alloc_strategy; size_type m_size; bids_container_type m_bids; mutable pager_type m_pager; // enum specifying status of a page of the vector enum { valid_on_disk = 0, uninitialized = 1, dirty = 2 }; //! status of each page (valid_on_disk, uninitialized or dirty) mutable std::vector m_page_status; mutable std::vector m_page_to_slot; mutable simple_vector m_slot_to_page; mutable std::queue m_free_slots; mutable simple_vector* m_cache; file* m_from; block_manager* m_bm; bool m_exported; size_type size_from_file_length(stxxl::uint64 file_length) const { stxxl::uint64 blocks_fit = file_length / stxxl::uint64(block_type::raw_size); size_type cur_size = blocks_fit * stxxl::uint64(block_type::size); stxxl::uint64 rest = file_length - blocks_fit * stxxl::uint64(block_type::raw_size); return (cur_size + rest / stxxl::uint64(sizeof(value_type))); } stxxl::uint64 file_length() const { typedef stxxl::uint64 file_size_type; size_type cur_size = size(); size_type num_full_blocks = cur_size / block_type::size; if (cur_size % block_type::size != 0) { size_type rest = cur_size - num_full_blocks * block_type::size; return file_size_type(num_full_blocks) * block_type::raw_size + rest * sizeof(value_type); } return file_size_type(num_full_blocks) * block_type::raw_size; } public: //! \name Constructors/Destructors //! \{ //! Constructs external vector with n elements. //! //! \param n Number of elements. //! \param npages Number of cached pages. vector(size_type n = 0, unsigned_type npages = pager_type().size()) : m_size(n), m_bids((size_t)div_ceil(n, block_type::size)), m_pager(npages), m_page_status(div_ceil(m_bids.size(), page_size)), m_page_to_slot(div_ceil(m_bids.size(), page_size)), m_slot_to_page(npages), m_cache(NULL), m_from(NULL), m_exported(false) { m_bm = block_manager::get_instance(); allocate_page_cache(); for (size_t i = 0; i < m_page_status.size(); ++i) { m_page_status[i] = uninitialized; m_page_to_slot[i] = on_disk; } for (unsigned_type i = 0; i < numpages(); ++i) m_free_slots.push(i); m_bm->new_blocks(m_alloc_strategy, m_bids.begin(), m_bids.end(), 0); } //! \} //! \name Modifier //! \{ //! swap content void swap(vector& obj) { std::swap(m_alloc_strategy, obj.m_alloc_strategy); std::swap(m_size, obj.m_size); std::swap(m_bids, obj.m_bids); std::swap(m_pager, obj.m_pager); std::swap(m_page_status, obj.m_page_status); std::swap(m_page_to_slot, obj.m_page_to_slot); std::swap(m_slot_to_page, obj.m_slot_to_page); std::swap(m_free_slots, obj.m_free_slots); std::swap(m_cache, obj.m_cache); std::swap(m_from, obj.m_from); std::swap(m_exported, obj.m_exported); } //! \} //! \name Miscellaneous //! \{ //! Allocate page cache, must be called to allow access to elements. void allocate_page_cache() const { // numpages() might be zero if (!m_cache && numpages() > 0) m_cache = new simple_vector(numpages() * page_size); } //! allows to free the cache, but you may not access any element until call //! allocate_page_cache() again void deallocate_page_cache() const { flush(); delete m_cache; m_cache = NULL; } //! \name Size and Capacity //! \{ //! return the size of the vector. size_type size() const { return m_size; } //! true if the vector's size is zero. bool empty() const { return (!m_size); } //! Return the number of elelemtsn for which \a external memory has been //! allocated. capacity() is always greator than or equal to size(). size_type capacity() const { return size_type(m_bids.size()) * block_type::size; } //! Returns the number of bytes that the vector has allocated on disks. size_type raw_capacity() const { return size_type(m_bids.size()) * block_type::raw_size; } /*! Reserves at least n elements in external memory. * * If n is less than or equal to capacity(), this call has no * effect. Otherwise, it is a request for allocation of additional \b * external memory. If the request is successful, then capacity() is * greater than or equal to n; otherwise capacity() is unchanged. In either * case, size() is unchanged. */ void reserve(size_type n) { if (n <= capacity()) return; unsigned_type old_bids_size = m_bids.size(); unsigned_type new_bids_size = (unsigned_type)div_ceil(n, block_type::size); unsigned_type new_pages = div_ceil(new_bids_size, page_size); m_page_status.resize(new_pages, uninitialized); m_page_to_slot.resize(new_pages, on_disk); m_bids.resize(new_bids_size); if (m_from == NULL) { m_bm->new_blocks(m_alloc_strategy, m_bids.begin() + old_bids_size, m_bids.end(), old_bids_size); } else { size_type offset = size_type(old_bids_size) * size_type(block_type::raw_size); for (bids_container_iterator it = m_bids.begin() + old_bids_size; it != m_bids.end(); ++it, offset += size_type(block_type::raw_size)) { (*it).storage = m_from; (*it).offset = offset; } STXXL_VERBOSE_VECTOR("reserve(): Changing size of file " << ((void*)m_from) << " to " << offset); m_from->set_size(offset); } } //! Resize vector contents to n items. //! \warning this will not call the constructor of objects in external memory! void resize(size_type n) { _resize(n); } //! Resize vector contents to n items, and allow the allocated external //! memory to shrink. Internal memory allocation remains unchanged. //! \warning this will not call the constructor of objects in external memory! void resize(size_type n, bool shrink_capacity) { if (shrink_capacity) _resize_shrink_capacity(n); else _resize(n); } //! \} private: //! Resize vector, only allow capacity growth. void _resize(size_type n) { reserve(n); if (n < m_size) { // mark excess pages as uninitialized and evict them from cache unsigned_type first_page_to_evict = (unsigned_type)div_ceil(n, block_type::size * page_size); for (size_t i = first_page_to_evict; i < m_page_status.size(); ++i) { if (m_page_to_slot[i] != on_disk) { m_free_slots.push(m_page_to_slot[i]); m_page_to_slot[i] = on_disk; } m_page_status[i] = uninitialized; } } m_size = n; } //! Resize vector, also allow reduction of external memory capacity. void _resize_shrink_capacity(size_type n) { unsigned_type old_bids_size = m_bids.size(); unsigned_type new_bids_size = (unsigned_type)div_ceil(n, block_type::size); if (new_bids_size > old_bids_size) { reserve(n); } else if (new_bids_size < old_bids_size) { unsigned_type new_pages_size = div_ceil(new_bids_size, page_size); STXXL_VERBOSE_VECTOR("shrinking from " << old_bids_size << " to " << new_bids_size << " blocks = from " << m_page_status.size() << " to " << new_pages_size << " pages"); // release blocks if (m_from != NULL) m_from->set_size(new_bids_size * block_type::raw_size); else m_bm->delete_blocks(m_bids.begin() + old_bids_size, m_bids.end()); m_bids.resize(new_bids_size); // don't resize m_page_to_slot or m_page_status, because it is // still needed to check page status and match the mapping // m_slot_to_page // clear dirty flag, so these pages will be never written std::fill(m_page_status.begin() + new_pages_size, m_page_status.end(), (unsigned char)valid_on_disk); } m_size = n; } public: //! \name Modifiers //! \{ //! Erases all of the elements and deallocates all external memory that is //! occupied. void clear() { m_size = 0; if (m_from == NULL) m_bm->delete_blocks(m_bids.begin(), m_bids.end()); m_bids.clear(); m_page_status.clear(); m_page_to_slot.clear(); while (!m_free_slots.empty()) m_free_slots.pop(); for (unsigned_type i = 0; i < numpages(); ++i) m_free_slots.push(i); } //! \name Front and Back Access //! \{ //! Append a new element at the end. void push_back(const_reference obj) { size_type old_size = m_size; resize(old_size + 1); element(old_size) = obj; } //! Removes the last element (without returning it, see back()). void pop_back() { resize(m_size - 1); } //! \} //! \name Operators //! \{ //! Returns a reference to the last element, see \ref design_vector_notes. reference back() { return element(m_size - 1); } //! Returns a reference to the first element, see \ref design_vector_notes. reference front() { return element(0); } //! Returns a constant reference to the last element, see \ref design_vector_notes. const_reference back() const { return const_element(m_size - 1); } //! Returns a constant reference to the first element, see \ref design_vector_notes. const_reference front() const { return const_element(0); } //! \} //! \name Constructors/Destructors //! \{ //! Construct vector from a file. //! \param from file to be constructed from //! \param size Number of elements. //! \param npages Number of cached pages. //! \warning Only one \c vector can be assigned to a particular (physical) file. //! The block size of the vector must be a multiple of the element size //! \c sizeof(ValueType) and the page size (4096). vector(file* from, size_type size = size_type(-1), unsigned_type npages = pager_type().size()) : m_size((size == size_type(-1)) ? size_from_file_length(from->size()) : size), m_bids((size_t)div_ceil(m_size, size_type(block_type::size))), m_pager(npages), m_page_status(div_ceil(m_bids.size(), page_size)), m_page_to_slot(div_ceil(m_bids.size(), page_size)), m_slot_to_page(npages), m_cache(NULL), m_from(from), m_exported(false) { // initialize from file if (!block_type::has_only_data) { std::ostringstream str; str << "The block size for a vector that is mapped to a file must be a multiple of the element size (" << sizeof(value_type) << ") and the page size (4096)."; throw std::runtime_error(str.str()); } m_bm = block_manager::get_instance(); allocate_page_cache(); for (size_t i = 0; i < m_page_status.size(); ++i) { m_page_status[i] = valid_on_disk; m_page_to_slot[i] = on_disk; } for (unsigned_type i = 0; i < numpages(); ++i) m_free_slots.push(i); // allocate blocks equidistantly and in-order size_type offset = 0; for (bids_container_iterator it = m_bids.begin(); it != m_bids.end(); ++it, offset += size_type(block_type::raw_size)) { (*it).storage = from; (*it).offset = offset; } from->set_size(offset); } //! copy-constructor vector(const vector& obj) : m_size(obj.size()), m_bids((size_t)div_ceil(obj.size(), block_type::size)), m_pager(obj.numpages()), m_page_status(div_ceil(m_bids.size(), page_size)), m_page_to_slot(div_ceil(m_bids.size(), page_size)), m_slot_to_page(obj.numpages()), m_cache(NULL), m_from(NULL), m_exported(false) { assert(!obj.m_exported); m_bm = block_manager::get_instance(); allocate_page_cache(); for (size_t i = 0; i < m_page_status.size(); ++i) { m_page_status[i] = uninitialized; m_page_to_slot[i] = on_disk; } for (unsigned_type i = 0; i < numpages(); ++i) m_free_slots.push(i); m_bm->new_blocks(m_alloc_strategy, m_bids.begin(), m_bids.end(), 0); const_iterator inbegin = obj.begin(); const_iterator inend = obj.end(); std::copy(inbegin, inend, begin()); } //! \} //! \name Operators //! \{ //! assignment operator vector& operator = (const vector& obj) { if (&obj != this) { vector tmp(obj); this->swap(tmp); } return *this; } //! \} //! \name Iterator Construction //! \{ //! returns an iterator pointing to the beginning of the vector, see \ref design_vector_notes. iterator begin() { return iterator(this, 0); } //! returns a const_iterator pointing to the beginning of the vector, see \ref design_vector_notes. const_iterator begin() const { return const_iterator(this, 0); } //! returns a const_iterator pointing to the beginning of the vector, see \ref design_vector_notes. const_iterator cbegin() const { return begin(); } //! returns an iterator pointing beyond the end of the vector, see \ref design_vector_notes. iterator end() { return iterator(this, m_size); } //! returns a const_iterator pointing beyond the end of the vector, see \ref design_vector_notes. const_iterator end() const { return const_iterator(this, m_size); } //! returns a const_iterator pointing beyond the end of the vector, see \ref design_vector_notes. const_iterator cend() const { return end(); } //! returns a reverse_iterator pointing to the end of the vector. reverse_iterator rbegin() { return reverse_iterator(end()); } //! returns a reverse_iterator pointing to the end of the vector. const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } //! returns a reverse_iterator pointing to the end of the vector. const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } //! returns a reverse_iterator pointing beyond the beginning of the vector. reverse_iterator rend() { return reverse_iterator(begin()); } //! returns a reverse_iterator pointing beyond the beginning of the vector. const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } //! returns a reverse_iterator pointing beyond the beginning of the vector. const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } //! \} //! \name Direct Element Access //! \{ //! access the element at the given vector's offset reference operator [] (size_type offset) { return element(offset); } //! access the element at the given vector's offset const_reference operator [] (size_type offset) const { return const_element(offset); } //! access the element at the given vector's offset reference at(size_type offset) { assert(offset < (size_type)size()); return element(offset); } //! access the element at the given vector's offset const_reference at(size_type offset) const { assert(offset < (size_type)size()); return const_element(offset); } //! return true if the given vector offset is in cache bool is_element_cached(size_type offset) const { return is_page_cached(blocked_index_type(offset)); } //! \} //! \name Modifiers //! \{ //! Flushes the cache pages to the external memory. void flush() const { simple_vector non_free_slots(numpages()); for (unsigned_type i = 0; i < numpages(); i++) non_free_slots[i] = true; while (!m_free_slots.empty()) { non_free_slots[m_free_slots.front()] = false; m_free_slots.pop(); } for (unsigned_type i = 0; i < numpages(); i++) { m_free_slots.push(i); int_type page_no = m_slot_to_page[i]; if (non_free_slots[i]) { STXXL_VERBOSE_VECTOR("flush(): flushing page " << i << " at address " << (int64(page_no) * int64(block_type::size) * int64(page_size))); write_page(page_no, i); m_page_to_slot[page_no] = on_disk; } } } //! \} //! \name Constructors/Destructors //! \{ ~vector() { STXXL_VERBOSE_VECTOR("~vector()"); try { flush(); } catch (io_error e) { STXXL_ERRMSG("io_error thrown in ~vector(): " << e.what()); } catch (...) { STXXL_ERRMSG("Exception thrown in ~vector()"); } if (!m_exported) { if (m_from == NULL) { m_bm->delete_blocks(m_bids.begin(), m_bids.end()); } else // file must be truncated { STXXL_VERBOSE_VECTOR("~vector(): Changing size of file " << ((void*)m_from) << " to " << file_length()); STXXL_VERBOSE_VECTOR("~vector(): size of the vector is " << size()); try { m_from->set_size(file_length()); } catch (...) { STXXL_ERRMSG("Exception thrown in ~vector()...set_size()"); } } } delete m_cache; } //! \} //! \name Miscellaneous //! \{ //! Export data such that it is persistent on the file system. Resulting //! files will be numbered ascending. void export_files(std::string filename_prefix) { int64 no = 0; for (bids_container_iterator i = m_bids.begin(); i != m_bids.end(); ++i) { std::ostringstream number; number << std::setw(9) << std::setfill('0') << no; size_type current_block_size = ((i + 1) == m_bids.end() && m_size % block_type::size > 0) ? (m_size % block_type::size) * sizeof(value_type) : block_type::size * sizeof(value_type); (*i).storage->export_files((*i).offset, current_block_size, filename_prefix + number.str()); ++no; } m_exported = true; } //! Get the file associated with this vector, or NULL. file * get_file() const { return m_from; } //! \} //! \name Capacity //! \{ //! Set the blocks and the size of this container explicitly. //! The vector must be completely empty before. template void set_content(const ForwardIterator& bid_begin, const ForwardIterator& bid_end, size_type n) { unsigned_type new_bids_size = div_ceil(n, block_type::size); m_bids.resize(new_bids_size); std::copy(bid_begin, bid_end, m_bids.begin()); unsigned_type new_pages = div_ceil(new_bids_size, page_size); m_page_status.resize(new_pages, valid_on_disk); m_page_to_slot.resize(new_pages, on_disk); m_size = n; } //! Number of pages used by the pager. inline unsigned_type numpages() const { return m_pager.size(); } //! \} private: bids_container_iterator bid(const size_type& offset) { return (m_bids.begin() + static_cast (offset / block_type::size)); } bids_container_iterator bid(const blocked_index_type& offset) { return (m_bids.begin() + static_cast (offset.get_block2() * PageSize + offset.get_block1())); } const_bids_container_iterator bid(const size_type& offset) const { return (m_bids.begin() + static_cast (offset / block_type::size)); } const_bids_container_iterator bid(const blocked_index_type& offset) const { return (m_bids.begin() + static_cast (offset.get_block2() * PageSize + offset.get_block1())); } void read_page(int_type page_no, int_type cache_slot) const { assert(page_no < (int_type)m_page_status.size()); if (m_page_status[page_no] == uninitialized) return; STXXL_VERBOSE_VECTOR("read_page(): page_no=" << page_no << " cache_slot=" << cache_slot); request_ptr* reqs = new request_ptr[page_size]; int_type block_no = page_no * page_size; int_type last_block = STXXL_MIN(block_no + page_size, int_type(m_bids.size())); int_type i = cache_slot * page_size, j = 0; for ( ; block_no < last_block; ++block_no, ++i, ++j) { reqs[j] = (*m_cache)[i].read(m_bids[block_no]); } assert(last_block - page_no * page_size > 0); wait_all(reqs, last_block - page_no * page_size); delete[] reqs; } void write_page(int_type page_no, int_type cache_slot) const { assert(page_no < (int_type)m_page_status.size()); if (!(m_page_status[page_no] & dirty)) return; STXXL_VERBOSE_VECTOR("write_page(): page_no=" << page_no << " cache_slot=" << cache_slot); request_ptr* reqs = new request_ptr[page_size]; int_type block_no = page_no * page_size; int_type last_block = STXXL_MIN(block_no + page_size, int_type(m_bids.size())); assert(block_no < last_block); int_type i = cache_slot * page_size, j = 0; for ( ; block_no < last_block; ++block_no, ++i, ++j) { reqs[j] = (*m_cache)[i].write(m_bids[block_no]); } m_page_status[page_no] = valid_on_disk; assert(last_block - page_no * page_size > 0); wait_all(reqs, last_block - page_no * page_size); delete[] reqs; } reference element(size_type offset) { #ifdef STXXL_RANGE_CHECK assert(offset < (size_type)size()); #endif return element(blocked_index_type(offset)); } reference element(const blocked_index_type& offset) { #ifdef STXXL_RANGE_CHECK assert(offset.get_pos() < size()); #endif unsigned_type page_no = offset.get_block2(); assert(page_no < m_page_to_slot.size()); // fails if offset is too large, out of bound access int_type cache_slot = m_page_to_slot[page_no]; if (cache_slot < 0) // == on_disk { if (m_free_slots.empty()) // has to kick { int_type kicked_slot = m_pager.kick(); m_pager.hit(kicked_slot); int_type old_page_no = m_slot_to_page[kicked_slot]; m_page_to_slot[page_no] = kicked_slot; m_page_to_slot[old_page_no] = on_disk; m_slot_to_page[kicked_slot] = page_no; write_page(old_page_no, kicked_slot); read_page(page_no, kicked_slot); m_page_status[page_no] = dirty; return (*m_cache)[kicked_slot * page_size + offset.get_block1()][offset.get_offset()]; } else { int_type free_slot = m_free_slots.front(); m_free_slots.pop(); m_pager.hit(free_slot); m_page_to_slot[page_no] = free_slot; m_slot_to_page[free_slot] = page_no; read_page(page_no, free_slot); m_page_status[page_no] = dirty; return (*m_cache)[free_slot * page_size + offset.get_block1()][offset.get_offset()]; } } else { m_page_status[page_no] = dirty; m_pager.hit(cache_slot); return (*m_cache)[cache_slot * page_size + offset.get_block1()][offset.get_offset()]; } } // don't forget to first flush() the vector's cache before updating pages externally void page_externally_updated(unsigned_type page_no) const { // fails if offset is too large, out of bound access assert(page_no < m_page_status.size()); // "A dirty page has been marked as newly initialized. The page content will be lost." assert(!(m_page_status[page_no] & dirty)); if (m_page_to_slot[page_no] != on_disk) { // remove page from cache m_free_slots.push(m_page_to_slot[page_no]); m_page_to_slot[page_no] = on_disk; STXXL_VERBOSE_VECTOR("page_externally_updated(): page_no=" << page_no << " flushed from cache."); } else { STXXL_VERBOSE_VECTOR("page_externally_updated(): page_no=" << page_no << " no need to flush."); } m_page_status[page_no] = valid_on_disk; } void block_externally_updated(size_type offset) const { page_externally_updated( (unsigned_type)(offset / (block_type::size * page_size)) ); } void block_externally_updated(const blocked_index_type& offset) const { page_externally_updated(offset.get_block2()); } const_reference const_element(size_type offset) const { return const_element(blocked_index_type(offset)); } const_reference const_element(const blocked_index_type& offset) const { unsigned_type page_no = offset.get_block2(); assert(page_no < m_page_to_slot.size()); // fails if offset is too large, out of bound access int_type cache_slot = m_page_to_slot[page_no]; if (cache_slot < 0) // == on_disk { if (m_free_slots.empty()) // has to kick { int_type kicked_slot = m_pager.kick(); m_pager.hit(kicked_slot); int_type old_page_no = m_slot_to_page[kicked_slot]; m_page_to_slot[page_no] = kicked_slot; m_page_to_slot[old_page_no] = on_disk; m_slot_to_page[kicked_slot] = page_no; write_page(old_page_no, kicked_slot); read_page(page_no, kicked_slot); return (*m_cache)[kicked_slot * page_size + offset.get_block1()][offset.get_offset()]; } else { int_type free_slot = m_free_slots.front(); m_free_slots.pop(); m_pager.hit(free_slot); m_page_to_slot[page_no] = free_slot; m_slot_to_page[free_slot] = page_no; read_page(page_no, free_slot); return (*m_cache)[free_slot * page_size + offset.get_block1()][offset.get_offset()]; } } else { m_pager.hit(cache_slot); return (*m_cache)[cache_slot * page_size + offset.get_block1()][offset.get_offset()]; } } bool is_page_cached(const blocked_index_type& offset) const { unsigned_type page_no = offset.get_block2(); assert(page_no < m_page_to_slot.size()); // fails if offset is too large, out of bound access int_type cache_slot = m_page_to_slot[page_no]; return (cache_slot >= 0); // on_disk == -1 } }; template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> inline bool operator == (stxxl::vector& a, stxxl::vector& b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> inline bool operator != (stxxl::vector& a, stxxl::vector& b) { return !(a == b); } template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> inline bool operator < (stxxl::vector& a, stxxl::vector& b) { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> inline bool operator > (stxxl::vector& a, stxxl::vector& b) { return b < a; } template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> inline bool operator <= (stxxl::vector& a, stxxl::vector& b) { return !(b < a); } template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> inline bool operator >= (stxxl::vector& a, stxxl::vector& b) { return !(a < b); } //////////////////////////////////////////////////////////////////////////// // specialization for stxxl::vector, to use only const_iterators template bool is_sorted( stxxl::vector_iterator first, stxxl::vector_iterator last) { return is_sorted_helper( stxxl::const_vector_iterator(first), stxxl::const_vector_iterator(last)); } template bool is_sorted( stxxl::vector_iterator first, stxxl::vector_iterator last, StrictWeakOrdering comp) { return is_sorted_helper( stxxl::const_vector_iterator(first), stxxl::const_vector_iterator(last), comp); } //////////////////////////////////////////////////////////////////////////// template class vector_bufreader_iterator; /*! * Buffered sequential reader from a vector using overlapped I/O. * * This buffered reader can be used to read a large sequential region of a * vector using overlapped I/O. The object is created from an iterator range, * which can then be read to using operator<<(), or with operator*() and * operator++(). * * The interface also fulfills all requirements of a stream. Actually most of * the code is identical to stream::vector_iterator2stream. * * Note that this buffered reader is inefficient for reading small ranges. This * is intentional, as one can just use operator[] on the vector for that. * * See \ref tutorial_vector_buf */ template class vector_bufreader : public noncopyable { public: //! template parameter: the vector iterator type typedef VectorIterator vector_iterator; //! value type of the output vector typedef typename vector_iterator::value_type value_type; //! block type used in the vector typedef typename vector_iterator::block_type block_type; //! type of the input vector typedef typename vector_iterator::vector_type vector_type; //! block identifier iterator of the vector typedef typename vector_iterator::bids_container_iterator bids_container_iterator; //! construct output buffered stream used for overlapped reading typedef buf_istream buf_istream_type; //! construct an iterator for vector_bufreader (for C++11 range-based for loop) typedef vector_bufreader_iterator bufreader_iterator; //! size of remaining data typedef typename vector_type::size_type size_type; protected: //! iterator to the beginning of the range. vector_iterator m_begin; //! internal "current" iterator into the vector. vector_iterator m_iter; //! iterator to the end of the range. vector_iterator m_end; //! buffered input stream used to overlapped I/O. buf_istream_type* m_bufin; //! number of blocks to use as buffers. unsigned_type m_nbuffers; //! allow vector_bufreader_iterator to check m_iter against its current value friend class vector_bufreader_iterator; public: //! Create overlapped reader for the given iterator range. //! \param begin iterator to position were to start reading in vector //! \param end iterator to position were to end reading in vector //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) vector_bufreader(vector_iterator begin, vector_iterator end, unsigned_type nbuffers = 0) : m_begin(begin), m_end(end), m_bufin(NULL), m_nbuffers(nbuffers) { m_begin.flush(); // flush container if (m_nbuffers == 0) m_nbuffers = 2 * config::get_instance()->disks_number(); rewind(); } //! Create overlapped reader for the whole vector's content. //! \param vec vector to read //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) vector_bufreader(const vector_type& vec, unsigned_type nbuffers = 0) : m_begin(vec.begin()), m_end(vec.end()), m_bufin(NULL), m_nbuffers(nbuffers) { m_begin.flush(); // flush container if (m_nbuffers == 0) m_nbuffers = 2 * config::get_instance()->disks_number(); rewind(); } //! Rewind stream back to begin. Note that this recreates the buffered //! reader and is thus not cheap. void rewind() { m_iter = m_begin; if (empty()) return; if (m_bufin) delete m_bufin; // find last bid to read bids_container_iterator end_bid = m_end.bid() + (m_end.block_offset() ? 1 : 0); // construct buffered istream for range m_bufin = new buf_istream_type(m_begin.bid(), end_bid, m_nbuffers); // skip the beginning of the block, up to real beginning vector_iterator curr = m_begin - m_begin.block_offset(); for ( ; curr != m_begin; ++curr) ++(*m_bufin); } //! Finish reading and free buffered reader. ~vector_bufreader() { if (m_bufin) delete m_bufin; } //! Return constant reference to current item const value_type& operator * () const { return *(*m_bufin); } //! Return constant pointer to current item const value_type* operator -> () const { return &(*(*m_bufin)); } //! Advance to next item (asserts if !empty()). vector_bufreader& operator ++ () { assert(!empty()); ++m_iter; ++(*m_bufin); if (UNLIKELY(empty())) { delete m_bufin; m_bufin = NULL; } return *this; } //! Read current item into variable and advance to next one. vector_bufreader& operator >> (value_type& v) { v = operator * (); operator ++ (); return *this; } //! Return remaining size. size_type size() const { assert(m_begin <= m_iter && m_iter <= m_end); return (size_type)(m_end - m_iter); } //! Returns true once the whole range has been read. bool empty() const { return (m_iter == m_end); } //! Return vector_bufreader_iterator for C++11 range-based for loop bufreader_iterator begin() { return bufreader_iterator(*this, m_begin); } //! Return vector_bufreader_iterator for C++11 range-based for loop bufreader_iterator end() { return bufreader_iterator(*this, m_end); } }; //////////////////////////////////////////////////////////////////////////// /*! * Adapter for vector_bufreader to match iterator requirements of C++11 * range-based loop construct. * * Since vector_bufreader itself points to only one specific item, this * iterator is merely a counter facade. The functions operator*() and * operator++() must only be called when it is in _sync_ with the bufreader * object. This is generally only the case for an iterator constructed with * begin() and then advanced with operator++(). The class checks this using * asserts(), the operators will fail if used wrong. * * See \ref tutorial_vector_buf */ template class vector_bufreader_iterator { public: //! The underlying buffered reader type typedef VectorBufReaderType vector_bufreader_type; //! Value type of vector typedef typename vector_bufreader_type::value_type value_type; //! Use vector_iterator to reference a point in the vector. typedef typename vector_bufreader_type::vector_iterator vector_iterator; protected: //! Buffered reader used to access elements in vector vector_bufreader_type& m_bufreader; //! Use vector_iterator to reference a point in the vector. vector_iterator m_iter; public: //! Construct iterator using vector_iterator vector_bufreader_iterator(vector_bufreader_type& bufreader, const vector_iterator& iter) : m_bufreader(bufreader), m_iter(iter) { } //! Return constant reference to current item const value_type& operator * () const { assert(m_bufreader.m_iter == m_iter); return m_bufreader.operator * (); } //! Return constant pointer to current item const value_type* operator -> () const { assert(m_bufreader.m_iter == m_iter); return m_bufreader.operator -> (); } //! Make bufreader advance to next item (asserts if !empty() or if iterator //! does not point to current). vector_bufreader_iterator& operator ++ () { assert(m_bufreader.m_iter == m_iter); m_bufreader.operator ++ (); m_iter++; return *this; } //! Equality comparison operator bool operator == (const vector_bufreader_iterator& vbi) const { assert(&m_bufreader == &vbi.m_bufreader); return (m_iter == vbi.m_iter); } //! Inequality comparison operator bool operator != (const vector_bufreader_iterator& vbi) const { assert(&m_bufreader == &vbi.m_bufreader); return (m_iter != vbi.m_iter); } }; //////////////////////////////////////////////////////////////////////////// /*! * Buffered sequential reverse reader from a vector using overlapped I/O. * * This buffered reader can be used to read a large sequential region of a * vector _in_reverse_ using overlapped I/O. The object is created from an * iterator range, which can then be read to using operator<<(), or with * operator*() and operator++(), where ++ actually goes to the preceding * element. * * The interface also fulfills all requirements of a stream. Actually most of * the code is identical to stream::vector_iterator2stream. * * Note that this buffered reader is inefficient for reading small ranges. This * is intentional, as one can just use operator[] on the vector for that. * * See \ref tutorial_vector_buf */ template class vector_bufreader_reverse : public noncopyable { public: //! template parameter: the vector iterator type typedef VectorIterator vector_iterator; //! value type of the output vector typedef typename vector_iterator::value_type value_type; //! block type used in the vector typedef typename vector_iterator::block_type block_type; //! type of the input vector typedef typename vector_iterator::vector_type vector_type; //! block identifier iterator of the vector typedef typename vector_iterator::bids_container_iterator bids_container_iterator; //! construct output buffered stream used for overlapped reading typedef buf_istream_reverse buf_istream_type; //! size of remaining data typedef typename vector_type::size_type size_type; protected: //! iterator to the beginning of the range. vector_iterator m_begin; //! internal "current" iterator into the vector. vector_iterator m_iter; //! iterator to the end of the range. vector_iterator m_end; //! buffered input stream used to overlapped I/O. buf_istream_type* m_bufin; //! number of blocks to use as buffers. unsigned_type m_nbuffers; public: //! Create overlapped reader for the given iterator range. //! \param begin iterator to position were to start reading in vector //! \param end iterator to position were to end reading in vector //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) vector_bufreader_reverse(vector_iterator begin, vector_iterator end, unsigned_type nbuffers = 0) : m_begin(begin), m_end(end), m_bufin(NULL), m_nbuffers(nbuffers) { m_begin.flush(); // flush container if (m_nbuffers == 0) m_nbuffers = 2 * config::get_instance()->disks_number(); rewind(); } //! Create overlapped reader for the whole vector's content. //! \param vec vector to read //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) vector_bufreader_reverse(const vector_type& vec, unsigned_type nbuffers = 0) : m_begin(vec.begin()), m_end(vec.end()), m_bufin(NULL), m_nbuffers(nbuffers) { m_begin.flush(); // flush container if (m_nbuffers == 0) m_nbuffers = 2 * config::get_instance()->disks_number(); rewind(); } //! Rewind stream back to begin. Note that this recreates the buffered //! reader and is thus not cheap. void rewind() { m_iter = m_end; if (empty()) return; if (m_bufin) delete m_bufin; // find last bid to read bids_container_iterator end_bid = m_end.bid() + (m_end.block_offset() ? 1 : 0); // construct buffered istream_reverse for range m_bufin = new buf_istream_type(m_begin.bid(), end_bid, m_nbuffers); // skip to beginning of reverse sequence. stxxl::int_type endoff = m_end.block_offset(); if (endoff == 0) { // nothing to skip } else { // else, let ifstream_reverse skip last elements at end of block, // up to real end for ( ; endoff != block_type::size; endoff++) ++(*m_bufin); } } //! Finish reading and free buffered reader. ~vector_bufreader_reverse() { if (m_bufin) delete m_bufin; } //! Return constant reference to current item const value_type& operator * () const { return *(*m_bufin); } //! Return constant pointer to current item const value_type* operator -> () const { return &(*(*m_bufin)); } //! Advance to next item (asserts if !empty()). vector_bufreader_reverse& operator ++ () { assert(!empty()); --m_iter; ++(*m_bufin); if (UNLIKELY(empty())) { delete m_bufin; m_bufin = NULL; } return *this; } //! Read current item into variable and advance to next one. vector_bufreader_reverse& operator >> (value_type& v) { v = operator * (); operator ++ (); return *this; } //! Return remaining size. size_type size() const { assert(m_begin <= m_iter && m_iter <= m_end); return (size_type)(m_iter - m_begin); } //! Returns true once the whole range has been read. bool empty() const { return (m_iter == m_begin); } }; //////////////////////////////////////////////////////////////////////////// /*! * Buffered sequential writer to a vector using overlapped I/O. * * This buffered writer can be used to write a large sequential region of a * vector using overlapped I/O. The object is created from an iterator range, * which can then be written to using operator << (), or with operator * () and * operator ++ (). * * The buffered writer is given one iterator in the constructor. When writing, * this iterator advances in the vector and will \b enlarge the vector once it * reaches the end(). The vector size is doubled each time; nevertheless, it is * better to preinitialize the vector's size using stxxl::vector::resize(). * * See \ref tutorial_vector_buf */ template class vector_bufwriter : public noncopyable { public: //! template parameter: the vector iterator type typedef VectorIterator iterator; //! type of the output vector typedef typename iterator::vector_type vector_type; //! value type of the output vector typedef typename iterator::value_type value_type; //! block type used in the vector typedef typename iterator::block_type block_type; //! block identifier iterator of the vector typedef typename iterator::bids_container_iterator bids_container_iterator; //! iterator type of vector typedef typename iterator::iterator vector_iterator; typedef typename iterator::const_iterator vector_const_iterator; //! construct output buffered stream used for overlapped writing typedef buf_ostream buf_ostream_type; protected: //! internal iterator into the vector. vector_iterator m_iter; //! iterator to the current end of the vector. vector_const_iterator m_end; //! boolean whether the vector was grown, will shorten at finish(). bool m_grown; //! iterator into vector of the last block accessed (used to issue updates //! when the block is switched). vector_const_iterator m_prevblk; //! buffered output stream used to overlapped I/O. buf_ostream_type* m_bufout; //! number of blocks to use as buffers. unsigned_type m_nbuffers; public: //! Create overlapped writer beginning at the given iterator. //! \param begin iterator to position were to start writing in vector //! \param nbuffers number of buffers used for overlapped I/O (>= 2D recommended) vector_bufwriter(vector_iterator begin, unsigned_type nbuffers = 0) : m_iter(begin), m_end(m_iter.parent_vector()->end()), m_grown(false), m_bufout(NULL), m_nbuffers(nbuffers) { if (m_nbuffers == 0) m_nbuffers = 2 * config::get_instance()->disks_number(); assert(m_iter <= m_end); } //! Create overlapped writer for the vector's beginning //! \param vec vector to write //! \param nbuffers number of buffers used for overlapped I/O (>= 2D recommended) vector_bufwriter(vector_type& vec, unsigned_type nbuffers = 0) : m_iter(vec.begin()), m_end(m_iter.parent_vector()->end()), m_grown(false), m_bufout(NULL), m_nbuffers(nbuffers) { if (m_nbuffers == 0) m_nbuffers = 2 * config::get_instance()->disks_number(); assert(m_iter <= m_end); } //! Finish writing and flush output back to vector. ~vector_bufwriter() { finish(); } //! Return mutable reference to item at the position of the internal //! iterator. value_type& operator * () { if (UNLIKELY(m_iter == m_end)) { // iterator points to end of vector -> double vector's size if (m_bufout) { // fixes issue with buf_ostream writing invalid blocks: when // buf_ostream::current_elem advances to next block, flush() // will write to block beyond bid().end. if (m_iter.block_offset() != 0) m_bufout->flush(); // flushes overlap buffers delete m_bufout; m_bufout = NULL; if (m_iter.block_offset() != 0) m_iter.block_externally_updated(); } vector_type& v = *m_iter.parent_vector(); if (v.size() < 2 * block_type::size) { v.resize(2 * block_type::size); } else { v.resize(2 * v.size()); } m_end = v.end(); m_grown = true; } assert(m_iter < m_end); if (UNLIKELY(m_bufout == NULL)) { if (m_iter.block_offset() != 0) { // output position is not at the start of the block, we // continue to use the iterator initially passed to the // constructor. return *m_iter; } else { // output position is start of block: create buffered writer m_iter.flush(); // flush container // create buffered write stream for blocks m_bufout = new buf_ostream_type(m_iter.bid(), m_nbuffers); m_prevblk = m_iter; // drop through to normal output into buffered writer } } // if the pointer has finished a block, then we inform the vector that // this block has been updated. if (UNLIKELY(m_iter.block_offset() == 0)) { if (m_prevblk != m_iter) { m_prevblk.block_externally_updated(); m_prevblk = m_iter; } } return m_bufout->operator * (); } //! Advance internal iterator. vector_bufwriter& operator ++ () { // always advance internal iterator ++m_iter; // if buf_ostream active, advance that too if (LIKELY(m_bufout != NULL)) m_bufout->operator ++ (); return *this; } //! Write value to the current position and advance the internal iterator. vector_bufwriter& operator << (const value_type& v) { operator * () = v; operator ++ (); return *this; } //! Finish writing and flush output back to vector. void finish() { if (m_bufout) { // must finish the block started in the buffered writer: fill it with // the data in the vector vector_const_iterator const_out = m_iter; while (const_out.block_offset() != 0) { m_bufout->operator * () = *const_out; m_bufout->operator ++ (); ++const_out; } // inform the vector that the block has been updated. if (m_prevblk != m_iter) { m_prevblk.block_externally_updated(); m_prevblk = m_iter; } delete m_bufout; m_bufout = NULL; } if (m_grown) { vector_type& v = *m_iter.parent_vector(); v.resize(m_iter - v.begin()); m_grown = false; } } }; //////////////////////////////////////////////////////////////////////////// //! External vector type generator. //! //! \tparam ValueType element type of contained objects (POD with no references to internal memory) //! \tparam PageSize number of blocks in a page, default: \b 4 (recommended >= D) //! \tparam CachePages number of pages in cache, default: \b 8 (recommended >= 2) //! \tparam BlockSize external block size \a B in bytes, default: 2 MiB //! \tparam AllocStr parallel disk allocation strategies: \c striping, RC, SR, or FR. default: \b RC. //! \tparam Pager pager type: \c random or \c lru, default: \b lru. //! //! \warning Do not store references to the elements of an external vector. Such references //! might be invalidated during any following access to elements of the vector template < typename ValueType, unsigned PageSize = 4, unsigned CachePages = 8, unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), typename AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY, pager_type Pager = lru > struct VECTOR_GENERATOR { typedef typename IF, random_pager >::result PagerType; typedef vector result; }; //! \} STXXL_END_NAMESPACE namespace std { template < typename ValueType, unsigned PageSize, typename PagerType, unsigned BlockSize, typename AllocStr, typename SizeType> void swap(stxxl::vector& a, stxxl::vector& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_VECTOR_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/pq_helpers.h000644 001411 000144 00000015730 12414452316 022733 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/pq_helpers.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 1999 Peter Sanders * Copyright (C) 2003, 2004, 2007 Roman Dementiev * Copyright (C) 2007, 2009 Johannes Singler * Copyright (C) 2007, 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_PQ_HELPERS_HEADER #define STXXL_CONTAINERS_PQ_HELPERS_HEADER #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if STXXL_PARALLEL #if defined(STXXL_PARALLEL_MODE) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40400) #undef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL #undef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL #undef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL 0 #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL 0 #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER 0 #endif // enable/disable parallel merging for certain cases, for performance tuning #ifndef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL 1 #endif #ifndef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL 1 #endif #ifndef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER 1 #endif #endif //STXXL_PARALLEL #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL #define STXXL_PQ_EXTERNAL_LOSER_TREE 0 // no loser tree for the external sequences #else #define STXXL_PQ_EXTERNAL_LOSER_TREE 1 #endif #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL #define STXXL_PQ_INTERNAL_LOSER_TREE 0 // no loser tree for the internal sequences #else #define STXXL_PQ_INTERNAL_LOSER_TREE 1 #endif #define STXXL_VERBOSE_PQ(msg) STXXL_VERBOSE2_THIS("priority_queue::" << msg) STXXL_BEGIN_NAMESPACE //! \defgroup stlcontinternals internals //! \ingroup stlcont //! Supporting internal classes //! \{ /*! \internal */ namespace priority_queue_local { /*! * Similar to std::priority_queue, with the following differences: * - Maximum size is fixed at construction time, so an array can be used. * - Provides access to underlying heap, so (parallel) sorting in place is possible. * - Can be cleared "at once", without reallocation. */ template , typename CompareType = std::less > class internal_priority_queue { public: typedef ValueType value_type; typedef ContainerType container_type; typedef CompareType compare_type; typedef typename container_type::reference reference; typedef typename container_type::const_reference const_reference; typedef typename container_type::size_type size_type; protected: // See queue::heap for notes on these names. container_type heap; CompareType comp; size_type current_size; public: //! Default constructor creates no elements. explicit internal_priority_queue(size_type capacity) : heap(capacity), current_size(0) { } //! Returns true if the %queue is empty. bool empty() const { return current_size == 0; } //! Returns the number of elements in the %queue. size_type size() const { return current_size; } /*! * Returns a read-only (constant) reference to the data at the first * element of the %queue. */ const_reference top() const { return heap.front(); } /*! * Add data to the %queue. * @param x Data to be added. * * This is a typical %queue operation. * The time complexity of the operation depends on the underlying * container. */ void push(const value_type& x) { heap[current_size] = x; ++current_size; std::push_heap(heap.begin(), heap.begin() + current_size, comp); } /*! * Removes first element. * * This is a typical %queue operation. It shrinks the %queue * by one. The time complexity of the operation depends on the * underlying container. * * Note that no data is returned, and if the first element's * data is needed, it should be retrieved before pop() is * called. */ void pop() { std::pop_heap(heap.begin(), heap.begin() + current_size, comp); --current_size; } //! Sort all contained elements, write result to @c target. void sort_to(value_type* target) { check_sort_settings(); potentially_parallel:: sort(heap.begin(), heap.begin() + current_size, comp); std::reverse_copy(heap.begin(), heap.begin() + current_size, target); } //! Remove all contained elements. void clear() { current_size = 0; } }; //! Inverts the order of a comparison functor by swapping its arguments. template class invert_order { protected: Predicate pred; public: explicit invert_order(const Predicate& _pred) : pred(_pred) { } bool operator () (const FirstType& x, const SecondType& y) const { return pred(y, x); } }; /*! * Similar to std::stack, with the following differences: * - Maximum size is fixed at compilation time, so an array can be used. * - Can be cleared "at once", without reallocation. */ template class internal_bounded_stack { typedef ValueType value_type; typedef unsigned_type size_type; enum { max_size = MaxSize }; size_type m_size; value_type m_array[max_size]; public: internal_bounded_stack() : m_size(0) { } void push(const value_type& x) { assert(m_size < max_size); m_array[m_size++] = x; } const value_type & top() const { assert(m_size > 0); return m_array[m_size - 1]; } void pop() { assert(m_size > 0); --m_size; } void clear() { m_size = 0; } size_type size() const { return m_size; } bool empty() const { return m_size == 0; } }; } // namespace priority_queue_local //! \} STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_PQ_HELPERS_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/containers/matrix_arithmetic.h000644 001411 000144 00000273372 12405375303 024316 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/matrix_arithmetic.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_MATRIX_ARITHMETIC_HEADER #define STXXL_CONTAINERS_MATRIX_ARITHMETIC_HEADER #include #include STXXL_BEGIN_NAMESPACE #ifndef STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS #define STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS 3 #endif #ifndef STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE #define STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE 2 #endif template class column_vector; template class row_vector; template class swappable_block_matrix; //! \addtogroup matrix //! \{ struct matrix_operation_statistic_dataset { int_type block_multiplication_calls, block_multiplications_saved_through_zero, block_addition_calls, block_additions_saved_through_zero; matrix_operation_statistic_dataset() : block_multiplication_calls(0), block_multiplications_saved_through_zero(0), block_addition_calls(0), block_additions_saved_through_zero(0) { } matrix_operation_statistic_dataset operator + (const matrix_operation_statistic_dataset& stat) { matrix_operation_statistic_dataset res(*this); res.block_multiplication_calls += stat.block_multiplication_calls; res.block_multiplications_saved_through_zero += stat.block_multiplications_saved_through_zero; res.block_addition_calls += stat.block_addition_calls; res.block_additions_saved_through_zero += stat.block_additions_saved_through_zero; return res; } matrix_operation_statistic_dataset operator - (const matrix_operation_statistic_dataset& stat) { matrix_operation_statistic_dataset res(*this); res.block_multiplication_calls -= stat.block_multiplication_calls; res.block_multiplications_saved_through_zero -= stat.block_multiplications_saved_through_zero; res.block_addition_calls -= stat.block_addition_calls; res.block_additions_saved_through_zero -= stat.block_additions_saved_through_zero; return res; } }; struct matrix_operation_statistic : public singleton, public matrix_operation_statistic_dataset { }; struct matrix_operation_statistic_data : public matrix_operation_statistic_dataset { matrix_operation_statistic_data(const matrix_operation_statistic& stat = * matrix_operation_statistic::get_instance()) : matrix_operation_statistic_dataset(stat) { } matrix_operation_statistic_data(const matrix_operation_statistic_dataset& stat) : matrix_operation_statistic_dataset(stat) { } matrix_operation_statistic_data& operator = (const matrix_operation_statistic& stat) { return *this = matrix_operation_statistic_data(stat); } void set() { operator = (*matrix_operation_statistic::get_instance()); } matrix_operation_statistic_data operator + (const matrix_operation_statistic_data& stat) { return matrix_operation_statistic_data(matrix_operation_statistic_dataset(*this) + matrix_operation_statistic_dataset(stat)); } matrix_operation_statistic_data operator - (const matrix_operation_statistic_data& stat) { return matrix_operation_statistic_data(matrix_operation_statistic_dataset(*this) - matrix_operation_statistic_dataset(stat)); } }; std::ostream& operator << (std::ostream& o, const matrix_operation_statistic_data& statsd) { o << "matrix operation statistics" << std::endl; o << "block multiplication calls : " << statsd.block_multiplication_calls << std::endl; o << "block multiplications saved through zero blocks: " << statsd.block_multiplications_saved_through_zero << std::endl; o << "block multiplications performed : " << statsd.block_multiplication_calls - statsd.block_multiplications_saved_through_zero << std::endl; o << "block addition calls : " << statsd.block_addition_calls << std::endl; o << "block additions saved through zero blocks : " << statsd.block_additions_saved_through_zero << std::endl; o << "block additions performed : " << statsd.block_addition_calls - statsd.block_additions_saved_through_zero << std::endl; return o; } //! \} //! matrix low-level operations and tools namespace matrix_local { //! A static_quadtree holds 4^Level elements arranged in a quad tree. //! //! Static quad trees are useful for recursive algorithms with fixed depth //! that partition the in- and output and perform pre- and postcalculations on the partitions. //! The four children of one node are denoted as ul (up left), ur (up right), dl (down left), and dr (down right). template struct static_quadtree { typedef static_quadtree smaller_static_quadtree; smaller_static_quadtree ul, ur, dl, dr; static_quadtree(smaller_static_quadtree ul, smaller_static_quadtree ur, smaller_static_quadtree dl, smaller_static_quadtree dr) : ul(ul), ur(ur), dl(dl), dr(dr) { } static_quadtree() { } static_quadtree& operator &= (const static_quadtree& right) { ul &= right.ul, ur &= right.ur; dl &= right.dl, dr &= right.dr; return *this; } static_quadtree& operator += (const static_quadtree& right) { ul += right.ul, ur += right.ur; dl += right.dl, dr += right.dr; return *this; } static_quadtree& operator -= (const static_quadtree& right) { ul -= right.ul, ur -= right.ur; dl -= right.dl, dr -= right.dr; return *this; } static_quadtree operator & (const static_quadtree& right) const { return static_quadtree(ul & right.ul, ur & right.ur, dl & right.dl, dr & right.dr); } static_quadtree operator + (const static_quadtree& right) const { return static_quadtree(ul + right.ul, ur + right.ur, dl + right.dl, dr + right.dr); } static_quadtree operator - (const static_quadtree& right) const { return static_quadtree(ul - right.ul, ur - right.ur, dl - right.dl, dr - right.dr); } }; template struct static_quadtree { ValueType val; static_quadtree(const ValueType& v) : val(v) { } static_quadtree() { } operator const ValueType& () const { return val; } operator ValueType& () { return val; } static_quadtree& operator &= (const static_quadtree& right) { val &= right.val; return *this; } static_quadtree& operator += (const static_quadtree& right) { val += right.val; return *this; } static_quadtree& operator -= (const static_quadtree& right) { val -= right.val; return *this; } static_quadtree operator ! () const { return static_quadtree(! val); } static_quadtree operator & (const static_quadtree& right) const { return val & right.val; } static_quadtree operator + (const static_quadtree& right) const { return val + right.val; } static_quadtree operator - (const static_quadtree& right) const { return val - right.val; } }; template struct feedable_strassen_winograd { typedef static_quadtree zbt; // true <=> is a zero-block typedef static_quadtree vt; typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_ab; typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_a; typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_b; typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_n; typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename swappable_block_matrix_type::size_type size_type; const size_type n, m, l; smaller_feedable_strassen_winograd_ab p1, p2; smaller_feedable_strassen_winograd_n p3, p4, p5; smaller_feedable_strassen_winograd_b p6; smaller_feedable_strassen_winograd_a p7; feedable_strassen_winograd( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : n(n), m(m), l(l), p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), p3( bs_c, n/2, m/2, l/2), p4( bs_c, n/2, m/2, l/2), p5( bs_c, n/2, m/2, l/2), p6( bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} feedable_strassen_winograd( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : n(n), m(m), l(l), p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2), p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2), p3( bs_c, n/2, m/2, l/2), p4( bs_c, n/2, m/2, l/2), p5( bs_c, n/2, m/2, l/2), p6( bs_c, n/2, m/2, l/2), p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} feedable_strassen_winograd( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : n(n), m(m), l(l), p1(bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), p2(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), p3(bs_c, n/2, m/2, l/2), p4(bs_c, n/2, m/2, l/2), p5(bs_c, n/2, m/2, l/2), p6(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), p7(bs_c, n/2, m/2, l/2) {} feedable_strassen_winograd( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : n(n), m(m), l(l), p1(bs_c, n / 2, m / 2, l / 2), p2(bs_c, n / 2, m / 2, l / 2), p3(bs_c, n / 2, m / 2, l / 2), p4(bs_c, n / 2, m / 2, l / 2), p5(bs_c, n / 2, m / 2, l / 2), p6(bs_c, n / 2, m / 2, l / 2), p7(bs_c, n / 2, m / 2, l / 2) { } void begin_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt zb) { typename zbt::smaller_static_quadtree s1 = zb.dl & zb.dr, s2 = s1 & zb.ul, s3 = zb.ul & zb.dl, s4 = zb.ur & s2; p1.begin_feeding_a_block(block_row, block_col, zb.ul); p2.begin_feeding_a_block(block_row, block_col, zb.ur); p3.begin_feeding_a_block(block_row, block_col, s1); p4.begin_feeding_a_block(block_row, block_col, s2); p5.begin_feeding_a_block(block_row, block_col, s3); p6.begin_feeding_a_block(block_row, block_col, s4); p7.begin_feeding_a_block(block_row, block_col, zb.dr); } void feed_a_element(const int_type element_num, const vt v) { typename vt::smaller_static_quadtree s1 = v.dl + v.dr, s2 = s1 - v.ul, s3 = v.ul - v.dl, s4 = v.ur - s2; p1.feed_a_element(element_num, v.ul); p2.feed_a_element(element_num, v.ur); p3.feed_a_element(element_num, s1); p4.feed_a_element(element_num, s2); p5.feed_a_element(element_num, s3); p6.feed_a_element(element_num, s4); p7.feed_a_element(element_num, v.dr); } void end_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt zb) { typename zbt::smaller_static_quadtree s1 = zb.dl & zb.dr, s2 = s1 & zb.ul, s3 = zb.ul & zb.dl, s4 = zb.ur & s2; p1.end_feeding_a_block(block_row, block_col, zb.ul); p2.end_feeding_a_block(block_row, block_col, zb.ur); p3.end_feeding_a_block(block_row, block_col, s1); p4.end_feeding_a_block(block_row, block_col, s2); p5.end_feeding_a_block(block_row, block_col, s3); p6.end_feeding_a_block(block_row, block_col, s4); p7.end_feeding_a_block(block_row, block_col, zb.dr); } void begin_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt zb) { typename zbt::smaller_static_quadtree t1 = zb.ur & zb.ul, t2 = zb.dr & t1, t3 = zb.dr & zb.ur, t4 = zb.dl & t2; p1.begin_feeding_b_block(block_row, block_col, zb.ul); p2.begin_feeding_b_block(block_row, block_col, zb.dl); p3.begin_feeding_b_block(block_row, block_col, t1); p4.begin_feeding_b_block(block_row, block_col, t2); p5.begin_feeding_b_block(block_row, block_col, t3); p6.begin_feeding_b_block(block_row, block_col, zb.dr); p7.begin_feeding_b_block(block_row, block_col, t4); } void feed_b_element(const int_type element_num, const vt v) { typename vt::smaller_static_quadtree t1 = v.ur - v.ul, t2 = v.dr - t1, t3 = v.dr - v.ur, t4 = v.dl - t2; p1.feed_b_element(element_num, v.ul); p2.feed_b_element(element_num, v.dl); p3.feed_b_element(element_num, t1); p4.feed_b_element(element_num, t2); p5.feed_b_element(element_num, t3); p6.feed_b_element(element_num, v.dr); p7.feed_b_element(element_num, t4); } void end_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt zb) { typename zbt::smaller_static_quadtree t1 = zb.ur & zb.ul, t2 = zb.dr & t1, t3 = zb.dr & zb.ur, t4 = zb.dl & t2; p1.end_feeding_b_block(block_row, block_col, zb.ul); p2.end_feeding_b_block(block_row, block_col, zb.dl); p3.end_feeding_b_block(block_row, block_col, t1); p4.end_feeding_b_block(block_row, block_col, t2); p5.end_feeding_b_block(block_row, block_col, t3); p6.end_feeding_b_block(block_row, block_col, zb.dr); p7.end_feeding_b_block(block_row, block_col, t4); } void multiply() { p1.multiply(); p2.multiply(); p3.multiply(); p4.multiply(); p5.multiply(); p6.multiply(); p7.multiply(); } zbt begin_reading_block(const size_type& block_row, const size_type& block_col) { zbt r; r.ur = r.ul = p1.begin_reading_block(block_row, block_col); r.ul &= p2.begin_reading_block(block_row, block_col); r.ur &= p4.begin_reading_block(block_row, block_col); r.dr = r.dl = p5.begin_reading_block(block_row, block_col); r.dl &= r.ur; r.dl &= p7.begin_reading_block(block_row, block_col); r.ur &= p3.begin_reading_block(block_row, block_col); r.dr &= r.ur; r.ur &= p6.begin_reading_block(block_row, block_col); return r; } vt read_element(int_type element_num) { vt r; r.ur = r.ul = p1.read_element(element_num); r.ul += p2.read_element(element_num); r.ur += p4.read_element(element_num); r.dr = r.dl = p5.read_element(element_num); r.dl += r.ur; r.dl += p7.read_element(element_num); r.ur += p3.read_element(element_num); r.dr += r.ur; r.ur += p6.read_element(element_num); return r; } zbt end_reading_block(const size_type& block_row, const size_type& block_col) { zbt r; r.ur = r.ul = p1.end_reading_block(block_row, block_col); r.ul &= p2.end_reading_block(block_row, block_col); r.ur &= p4.end_reading_block(block_row, block_col); r.dr = r.dl = p5.end_reading_block(block_row, block_col); r.dl &= r.ur; r.dl &= p7.end_reading_block(block_row, block_col); r.ur &= p3.end_reading_block(block_row, block_col); r.dr &= r.ur; r.ur &= p6.end_reading_block(block_row, block_col); return r; } }; template struct feedable_strassen_winograd { typedef static_quadtree zbt; // true <=> is a zero-block typedef static_quadtree vt; typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename swappable_block_matrix_type::size_type size_type; swappable_block_matrix_type a, b, c; const size_type n, m, l; internal_block_type* iblock; feedable_strassen_winograd( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : a(existing_a, n, l, a_from_row, a_from_col), b(existing_b, n, l, b_from_row, b_from_col), c(bs_c, n, m), n(n), m(m), l(l), iblock(0) { } feedable_strassen_winograd( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : a(existing_a, n, l, a_from_row, a_from_col), b(bs_c, n, l), c(bs_c, n, m), n(n), m(m), l(l), iblock(0) { } feedable_strassen_winograd( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : a(bs_c, n, l), b(existing_b, n, l, b_from_row, b_from_col), c(bs_c, n, m), n(n), m(m), l(l), iblock(0) { } feedable_strassen_winograd( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : a(bs_c, n, l), b(bs_c, n, l), c(bs_c, n, m), n(n), m(m), l(l), iblock(0) { } void begin_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt) { if (! AExists) iblock = &a.bs.acquire(a(block_row, block_col), true); } void feed_a_element(const int_type element_num, const vt v) { if (! AExists) (*iblock)[element_num] = v; } void end_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt zb) { if (! AExists) { a.bs.release(a(block_row, block_col), ! zb); iblock = 0; } } void begin_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt) { if (! BExists) iblock = &b.bs.acquire(b(block_row, block_col), true); } void feed_b_element(const int_type element_num, const vt v) { if (! BExists) (*iblock)[element_num] = v; } void end_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt zb) { if (! BExists) { b.bs.release(b(block_row, block_col), ! zb); iblock = 0; } } void multiply() { matrix_operations::choose_level_for_feedable_sw(a, b, c); } zbt begin_reading_block(const size_type& block_row, const size_type& block_col) { bool zb = ! c.bs.is_initialized(c(block_row, block_col)); iblock = &c.bs.acquire(c(block_row, block_col)); return zb; } vt read_element(const int_type element_num) { return (*iblock)[element_num]; } zbt end_reading_block(const size_type& block_row, const size_type& block_col) { c.bs.release(c(block_row, block_col), false); iblock = 0; return ! c.bs.is_initialized(c(block_row, block_col)); } }; template struct matrix_to_quadtree { typedef static_quadtree zbt; // true <=> is a zero-block typedef static_quadtree vt; typedef matrix_to_quadtree smaller_matrix_to_quadtree; typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename swappable_block_matrix_type::size_type size_type; smaller_matrix_to_quadtree ul, ur, dl, dr; matrix_to_quadtree(const swappable_block_matrix_type & matrix) : ul(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, 0), ur(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, matrix.get_width()/2), dl(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, 0), dr(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, matrix.get_width()/2) { assert(! (matrix.get_height() % 2 | matrix.get_width() % 2)); } matrix_to_quadtree(const swappable_block_matrix_type & matrix, const size_type height, const size_type width, const size_type from_row, const size_type from_col) : ul(matrix, height/2, width/2, from_row, from_col), ur(matrix, height/2, width/2, from_row, from_col + width/2), dl(matrix, height/2, width/2, from_row + height/2, from_col), dr(matrix, height/2, width/2, from_row + height/2, from_col + width/2) { assert(! (height % 2 | width % 2)); } void begin_feeding_block(const size_type& block_row, const size_type& block_col, const zbt zb) { ul.begin_feeding_block(block_row, block_col, zb.ul); ur.begin_feeding_block(block_row, block_col, zb.ur); dl.begin_feeding_block(block_row, block_col, zb.dl); dr.begin_feeding_block(block_row, block_col, zb.dr); } void feed_element(const int_type element_num, const vt v) { ul.feed_element(element_num, v.ul); ur.feed_element(element_num, v.ur); dl.feed_element(element_num, v.dl); dr.feed_element(element_num, v.dr); } void feed_and_add_element(const int_type element_num, const vt v) { ul.feed_and_add_element(element_num, v.ul); ur.feed_and_add_element(element_num, v.ur); dl.feed_and_add_element(element_num, v.dl); dr.feed_and_add_element(element_num, v.dr); } void end_feeding_block(const size_type& block_row, const size_type& block_col, const zbt zb) { ul.end_feeding_block(block_row, block_col, zb.ul); ur.end_feeding_block(block_row, block_col, zb.ur); dl.end_feeding_block(block_row, block_col, zb.dl); dr.end_feeding_block(block_row, block_col, zb.dr); } zbt begin_reading_block(const size_type& block_row, const size_type& block_col) { zbt zb; zb.ul = ul.begin_reading_block(block_row, block_col); zb.ur = ur.begin_reading_block(block_row, block_col); zb.dl = dl.begin_reading_block(block_row, block_col); zb.dr = dr.begin_reading_block(block_row, block_col); return zb; } vt read_element(const int_type element_num) { vt v; v.ul = ul.read_element(element_num); v.ur = ur.read_element(element_num); v.dl = dl.read_element(element_num); v.dr = dr.read_element(element_num); return v; } zbt end_reading_block(const size_type& block_row, const size_type& block_col) { zbt zb; zb.ul = ul.end_reading_block(block_row, block_col); zb.ur = ur.end_reading_block(block_row, block_col); zb.dl = dl.end_reading_block(block_row, block_col); zb.dr = dr.end_reading_block(block_row, block_col); return zb; } const size_type & get_height_in_blocks() { return ul.get_height_in_blocks(); } const size_type & get_width_in_blocks() { return ul.get_width_in_blocks(); } }; template struct matrix_to_quadtree { typedef static_quadtree zbt; // true <=> is a zero-block typedef static_quadtree vt; typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename swappable_block_matrix_type::size_type size_type; swappable_block_matrix_type m; internal_block_type* iblock; matrix_to_quadtree(const swappable_block_matrix_type& matrix) : m(matrix, matrix.get_height(), matrix.get_width(), 0, 0), iblock(0) { } matrix_to_quadtree(const swappable_block_matrix_type& matrix, const size_type height, const size_type width, const size_type from_row, const size_type from_col) : m(matrix, height, width, from_row, from_col), iblock(0) { } void begin_feeding_block(const size_type& block_row, const size_type& block_col, const zbt) { iblock = &m.bs.acquire(m(block_row, block_col)); } void feed_element(const int_type element_num, const vt v) { (*iblock)[element_num] = v; } void feed_and_add_element(const int_type element_num, const vt v) { (*iblock)[element_num] += v; } void end_feeding_block(const size_type& block_row, const size_type& block_col, const zbt zb) { m.bs.release(m(block_row, block_col), ! zb); iblock = 0; } zbt begin_reading_block(const size_type& block_row, const size_type& block_col) { zbt zb = ! m.bs.is_initialized(m(block_row, block_col)); iblock = &m.bs.acquire(m(block_row, block_col)); return zb; } vt read_element(const int_type element_num) { return (*iblock)[element_num]; } zbt end_reading_block(const size_type& block_row, const size_type& block_col) { m.bs.release(m(block_row, block_col), false); iblock = 0; return ! m.bs.is_initialized(m(block_row, block_col)); } const size_type & get_height_in_blocks() { return m.get_height(); } const size_type & get_width_in_blocks() { return m.get_width(); } }; template struct feedable_strassen_winograd_block_grained { typedef static_quadtree zbt; // true <=> is a zero-block typedef static_quadtree vt; typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_ab; typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_a; typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_b; typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_n; typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename swappable_block_matrix_type::size_type size_type; typedef matrix_operations Ops; const size_type n, m, l; smaller_feedable_strassen_winograd_ab p1, p2; smaller_feedable_strassen_winograd_n p3, p4, p5; smaller_feedable_strassen_winograd_b p6; smaller_feedable_strassen_winograd_a p7; inline feedable_strassen_winograd_block_grained( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : n(n), m(m), l(l), p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), p3( bs_c, n/2, m/2, l/2), p4( bs_c, n/2, m/2, l/2), p5( bs_c, n/2, m/2, l/2), p6( bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} inline feedable_strassen_winograd_block_grained( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : n(n), m(m), l(l), p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2), p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2), p3( bs_c, n/2, m/2, l/2), p4( bs_c, n/2, m/2, l/2), p5( bs_c, n/2, m/2, l/2), p6( bs_c, n/2, m/2, l/2), p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} inline feedable_strassen_winograd_block_grained( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : n(n), m(m), l(l), p1(bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), p2(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), p3(bs_c, n/2, m/2, l/2), p4(bs_c, n/2, m/2, l/2), p5(bs_c, n/2, m/2, l/2), p6(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), p7(bs_c, n/2, m/2, l/2) {} inline feedable_strassen_winograd_block_grained( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : n(n), m(m), l(l), p1(bs_c, n / 2, m / 2, l / 2), p2(bs_c, n / 2, m / 2, l / 2), p3(bs_c, n / 2, m / 2, l / 2), p4(bs_c, n / 2, m / 2, l / 2), p5(bs_c, n / 2, m / 2, l / 2), p6(bs_c, n / 2, m / 2, l / 2), p7(bs_c, n / 2, m / 2, l / 2) { } inline void feed_a(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) { // partition bl typename Ops::swappable_block_matrix_quarterer qbl(bl); // preadditions swappable_block_matrix_type s1(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), s2(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), s3(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), s4(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()); Ops::strassen_winograd_preaddition_a(qbl.ul, qbl.ur, qbl.dl, qbl.dr, s1, s2, s3, s4); // feed recursive p1.feed_a(row, col, qbl.ul); p2.feed_a(row, col, qbl.ur); p3.feed_a(row, col, s1); p4.feed_a(row, col, s2); p5.feed_a(row, col, s3); p6.feed_a(row, col, s4); p7.feed_a(row, col, qbl.dr); } inline void feed_b(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) { // partition bl typename Ops::swappable_block_matrix_quarterer qbl(bl); // preadditions swappable_block_matrix_type t1(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), t2(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), t3(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), t4(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()); Ops::strassen_winograd_preaddition_b(qbl.ul, qbl.ur, qbl.dl, qbl.dr, t1, t2, t3, t4); // feed recursive p1.feed_b(row, col, qbl.ul); p2.feed_b(row, col, qbl.dl); p3.feed_b(row, col, t1); p4.feed_b(row, col, t2); p5.feed_b(row, col, t3); p6.feed_b(row, col, qbl.dr); p7.feed_b(row, col, t4); } inline void multiply() { p1.multiply(); p2.multiply(); p3.multiply(); p4.multiply(); p5.multiply(); p6.multiply(); p7.multiply(); } inline void read_and_add(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) { // partition bl typename Ops::swappable_block_matrix_quarterer qbl(bl); // postadditions swappable_block_matrix_type px(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()); p2.read_and_add(row, col, qbl.ul); p1.read_and_add(row, col, px); Ops::element_op(qbl.ul, px, typename Ops::addition()); p4.read_and_add(row, col, px); Ops::element_op(qbl.ur, px, typename Ops::addition()); p5.read_and_add(row, col, px); Ops::element_op_twice_nontransposed(qbl.dl, qbl.dr, px, typename Ops::addition()); px.set_zero(); p7.read_and_add(row, col, qbl.dl); p3.read_and_add(row, col, px); Ops::element_op_twice_nontransposed(qbl.dr, qbl.ur, px, typename Ops::addition()); p6.read_and_add(row, col, qbl.ur); } inline static unsigned_type get_num_temp_grains() { return smaller_feedable_strassen_winograd_ab::get_num_temp_grains() + (4 ^ Level) * 2; } }; template struct feedable_strassen_winograd_block_grained { typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename swappable_block_matrix_type::swappable_block_identifier_type swappable_block_identifier_type; typedef typename swappable_block_matrix_type::size_type size_type; typedef matrix_operations Ops; typedef static_quadtree bt; swappable_block_matrix_type a, b, c; inline feedable_strassen_winograd_block_grained( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : a(existing_a, n, l, a_from_row, a_from_col), b(existing_b, n, l, b_from_row, b_from_col), c(bs_c, n, m) { } inline feedable_strassen_winograd_block_grained( const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : a(existing_a, n, l, a_from_row, a_from_col), b(bs_c, n, l), c(bs_c, n, m) { } inline feedable_strassen_winograd_block_grained( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) : a(bs_c, n, l), b(existing_b, n, l, b_from_row, b_from_col), c(bs_c, n, m) { } inline feedable_strassen_winograd_block_grained( block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) : a(bs_c, n, l), b(bs_c, n, l), c(bs_c, n, m) { } inline void feed_a(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) { if (! AExists) { // copy bl to a from (row, col) (assuming a from (row, col) == 0) swappable_block_matrix_type at(a, bl.get_height(), bl.get_width(), row, col); Ops::element_op(at, bl, typename Ops::addition()); } } inline void feed_b(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) { if (! BExists) { // copy bl(0,0) to b(row, col) (assuming b from (row, col) == 0) swappable_block_matrix_type bt(b, bl.get_height(), bl.get_width(), row, col); Ops::element_op(bt, bl, typename Ops::addition()); } } inline void multiply() { matrix_operations:: multi_level_strassen_winograd_multiply_and_add_block_grained(a, b, c); if (! AExists) a.set_zero(); if (! BExists) b.set_zero(); } inline void read_and_add(const size_type& row, const size_type& col, swappable_block_matrix_type& bl) { // add c from (row, col) to bl swappable_block_matrix_type ct(c, bl.get_height(), bl.get_width(), row, col); Ops::element_op(bl, ct, typename Ops::addition()); ct.set_zero(); } inline static unsigned_type get_num_temp_grains() { return 0; } }; template struct matrix_to_quadtree_block_grained { typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::size_type size_type; typedef matrix_to_quadtree_block_grained smaller_matrix_to_quadtree_block_grained; smaller_matrix_to_quadtree_block_grained ul, ur, dl, dr; inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type & matrix) : ul(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, 0), ur(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, matrix.get_width()/2), dl(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, 0), dr(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, matrix.get_width()/2) { assert(! (matrix.get_height() % 2 | matrix.get_width() % 2)); } inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type & matrix, const size_type height, const size_type width, const size_type from_row, const size_type from_col) : ul(matrix, height/2, width/2, from_row, from_col), ur(matrix, height/2, width/2, from_row, from_col + width/2), dl(matrix, height/2, width/2, from_row + height/2, from_col), dr(matrix, height/2, width/2, from_row + height/2, from_col + width/2) { assert(! (height % 2 | width % 2)); } inline swappable_block_matrix_type operator () (const size_type& row, const size_type& col) { return swappable_block_matrix_type(ul(row, col), ur(row, col), dl(row, col), dr(row, col)); } inline const size_type get_height() { return ul.get_height(); } inline const size_type get_width() { return ul.get_width(); } }; template struct matrix_to_quadtree_block_grained { typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::size_type size_type; swappable_block_matrix_type m; inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type& matrix) : m(matrix, matrix.get_height(), matrix.get_width(), 0, 0) { assert(! (matrix.get_height() % Granularity | matrix.get_width() % Granularity)); } inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type& matrix, const size_type height, const size_type width, const size_type from_row, const size_type from_col) : m(matrix, height, width, from_row, from_col) { assert(! (matrix.get_height() % Granularity | matrix.get_width() % Granularity)); } inline swappable_block_matrix_type operator () (const size_type& row, const size_type& col) { return swappable_block_matrix_type(m, Granularity, Granularity, row * Granularity, col * Granularity); } inline const size_type get_height() { return m.get_height() / Granularity; } inline const size_type get_width() { return m.get_width() / Granularity; } }; template struct matrix_operations { // tuning-parameter: Only matrices larger than this (in blocks) are processed by Strassen-Winograd. // you have to adapt choose_level_for_feedable_sw, too static const int_type strassen_winograd_base_case_size; typedef swappable_block_matrix swappable_block_matrix_type; typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; typedef typename swappable_block_matrix_type::swappable_block_identifier_type swappable_block_identifier_type; typedef typename block_scheduler_type::internal_block_type internal_block_type; typedef typename swappable_block_matrix_type::size_type size_type; typedef column_vector column_vector_type; typedef row_vector row_vector_type; typedef typename column_vector_type::size_type vector_size_type; // +-+-+-+ addition +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ struct addition { /* op(c,a,b) means c = a b e.g. assign sum * op(c,a) means c = a e.g. add up * op(a) means a e.g. sign * * it should hold: * op(c,0,0) equivalent c = 0 * op(c=0,a) equivalent c = op(a) * op(c,0) equivalent {} */ inline ValueType& operator () (ValueType& c, const ValueType& a, const ValueType& b) { return c = a + b; } inline ValueType& operator () (ValueType& c, const ValueType& a) { return c += a; } inline ValueType operator () (const ValueType& a) { return +a; } }; struct subtraction { inline ValueType& operator () (ValueType& c, const ValueType& a, const ValueType& b) { return c = a - b; } inline ValueType& operator () (ValueType& c, const ValueType& a) { return c -= a; } inline ValueType operator () (const ValueType& a) { return -a; } }; struct scalar_multiplication { inline scalar_multiplication(const ValueType scalar = 1) : s(scalar) { } inline ValueType& operator () (ValueType& c, const ValueType& a) { return c = a * s; } inline ValueType operator () (const ValueType& a) { return a * s; } inline operator const ValueType& () { return s; } const ValueType s; }; // element_op(C,A,B) calculates C = A B template static swappable_block_matrix_type& element_op(swappable_block_matrix_type& C, const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, Op op = Op()) { for (size_type row = 0; row < C.get_height(); ++row) for (size_type col = 0; col < C.get_width(); ++col) element_op_swappable_block( C(row, col), C.is_transposed(), C.bs, A(row, col), A.is_transposed(), A.bs, B(row, col), B.is_transposed(), B.bs, op); return C; } // element_op(C,A) calculates C = A template static swappable_block_matrix_type& element_op(swappable_block_matrix_type& C, const swappable_block_matrix_type& A, Op op = Op()) { for (size_type row = 0; row < C.get_height(); ++row) for (size_type col = 0; col < C.get_width(); ++col) element_op_swappable_block( C(row, col), C.is_transposed(), C.bs, A(row, col), A.is_transposed(), A.bs, op); return C; } // element_op(C) calculates C = C template static swappable_block_matrix_type& element_op(swappable_block_matrix_type& C, Op op = Op()) { for (size_type row = 0; row < C.get_height(); ++row) for (size_type col = 0; col < C.get_width(); ++col) element_op_swappable_block( C(row, col), C.bs, op); return C; } // calculates c = a b template static void element_op_swappable_block( const swappable_block_identifier_type c, const bool c_is_transposed, block_scheduler_type& bs_c, const swappable_block_identifier_type a, bool a_is_transposed, block_scheduler_type& bs_a, const swappable_block_identifier_type b, bool b_is_transposed, block_scheduler_type& bs_b, Op op = Op()) { if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_addition_calls; // check if zero-block (== ! initialized) if (! bs_a.is_initialized(a) && ! bs_b.is_initialized(b)) { // => a and b are zero -> set c zero bs_c.deinitialize(c); if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_additions_saved_through_zero; return; } a_is_transposed = a_is_transposed != c_is_transposed; b_is_transposed = b_is_transposed != c_is_transposed; if (! bs_a.is_initialized(a)) { // a is zero -> copy b internal_block_type& ic = bs_c.acquire(c, true), & ib = bs_b.acquire(b); if (! bs_c.is_simulating()) { if (b_is_transposed) low_level_matrix_binary_ass_op(&ic[0], 0, &ib[0], op); else low_level_matrix_binary_ass_op(&ic[0], 0, &ib[0], op); } bs_b.release(b, false); bs_c.release(c, true); } else if (! bs_b.is_initialized(b)) { // b is zero -> copy a internal_block_type& ic = bs_c.acquire(c, true), & ia = bs_a.acquire(a); if (! bs_c.is_simulating()) { if (a_is_transposed) low_level_matrix_binary_ass_op(&ic[0], &ia[0], 0, op); else low_level_matrix_binary_ass_op(&ic[0], &ia[0], 0, op); } bs_a.release(a, false); bs_c.release(c, true); } else { internal_block_type& ic = bs_c.acquire(c, true), & ia = bs_a.acquire(a), & ib = bs_b.acquire(b); if (! bs_c.is_simulating()) { if (a_is_transposed) { if (b_is_transposed) low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); else low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); } else { if (b_is_transposed) low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); else low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); } } bs_a.release(a, false); bs_b.release(b, false); bs_c.release(c, true); } } // calculates c = a template static void element_op_swappable_block( const swappable_block_identifier_type c, const bool c_is_transposed, block_scheduler_type& bs_c, const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, Op op = Op()) { if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_addition_calls; // check if zero-block (== ! initialized) if (! bs_a.is_initialized(a)) { // => b is zero => nothing to do if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_additions_saved_through_zero; return; } const bool c_is_zero = ! bs_c.is_initialized(c); // acquire internal_block_type& ic = bs_c.acquire(c, c_is_zero), & ia = bs_a.acquire(a); // add if (! bs_c.is_simulating()) { if (c_is_zero) { if (c_is_transposed == a_is_transposed) low_level_matrix_unary_op(&ic[0], &ia[0], op); else low_level_matrix_unary_op(&ic[0], &ia[0], op); } else { if (c_is_transposed == a_is_transposed) low_level_matrix_unary_ass_op(&ic[0], &ia[0], op); else low_level_matrix_unary_ass_op(&ic[0], &ia[0], op); } } // release bs_c.release(c, true); bs_a.release(a, false); } // calculates c = c template static void element_op_swappable_block( const swappable_block_identifier_type c, block_scheduler_type& bs_c, Op op = Op()) { if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_addition_calls; // check if zero-block (== ! initialized) if (! bs_c.is_initialized(c)) { // => c is zero => nothing to do if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_additions_saved_through_zero; return; } // acquire internal_block_type& ic = bs_c.acquire(c); // add if (! bs_c.is_simulating()) low_level_matrix_unary_op(&ic[0], &ic[0], op); // release bs_c.release(c, true); } // additions for strassen-winograd inline static void strassen_winograd_preaddition_a(swappable_block_matrix_type& a11, swappable_block_matrix_type& a12, swappable_block_matrix_type& a21, swappable_block_matrix_type& a22, swappable_block_matrix_type& s1, swappable_block_matrix_type& s2, swappable_block_matrix_type& s3, swappable_block_matrix_type& s4) { for (size_type row = 0; row < a11.get_height(); ++row) for (size_type col = 0; col < a11.get_width(); ++col) { op_swappable_block_nontransposed(s3, a11, subtraction(), a21, row, col); op_swappable_block_nontransposed(s1, a21, addition(), a22, row, col); op_swappable_block_nontransposed(s2, s1, subtraction(), a11, row, col); op_swappable_block_nontransposed(s4, a12, subtraction(), s2, row, col); } } inline static void strassen_winograd_preaddition_b(swappable_block_matrix_type& b11, swappable_block_matrix_type& b12, swappable_block_matrix_type& b21, swappable_block_matrix_type& b22, swappable_block_matrix_type& t1, swappable_block_matrix_type& t2, swappable_block_matrix_type& t3, swappable_block_matrix_type& t4) { for (size_type row = 0; row < b11.get_height(); ++row) for (size_type col = 0; col < b11.get_width(); ++col) { op_swappable_block_nontransposed(t3, b22, subtraction(), b12, row, col); op_swappable_block_nontransposed(t1, b12, subtraction(), b11, row, col); op_swappable_block_nontransposed(t2, b22, subtraction(), t1, row, col); op_swappable_block_nontransposed(t4, b21, subtraction(), t2, row, col); } } inline static void strassen_winograd_postaddition(swappable_block_matrix_type& c11, // = p2 swappable_block_matrix_type& c12, // = p6 swappable_block_matrix_type& c21, // = p7 swappable_block_matrix_type& c22, // = p4 swappable_block_matrix_type& p1, swappable_block_matrix_type& p3, swappable_block_matrix_type& p5) { for (size_type row = 0; row < c11.get_height(); ++row) for (size_type col = 0; col < c11.get_width(); ++col) { op_swappable_block_nontransposed(c11, addition(), p1, row, col); // (u1) op_swappable_block_nontransposed( p1, addition(), c22, row, col); // (u2) op_swappable_block_nontransposed( p5, addition(), p1, row, col); // (u3) op_swappable_block_nontransposed(c21, addition(), p5, row, col); // (u4) op_swappable_block_nontransposed(c22, p5, addition(), p3, row, col); // (u5) op_swappable_block_nontransposed( p1, addition(), p3, row, col); // (u6) op_swappable_block_nontransposed(c12, addition(), p1, row, col); // (u7) } } // calculates c1 += a; c2 += a template inline static void element_op_twice_nontransposed(swappable_block_matrix_type& c1, swappable_block_matrix_type& c2, const swappable_block_matrix_type& a, Op op = Op()) { for (size_type row = 0; row < a.get_height(); ++row) for (size_type col = 0; col < a.get_width(); ++col) { element_op_swappable_block( c1(row, col), false, c1.bs, a(row, col), false, a.bs, op); element_op_swappable_block( c2(row, col), false, c2.bs, a(row, col), false, a.bs, op); } } template inline static void op_swappable_block_nontransposed(swappable_block_matrix_type& c, swappable_block_matrix_type& a, Op op, swappable_block_matrix_type& b, size_type& row, size_type& col) { element_op_swappable_block( c(row, col), false, c.bs, a(row, col), false, a.bs, b(row, col), false, b.bs, op); } template inline static void op_swappable_block_nontransposed(swappable_block_matrix_type& c, Op op, swappable_block_matrix_type& a, size_type& row, size_type& col) { element_op_swappable_block( c(row, col), false, c.bs, a(row, col), false, a.bs, op); } // +-+ end addition +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+ matrix multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /* n, m and l denote the three dimensions of a matrix multiplication, according to the following ascii-art diagram: * * +--m--+ * +----l-----+ | | +--m--+ * | | | | | | * n A | • l B | = n C | * | | | | | | * +----------+ | | +-----+ * +-----+ * * The index-variables are called i, j, k for dimension * n, m, l . */ // requires height and width divisible by 2 struct swappable_block_matrix_quarterer { swappable_block_matrix_type upleft, upright, downleft, downright, & ul, & ur, & dl, & dr; swappable_block_matrix_quarterer(const swappable_block_matrix_type & whole) : upleft (whole, whole.get_height()/2, whole.get_width()/2, 0, 0), upright (whole, whole.get_height()/2, whole.get_width()/2, 0, whole.get_width()/2), downleft (whole, whole.get_height()/2, whole.get_width()/2, whole.get_height()/2, 0), downright(whole, whole.get_height()/2, whole.get_width()/2, whole.get_height()/2, whole.get_width()/2), ul(upleft), ur(upright), dl(downleft), dr(downright) { assert(! (whole.get_height() % 2 | whole.get_width() % 2)); } }; struct swappable_block_matrix_padding_quarterer { swappable_block_matrix_type upleft, upright, downleft, downright, & ul, & ur, & dl, & dr; swappable_block_matrix_padding_quarterer(const swappable_block_matrix_type & whole) : upleft (whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), 0, 0), upright (whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), 0, div_ceil(whole.get_width(),2)), downleft (whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), div_ceil(whole.get_height(),2), 0), downright(whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2)), ul(upleft), ur(upright), dl(downleft), dr(downright) {} }; struct swappable_block_matrix_approximative_quarterer { swappable_block_matrix_type upleft, upright, downleft, downright, & ul, & ur, & dl, & dr; swappable_block_matrix_approximative_quarterer(const swappable_block_matrix_type & whole) : upleft (whole, whole.get_height()/2, whole.get_width()/2, 0, 0), upright (whole, whole.get_height()/2, whole.get_width() - whole.get_width()/2, 0, whole.get_width()/2), downleft (whole, whole.get_height() - whole.get_height()/2, whole.get_width()/2, whole.get_height()/2, 0), downright(whole, whole.get_height() - whole.get_height()/2, whole.get_width() - whole.get_width()/2, whole.get_height()/2, whole.get_width()/2), ul(upleft), ur(upright), dl(downleft), dr(downright) {} }; //! calculates C = A * B + C // requires fitting dimensions static swappable_block_matrix_type& multi_level_strassen_winograd_multiply_and_add_block_grained(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { int_type num_levels = ilog2_ceil(std::min(A.get_width(), std::min(C.get_width(), C.get_height()))); if (num_levels > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) { if (num_levels > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS) num_levels = STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS; swappable_block_matrix_type padded_a(A, round_up_to_power_of_two(A.get_height(), num_levels), round_up_to_power_of_two(A.get_width(), num_levels), 0, 0), padded_b(B, round_up_to_power_of_two(B.get_height(), num_levels), round_up_to_power_of_two(B.get_width(), num_levels), 0, 0), padded_c(C, round_up_to_power_of_two(C.get_height(), num_levels), round_up_to_power_of_two(C.get_width(), num_levels), 0, 0); switch (num_levels) { #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 5 && 5 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) case 5: use_feedable_sw_block_grained<5>(padded_a, padded_a, padded_c); break; #endif #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 4 && 4 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) case 4: use_feedable_sw_block_grained<4>(padded_a, padded_a, padded_c); break; #endif #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 3 && 3 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) case 3: use_feedable_sw_block_grained<3>(padded_a, padded_a, padded_c); break; #endif #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 2 && 2 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) case 2: use_feedable_sw_block_grained<2>(padded_a, padded_a, padded_c); break; #endif default: // only here in case of wrong bounds strassen_winograd_multiply_and_add_interleaved(A, B, C); break; } } else // base case strassen_winograd_multiply_and_add_interleaved(A, B, C); return C; } // input matrices have to be padded template static void use_feedable_sw_block_grained(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { const unsigned granularity = 1; feedable_strassen_winograd_block_grained fsw(A, 0, 0, C.bs, C.get_height(), C.get_width(), A.get_width(), B, 0, 0); // preadditions for A { matrix_to_quadtree_block_grained mtq_a(A); for (size_type row = 0; row < mtq_a.get_height(); ++row) for (size_type col = 0; col < mtq_a.get_width(); ++col) fsw.feed_a(row, col, mtq_a(row, col)); } // preadditions for B { matrix_to_quadtree_block_grained mtq_b(B); for (size_type row = 0; row < mtq_b.get_height(); ++row) for (size_type col = 0; col < mtq_b.get_width(); ++col) fsw.feed_b(row, col, mtq_b(row, col)); } // recursive multiplications fsw.multiply(); // postadditions { matrix_to_quadtree_block_grained mtq_c(C); for (size_type row = 0; row < mtq_c.get_height(); ++row) for (size_type col = 0; col < mtq_c.get_width(); ++col) fsw.read_and_add(row, col, mtq_c(row, col)); } } //! calculates C = A * B + C // requires fitting dimensions static swappable_block_matrix_type& multi_level_strassen_winograd_multiply_and_add(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { int_type p = ilog2_ceil(std::min(A.get_width(), std::min(C.get_width(), C.get_height()))); swappable_block_matrix_type padded_a(A, round_up_to_power_of_two(A.get_height(), p), round_up_to_power_of_two(A.get_width(), p), 0, 0), padded_b(B, round_up_to_power_of_two(B.get_height(), p), round_up_to_power_of_two(B.get_width(), p), 0, 0), padded_c(C, round_up_to_power_of_two(C.get_height(), p), round_up_to_power_of_two(C.get_width(), p), 0, 0); choose_level_for_feedable_sw(padded_a, padded_b, padded_c); return C; } // input matrices have to be padded static void choose_level_for_feedable_sw(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { switch (ilog2_ceil(std::min(A.get_width(), std::min(C.get_width(), C.get_height())))) { default: /* use_feedable_sw<4>(A, B, C); break; case 3: use_feedable_sw<3>(A, B, C); break; case 2:*/ use_feedable_sw<2>(A, B, C); break; case 1: /*use_feedable_sw<1>(A, B, C); break;*/ case 0: // base case recursive_multiply_and_add(A, B, C); break; } } // input matrices have to be padded template static void use_feedable_sw(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { feedable_strassen_winograd fsw(A, 0, 0, C.bs, C.get_height(), C.get_width(), A.get_width(), B, 0, 0); // preadditions for A matrix_to_quadtree mtq_a(A); for (size_type block_row = 0; block_row < mtq_a.get_height_in_blocks(); ++block_row) for (size_type block_col = 0; block_col < mtq_a.get_width_in_blocks(); ++block_col) { fsw.begin_feeding_a_block(block_row, block_col, mtq_a.begin_reading_block(block_row, block_col)); #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type element_row_in_block = 0; element_row_in_block < int_type(BlockSideLength); ++element_row_in_block) for (int_type element_col_in_block = 0; element_col_in_block < int_type(BlockSideLength); ++element_col_in_block) fsw.feed_a_element(element_row_in_block * BlockSideLength + element_col_in_block, mtq_a.read_element(element_row_in_block * BlockSideLength + element_col_in_block)); fsw.end_feeding_a_block(block_row, block_col, mtq_a.end_reading_block(block_row, block_col)); } // preadditions for B matrix_to_quadtree mtq_b(B); for (size_type block_row = 0; block_row < mtq_b.get_height_in_blocks(); ++block_row) for (size_type block_col = 0; block_col < mtq_b.get_width_in_blocks(); ++block_col) { fsw.begin_feeding_b_block(block_row, block_col, mtq_b.begin_reading_block(block_row, block_col)); #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type element_row_in_block = 0; element_row_in_block < int_type(BlockSideLength); ++element_row_in_block) for (int_type element_col_in_block = 0; element_col_in_block < int_type(BlockSideLength); ++element_col_in_block) fsw.feed_b_element(element_row_in_block * BlockSideLength + element_col_in_block, mtq_b.read_element(element_row_in_block * BlockSideLength + element_col_in_block)); fsw.end_feeding_b_block(block_row, block_col, mtq_b.end_reading_block(block_row, block_col)); } // recursive multiplications fsw.multiply(); // postadditions matrix_to_quadtree mtq_c(C); for (size_type block_row = 0; block_row < mtq_c.get_height_in_blocks(); ++block_row) for (size_type block_col = 0; block_col < mtq_c.get_width_in_blocks(); ++block_col) { mtq_c.begin_feeding_block(block_row, block_col, fsw.begin_reading_block(block_row, block_col)); #if STXXL_PARALLEL #pragma omp parallel for #endif for (int_type element_row_in_block = 0; element_row_in_block < int_type(BlockSideLength); ++element_row_in_block) for (int_type element_col_in_block = 0; element_col_in_block < int_type(BlockSideLength); ++element_col_in_block) mtq_c.feed_and_add_element(element_row_in_block * BlockSideLength + element_col_in_block, fsw.read_element(element_row_in_block * BlockSideLength + element_col_in_block)); mtq_c.end_feeding_block(block_row, block_col, fsw.end_reading_block(block_row, block_col)); } } //! calculates C = A * B // assumes fitting dimensions static swappable_block_matrix_type& strassen_winograd_multiply(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { // base case if (C.get_height() <= strassen_winograd_base_case_size || C.get_width() <= strassen_winograd_base_case_size || A.get_width() <= strassen_winograd_base_case_size) { C.set_zero(); return recursive_multiply_and_add(A, B, C); } // partition matrix swappable_block_matrix_padding_quarterer qa(A), qb(B), qc(C); // preadditions swappable_block_matrix_type s1(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), s2(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), s3(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), s4(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), t1(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), t2(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), t3(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), t4(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()); strassen_winograd_preaddition_a(qa.ul, qa.ur, qa.dl, qa.dr, s1, s2, s3, s4); strassen_winograd_preaddition_b(qb.ul, qb.ur, qb.dl, qb.dr, t1, t2, t3, t4); // recursive multiplications swappable_block_matrix_type p1(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()), // p2 stored in qc.ul p3(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()), // p4 stored in qc.dr p5(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()); // p6 stored in qc.ur // p7 stored in qc.dl strassen_winograd_multiply(qa.ul, qb.ul, p1); strassen_winograd_multiply(qa.ur, qb.dl, qc.ul); strassen_winograd_multiply( s1, t1, p3); strassen_winograd_multiply( s2, t2, qc.dr); strassen_winograd_multiply( s3, t3, p5); strassen_winograd_multiply( s4, qb.dr, qc.ur); strassen_winograd_multiply(qa.dr, t4, qc.dl); // postadditions strassen_winograd_postaddition(qc.ul, qc.ur, qc.dl, qc.dr, p1, p3, p5); return C; } //! calculates C = A * B + C // assumes fitting dimensions static swappable_block_matrix_type& strassen_winograd_multiply_and_add_interleaved(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { // base case if (C.get_height() <= strassen_winograd_base_case_size || C.get_width() <= strassen_winograd_base_case_size || A.get_width() <= strassen_winograd_base_case_size) return recursive_multiply_and_add(A, B, C); // partition matrix swappable_block_matrix_padding_quarterer qa(A), qb(B), qc(C); // preadditions swappable_block_matrix_type s1(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), s2(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), s3(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), s4(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), t1(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), t2(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), t3(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), t4(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()); strassen_winograd_preaddition_a(qa.ul, qa.ur, qa.dl, qa.dr, s1, s2, s3, s4); strassen_winograd_preaddition_b(qb.ul, qb.ur, qb.dl, qb.dr, t1, t2, t3, t4); // recursive multiplications and postadditions swappable_block_matrix_type px(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()); strassen_winograd_multiply_and_add_interleaved(qa.ur, qb.dl, qc.ul); // p2 strassen_winograd_multiply_and_add_interleaved(qa.ul, qb.ul, px); // p1 element_op(qc.ul, px); strassen_winograd_multiply_and_add_interleaved(s2, t2, px); // p4 s2.set_zero(); t2.set_zero(); element_op(qc.ur, px); strassen_winograd_multiply_and_add_interleaved(s3, t3, px); // p5 s3.set_zero(); t3.set_zero(); element_op_twice_nontransposed(qc.dl, qc.dr, px); px.set_zero(); strassen_winograd_multiply_and_add_interleaved(qa.dr, t4, qc.dl); // p7 t4.set_zero(); strassen_winograd_multiply_and_add_interleaved(s1, t1, px); // p3 s1.set_zero(); t1.set_zero(); element_op_twice_nontransposed(qc.dr, qc.ur, px); px.set_zero(); strassen_winograd_multiply_and_add_interleaved(s4, qb.dr, qc.ur); // p6 return C; } //! calculates C = A * B + C // assumes fitting dimensions static swappable_block_matrix_type& strassen_winograd_multiply_and_add(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { // base case if (C.get_height() <= strassen_winograd_base_case_size || C.get_width() <= strassen_winograd_base_case_size || A.get_width() <= strassen_winograd_base_case_size) return recursive_multiply_and_add(A, B, C); // partition matrix swappable_block_matrix_padding_quarterer qa(A), qb(B), qc(C); // preadditions swappable_block_matrix_type s1(C.bs, qa.ul.get_height(), qa.ul.get_width()), s2(C.bs, qa.ul.get_height(), qa.ul.get_width()), s3(C.bs, qa.ul.get_height(), qa.ul.get_width()), s4(C.bs, qa.ul.get_height(), qa.ul.get_width()), t1(C.bs, qb.ul.get_height(), qb.ul.get_width()), t2(C.bs, qb.ul.get_height(), qb.ul.get_width()), t3(C.bs, qb.ul.get_height(), qb.ul.get_width()), t4(C.bs, qb.ul.get_height(), qb.ul.get_width()); element_op(s3, qa.ul, qa.dl); element_op(s1, qa.dl, qa.dr); element_op(s2, s1, qa.ul); element_op(s4, qa.ur, s2); element_op(t3, qb.dr, qb.ur); element_op(t1, qb.ur, qb.ul); element_op(t2, qb.dr, t1); element_op(t4, qb.dl, t2); // recursive multiplications and postadditions swappable_block_matrix_type px(C.bs, qc.ul.get_height(), qc.ul.get_width()); strassen_winograd_multiply_and_add(qa.ur, qb.dl, qc.ul); // p2 strassen_winograd_multiply_and_add(qa.ul, qb.ul, px); // p1 element_op(qc.ul, px); strassen_winograd_multiply_and_add(s2, t2, px); // p4 element_op(qc.ur, px); strassen_winograd_multiply_and_add(s3, t3, px); // p5 element_op(qc.dl, px); element_op(qc.dr, px); px.set_zero(); strassen_winograd_multiply_and_add(qa.dr, t4, qc.dl); // p7 strassen_winograd_multiply_and_add(s1, t1, px); // p3 element_op(qc.dr, px); element_op(qc.ur, px); strassen_winograd_multiply_and_add(s4, qb.dr, qc.ur); // p6 return C; } //! calculates C = A * B + C // assumes fitting dimensions static swappable_block_matrix_type& recursive_multiply_and_add(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { // catch empty intervals if (C.get_height() * C.get_width() * A.get_width() == 0) return C; // base case if ((C.get_height() == 1) + (C.get_width() == 1) + (A.get_width() == 1) >= 2) return naive_multiply_and_add(A, B, C); // partition matrix swappable_block_matrix_approximative_quarterer qa(A), qb(B), qc(C); // recursive multiplication // The order of recursive calls is optimized to enhance locality. C has priority because it has to be read and written. recursive_multiply_and_add(qa.ul, qb.ul, qc.ul); recursive_multiply_and_add(qa.ur, qb.dl, qc.ul); recursive_multiply_and_add(qa.ur, qb.dr, qc.ur); recursive_multiply_and_add(qa.ul, qb.ur, qc.ur); recursive_multiply_and_add(qa.dl, qb.ur, qc.dr); recursive_multiply_and_add(qa.dr, qb.dr, qc.dr); recursive_multiply_and_add(qa.dr, qb.dl, qc.dl); recursive_multiply_and_add(qa.dl, qb.ul, qc.dl); return C; } //! calculates C = A * B + C // requires fitting dimensions static swappable_block_matrix_type& naive_multiply_and_add(const swappable_block_matrix_type& A, const swappable_block_matrix_type& B, swappable_block_matrix_type& C) { const size_type& n = C.get_height(), & m = C.get_width(), & l = A.get_width(); for (size_type i = 0; i < n; ++i) for (size_type j = 0; j < m; ++j) for (size_type k = 0; k < l; ++k) multiply_and_add_swappable_block(A(i, k), A.is_transposed(), A.bs, B(k, j), B.is_transposed(), B.bs, C(i, j), C.is_transposed(), C.bs); return C; } static void multiply_and_add_swappable_block( const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, const swappable_block_identifier_type b, const bool b_is_transposed, block_scheduler_type& bs_b, const swappable_block_identifier_type c, const bool c_is_transposed, block_scheduler_type& bs_c) { if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_multiplication_calls; // check if zero-block (== ! initialized) if (! bs_a.is_initialized(a) || ! bs_b.is_initialized(b)) { // => one factor is zero => product is zero if (! bs_c.is_simulating()) ++matrix_operation_statistic::get_instance()->block_multiplications_saved_through_zero; return; } // acquire ValueType* ap = bs_a.acquire(a).begin(), * bp = bs_b.acquire(b).begin(), * cp = bs_c.acquire(c).begin(); // multiply if (! bs_c.is_simulating()) low_level_matrix_multiply_and_add (ap, a_is_transposed, bp, b_is_transposed, cp, c_is_transposed); // release bs_a.release(a, false); bs_b.release(b, false); bs_c.release(c, true); } // +-+ end matrix multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+ matrix-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //! calculates z = A * x static column_vector_type& recursive_matrix_col_vector_multiply_and_add(const swappable_block_matrix_type& A, const column_vector_type& x, column_vector_type& z, const vector_size_type offset_x = 0, const vector_size_type offset_z = 0) { // catch empty intervals if (A.get_height() * A.get_width() == 0) return z; // base case if (A.get_height() == 1 || A.get_width() == 1) return naive_matrix_col_vector_multiply_and_add(A, x, z, offset_x, offset_z); // partition matrix swappable_block_matrix_approximative_quarterer qa(A); // recursive multiplication // The order of recursive calls is optimized to enhance locality. recursive_matrix_col_vector_multiply_and_add(qa.ul, x, z, offset_x, offset_z ); recursive_matrix_col_vector_multiply_and_add(qa.ur, x, z, offset_x + qa.ul.get_width(), offset_z ); recursive_matrix_col_vector_multiply_and_add(qa.dr, x, z, offset_x + qa.ul.get_width(), offset_z + qa.ul.get_height()); recursive_matrix_col_vector_multiply_and_add(qa.dl, x, z, offset_x, offset_z + qa.ul.get_height()); return z; } static column_vector_type& naive_matrix_col_vector_multiply_and_add(const swappable_block_matrix_type& A, const column_vector_type& x, column_vector_type& z, const vector_size_type offset_x = 0, const vector_size_type offset_z = 0) { for (size_type row = 0; row < A.get_height(); ++row) for (size_type col = 0; col < A.get_width(); ++col) matrix_col_vector_multiply_and_add_swappable_block(A(row, col), A.is_transposed(), A.bs, x, z, (offset_x + col) * BlockSideLength, (offset_z + row) * BlockSideLength); return z; } static void matrix_col_vector_multiply_and_add_swappable_block( const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, const column_vector_type& x, column_vector_type& z, const vector_size_type offset_x = 0, const vector_size_type offset_z = 0) { // check if zero-block (== ! initialized) if (! bs_a.is_initialized(a)) { // => matrix is zero => product is zero return; } // acquire internal_block_type& ia = bs_a.acquire(a); // multiply if (! bs_a.is_simulating()) { int_type row_limit = std::min(BlockSideLength, unsigned(z.size() - offset_z)), col_limit = std::min(BlockSideLength, unsigned(x.size() - offset_x)); if (a_is_transposed) for (int_type col = 0; col < col_limit; ++col) for (int_type row = 0; row < row_limit; ++row) z[offset_z + row] += x[offset_x + col] * ia[row + col * BlockSideLength]; else for (int_type row = 0; row < row_limit; ++row) for (int_type col = 0; col < col_limit; ++col) z[offset_z + row] += x[offset_x + col] * ia[row * BlockSideLength + col]; } // release bs_a.release(a, false); } //! calculates z = y * A static row_vector_type& recursive_matrix_row_vector_multiply_and_add(const row_vector_type& y, const swappable_block_matrix_type& A, row_vector_type& z, const vector_size_type offset_y = 0, const vector_size_type offset_z = 0) { // catch empty intervals if (A.get_height() * A.get_width() == 0) return z; // base case if (A.get_height() == 1 || A.get_width() == 1) return naive_matrix_row_vector_multiply_and_add(y, A, z, offset_y, offset_z); // partition matrix swappable_block_matrix_approximative_quarterer qa(A); // recursive multiplication // The order of recursive calls is optimized to enhance locality. recursive_matrix_row_vector_multiply_and_add(y, qa.ul, z, offset_y, offset_z ); recursive_matrix_row_vector_multiply_and_add(y, qa.dl, z, offset_y + qa.ul.get_height(), offset_z ); recursive_matrix_row_vector_multiply_and_add(y, qa.dr, z, offset_y + qa.ul.get_height(), offset_z + qa.ul.get_width()); recursive_matrix_row_vector_multiply_and_add(y, qa.ur, z, offset_y, offset_z + qa.ul.get_width()); return z; } static row_vector_type& naive_matrix_row_vector_multiply_and_add(const row_vector_type& y, const swappable_block_matrix_type& A, row_vector_type& z, const vector_size_type offset_y = 0, const vector_size_type offset_z = 0) { for (size_type row = 0; row < A.get_height(); ++row) for (size_type col = 0; col < A.get_width(); ++col) matrix_row_vector_multiply_and_add_swappable_block(y, A(row, col), A.is_transposed(), A.bs, z, (offset_y + row) * BlockSideLength, (offset_z + col) * BlockSideLength); return z; } static void matrix_row_vector_multiply_and_add_swappable_block(const row_vector_type& y, const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, row_vector_type& z, const vector_size_type offset_y = 0, const vector_size_type offset_z = 0) { // check if zero-block (== ! initialized) if (! bs_a.is_initialized(a)) { // => matrix is zero => product is zero return; } // acquire internal_block_type& ia = bs_a.acquire(a); // multiply if (! bs_a.is_simulating()) { int_type row_limit = std::min(BlockSideLength, unsigned(y.size() - offset_y)), col_limit = std::min(BlockSideLength, unsigned(z.size() - offset_z)); if (a_is_transposed) for (int_type col = 0; col < col_limit; ++col) for (int_type row = 0; row < row_limit; ++row) z[offset_z + col] += ia[row + col * BlockSideLength] * y[offset_y + row]; else for (int_type row = 0; row < row_limit; ++row) for (int_type col = 0; col < col_limit; ++col) z[offset_z + col] += ia[row * BlockSideLength + col] * y[offset_y + row]; } // release bs_a.release(a, false); } // +-+ end matrix-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+ vector-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ static void recursive_matrix_from_vectors(swappable_block_matrix_type A, const column_vector_type& l, const row_vector_type& r, vector_size_type offset_l = 0, vector_size_type offset_r = 0) { // catch empty intervals if (A.get_height() * A.get_width() == 0) return; // base case if (A.get_height() == 1 || A.get_width() == 1) { naive_matrix_from_vectors(A, l, r, offset_l, offset_r); return; } // partition matrix swappable_block_matrix_approximative_quarterer qa(A); // recursive creation // The order of recursive calls is optimized to enhance locality. recursive_matrix_from_vectors(qa.ul, l, r, offset_l, offset_r ); recursive_matrix_from_vectors(qa.ur, l, r, offset_l, offset_r + qa.ul.get_width()); recursive_matrix_from_vectors(qa.dr, l, r, offset_l + qa.ul.get_height(), offset_r + qa.ul.get_width()); recursive_matrix_from_vectors(qa.dl, l, r, offset_l + qa.ul.get_height(), offset_r ); } static void naive_matrix_from_vectors(swappable_block_matrix_type A, const column_vector_type& l, const row_vector_type& r, vector_size_type offset_l = 0, vector_size_type offset_r = 0) { for (size_type row = 0; row < A.get_height(); ++row) for (size_type col = 0; col < A.get_width(); ++col) matrix_from_vectors_swappable_block(A(row, col), A.is_transposed(), A.bs, l, r, (offset_l + row) * BlockSideLength, (offset_r + col) * BlockSideLength); } static void matrix_from_vectors_swappable_block(swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, const column_vector_type& l, const row_vector_type& r, vector_size_type offset_l, vector_size_type offset_r) { // acquire internal_block_type& ia = bs_a.acquire(a, true); // multiply if (! bs_a.is_simulating()) { int_type row_limit = std::min(BlockSideLength, unsigned(l.size() - offset_l)), col_limit = std::min(BlockSideLength, unsigned(r.size() - offset_r)); if (a_is_transposed) for (int_type col = 0; col < col_limit; ++col) for (int_type row = 0; row < row_limit; ++row) ia[row + col * BlockSideLength] = l[row + offset_l] * r[col + offset_r]; else for (int_type row = 0; row < row_limit; ++row) for (int_type col = 0; col < col_limit; ++col) ia[row * BlockSideLength + col] = l[row + offset_l] * r[col + offset_r]; } // release bs_a.release(a, true); } // +-+ end vector-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ }; // Adjust choose_level_for_feedable_sw, too! template const int_type matrix_operations::strassen_winograd_base_case_size = 3; } // namespace matrix_local STXXL_END_NAMESPACE #endif // !STXXL_CONTAINERS_MATRIX_ARITHMETIC_HEADER stxxl-1.4.1/include/stxxl/bits/containers/unordered_map.h000644 001411 000144 00000032461 12411453275 023417 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/unordered_map.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_UNORDERED_MAP_HEADER #define STXXL_CONTAINERS_UNORDERED_MAP_HEADER #include #include STXXL_BEGIN_NAMESPACE namespace hash_map { template < class KeyType, class DataType, class HashType, class CompareType, unsigned SubBlockSize, unsigned SubBlocksPerBlock, class Alloc > class hash_map; } // namespace hash_map //! \addtogroup stlcont //! \{ /*! * An external memory implementation of the STL unordered_map container, which * is based on an external memory hash map. For more information see \ref * tutorial_unordered_map. * * \tparam KeyType the key type * \tparam MappedType the mapped type associated with a key * \tparam HashType a hash functional * \tparam CompareType a less comparison relation for KeyType * \tparam SubBlockSize the raw size of a subblock (caching granularity) * (default: 8192) * \tparam SubBlocksPerBlock the number of subblocks per external block * (default: 256 -> 2MB blocks) * \tparam AllocType allocator for internal-memory buffer */ template < class KeyType, class MappedType, class HashType, class CompareType, unsigned SubBlockSize = 8* 1024, unsigned SubBlocksPerBlock = 256, class AllocType = std::allocator > > class unordered_map : private noncopyable { typedef hash_map::hash_map impl_type; impl_type impl; public: //! \name Types //! \{ //! the first template parameter (Key) typedef typename impl_type::key_type key_type; //! the second template parameter (T) typedef typename impl_type::mapped_type mapped_type; //! pair typedef typename impl_type::value_type value_type; //! the third template parameter (HashType) typedef typename impl_type::hasher hasher; //! the fourth template parameter (CompareType) (!!! not: equality compare) typedef typename impl_type::key_compare key_compare; //! the fifth template parameter (AllocType) typedef AllocType allocator_type; typedef typename impl_type::reference reference; typedef typename impl_type::const_reference const_reference; typedef typename impl_type::pointer pointer; typedef typename impl_type::const_pointer const_pointer; typedef typename impl_type::external_size_type size_type; typedef typename impl_type::difference_type difference_type; typedef typename impl_type::external_size_type external_size_type; typedef typename impl_type::internal_size_type internal_size_type; typedef typename impl_type::iterator iterator; typedef typename impl_type::const_iterator const_iterator; //! constructed equality predicate for key typedef typename impl_type::key_equal key_equal; //! \} //! \name Constructors //! \{ /*! * Construct a new hash-map * * \param n initial number of buckets * \param hf hash-function * \param cmp comparator-object * \param buffer_size size of internal-memory buffer in bytes * \param a allocation-strategory for internal-memory buffer */ unordered_map(internal_size_type n = 0, const hasher& hf = hasher(), const key_compare& cmp = key_compare(), internal_size_type buffer_size = 100*1024*1024, const allocator_type& a = allocator_type()) : impl(n, hf, cmp, buffer_size, a) { } /*! * Construct a new hash-map and insert all values in the range [begin,end) * * \param begin beginning of the range * \param end end of the range * \param mem_to_sort internal memory that may be used for * bulk-construction (not to be confused with the buffer-memory) * \param n initial number of buckets * \param hf hash-function * \param cmp comparator-object * \param buffer_size size of internal-memory buffer in bytes * \param a allocation-strategory for internal-memory buffer */ template unordered_map(InputIterator begin, InputIterator end, internal_size_type mem_to_sort = 256*1024*1024, internal_size_type n = 0, const hasher& hf = hasher(), const key_compare& cmp = key_compare(), internal_size_type buffer_size = 100*1024*1024, const allocator_type& a = allocator_type()) : impl(begin, end, mem_to_sort, n, hf, cmp, buffer_size, a) { } //! \} //! \name Size and Capacity //! \{ //! Number of values currently stored. Note: If the correct number is //! currently unknown (because **oblivous-methods** were used), external //! memory will be scanned. external_size_type size() const { return impl.size(); } //! The hash-map may store up to this number of values external_size_type max_size() const { return impl.max_size(); } //! Check if container is empty, see size() about oblivious-methods. bool empty() const { return impl.empty(); } //! \} //! \name Iterators //! \{ //! iterator pointing to the beginnning of the hash-map iterator begin() { return impl.begin(); } //! iterator pointing to the end of the hash-map (iterator-type as //! template-parameter) iterator end() { return impl.end(); } //! iterator pointing to the beginnning of the hash-map const_iterator begin() const { return impl.begin(); } //! iterator pointing to the end of the hash-map (iterator-type as //! template-parameter) const_iterator end() const { return impl.end(); } //! \} //! \name Lookup and Element Access //! \{ //! Convenience operator to quickly insert or find values. Use with caution //! since using this operator will check external-memory. mapped_type& operator [] (const key_type& key) { return impl[key]; } //! Look up value by key. Non-const access. //! \param key key for value to look up iterator find(const key_type& key) { return impl.find(key); } //! Look up value by key. Const access. //! \param key key for value to look up const_iterator find(const key_type& key) const { return impl.find(key); } //! Number of values with given key //! \param key key for value to look up //! \return 0 or 1 depending on the presence of a value with the given key external_size_type count(const key_type& key) const { return impl.count(key); } //! Finds a range containing all values with given key. Non-const access //! \param key key to look for# //! \return range may be empty or contains exactly one value std::pair equal_range(const key_type& key) { return impl.equal_range(key); } //! Finds a range containing all values with given key. Const access //! \param key key to look for# //! \return range may be empty or contains exactly one value std::pair equal_range(const key_type& key) const { return impl.equal_range(key); } //! \} //! \name Modifiers: Insert //! \{ /*! * Insert a new value if no value with the same key is already present; * external memory must therefore be accessed * * \param value what to insert * \return a tuple whose second part is true iff the value was actually * added (no value with the same key present); the first part is an * iterator pointing to the newly inserted or already stored value */ std::pair insert(const value_type& value) { return impl.insert(value); } //! Insert a value; external memory is not accessed so that another value //! with the same key may be overwritten //! \param value what to insert //! \return iterator pointing to the inserted value iterator insert_oblivious(const value_type& value) { return impl.insert_oblivious(value); } //! Bulk-insert of values in the range [f, l) //! \param first beginning of the range //! \param last end of the range //! \param mem internal memory that may be used (note: this memory will be //! used additionally to the buffer). The more the better template void insert(InputIterator first, InputIterator last, internal_size_type mem) { impl.insert(first, last, mem); } //! \} //! \name Modifiers: Erase //! \{ //! Erase value by iterator //! \param it iterator pointing to the value to erase void erase(const_iterator it) { impl.erase(it); } //! Erase value by key; check external memory //! \param key key of value to erase //! \return number of values actually erased (0 or 1) external_size_type erase(const key_type& key) { return impl.erase(key); } //! Erase value by key but without looking at external memory //! \param key key for value to release void erase_oblivious(const key_type& key) { impl.erase_oblivious(key); } //! Reset hash-map: erase all values, invalidate all iterators void clear() { impl.clear(); } //! Exchange stored values with another hash-map //! \param obj hash-map to swap values with void swap(unordered_map& obj) { std::swap(impl, obj.impl); } //! \} //! \name Bucket Interface //! \{ //! Number of buckets internal_size_type bucket_count() const { return impl.bucket_count(); } //! Maximum number of buckets internal_size_type max_bucket_count() const { return impl.max_bucket_count(); } // bucket_size()? //! Bucket-index for values with given key. internal_size_type bucket(const key_type& k) const { return impl.bucket_index(k); } //! \} //! \name Hash Policy //! \{ //! Average number of (sub)blocks occupied by a bucket. float load_factor() const { return impl.load_factor(); } //! Set desired load-factor float opt_load_factor() const { return impl.opt_load_factor(); } //! Set desired load-factor void opt_load_factor(float z) { impl.opt_load_factor(z); } //! Rehash with (at least) n buckets void rehash(internal_size_type n) { impl.rehash(n); } //! \} //! \name Observers //! \{ //! Hash-function used by this hash-map hasher hash_function() const { return impl.hash_function(); } //! Strict-weak-ordering used by this hash-map key_compare key_comp() const { return impl.key_cmp(); } //! Constructed equality predicate used by this hash-map key_equal key_eq() const { return impl.key_eq(); } //! Get node memory allocator allocator_type get_allocator() const { return impl.get_allocator(); } //! \} //! \name Internal Memory Buffer Policy //! \{ //! Number of bytes occupied by buffer internal_size_type buffer_size() const { return impl.buffer_size(); } //! Maximum buffer size in byte internal_size_type max_buffer_size() const { return impl.max_buffer_size(); } //! Set maximum buffer size //! \param buffer_size new size in byte void max_buffer_size(internal_size_type buffer_size) { impl.max_buffer_size(buffer_size); } //! \} //! \name Statistics //! \{ //! Reset hash-map statistics void reset_statistics() { impl.reset_statistics(); } //! Print short general statistics to output stream void print_statistics(std::ostream& o = std::cout) const { impl.print_statistics(o); } //! Even more statistics: Number of buckets, number of values, buffer-size, //! values per bucket void print_load_statistics(std::ostream& o = std::cout) const { impl.print_load_statistics(o); } // \} }; //! \} STXXL_END_NAMESPACE namespace std { template < class KeyType, class MappedType, class HashType, class CompareType, unsigned SubBlockSize, unsigned SubBlocksPerBlock, class AllocType > void swap(stxxl::unordered_map& a, stxxl::unordered_map& b ) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_UNORDERED_MAP_HEADER stxxl-1.4.1/include/stxxl/bits/containers/priority_queue.h000644 001411 000144 00000115354 12414452316 023661 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/containers/priority_queue.h * * Implements a data structure from "Peter Sanders. Fast Priority Queues for * Cached Memory. ALENEX'99" for external memory. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 1999 Peter Sanders * Copyright (C) 2003, 2004, 2007 Roman Dementiev * Copyright (C) 2007-2009 Johannes Singler * Copyright (C) 2007-2010 Andreas Beckmann * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_CONTAINERS_PRIORITY_QUEUE_HEADER #define STXXL_CONTAINERS_PRIORITY_QUEUE_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE /* KNBufferSize1 = 32; KNN = 512; // length of group 1 sequences KNKMAX = 64; // maximal arity LogKNKMAX = 6; // ceil(log KNKMAX) KNLevels = 4; // overall capacity >= KNN*KNKMAX^KNLevels */ // internal memory consumption >= N_*(KMAX_^IntLevels_) + ext template < class ValueType, class CompareType, unsigned BufferSize1_ = 32, // equalize procedure call overheads etc. unsigned N_ = 512, // length of group 1 sequences unsigned IntKMAX_ = 64, // maximal arity for internal mergers unsigned IntLevels_ = 4, // number of internal groups unsigned BlockSize_ = (2* 1024* 1024), // external block size unsigned ExtKMAX_ = 64, // maximal arity for external mergers unsigned ExtLevels_ = 2, // number of external groups class AllocStr_ = STXXL_DEFAULT_ALLOC_STRATEGY > struct priority_queue_config { typedef ValueType value_type; typedef CompareType comparator_type; typedef AllocStr_ alloc_strategy_type; enum { delete_buffer_size = BufferSize1_, N = N_, IntKMAX = IntKMAX_, num_int_groups = IntLevels_, num_ext_groups = ExtLevels_, BlockSize = BlockSize_, ExtKMAX = ExtKMAX_, element_size = sizeof(ValueType) }; }; STXXL_END_NAMESPACE namespace std { template void swap(stxxl::priority_queue_local::ext_merger& a, stxxl::priority_queue_local::ext_merger& b) { a.swap(b); } template void swap(stxxl::priority_queue_local::loser_tree& a, stxxl::priority_queue_local::loser_tree& b) { a.swap(b); } } // namespace std STXXL_BEGIN_NAMESPACE //! External priority queue data structure \n //! Introduction to priority queue container: see \ref tutorial_pqueue tutorial. \n //! Design and Internals of priority queue container: see \ref design_pqueue. template class priority_queue : private noncopyable { public: typedef ConfigType Config; enum { delete_buffer_size = Config::delete_buffer_size, N = Config::N, IntKMAX = Config::IntKMAX, num_int_groups = Config::num_int_groups, num_ext_groups = Config::num_ext_groups, total_num_groups = Config::num_int_groups + Config::num_ext_groups, BlockSize = Config::BlockSize, ExtKMAX = Config::ExtKMAX }; //! The type of object stored in the priority_queue. typedef typename Config::value_type value_type; //! Comparison object. typedef typename Config::comparator_type comparator_type; typedef typename Config::alloc_strategy_type alloc_strategy_type; //! An unsigned integral type (64 bit). typedef stxxl::uint64 size_type; //! Type of the block used in disk-memory transfers typedef typed_block block_type; typedef read_write_pool pool_type; protected: typedef priority_queue_local::internal_priority_queue, comparator_type> insert_heap_type; typedef priority_queue_local::loser_tree< value_type, comparator_type, IntKMAX> int_merger_type; typedef priority_queue_local::ext_merger< block_type, comparator_type, ExtKMAX, alloc_strategy_type> ext_merger_type; int_merger_type int_mergers[num_int_groups]; pool_type* pool; bool pool_owned; ext_merger_type** ext_mergers; // one delete buffer for each tree => group buffer value_type group_buffers[total_num_groups][N + 1]; // tree->group_buffers->delete_buffer (extra space for sentinel) value_type* group_buffer_current_mins[total_num_groups]; // group_buffer_current_mins[i] is current start of group_buffers[i], end is group_buffers[i] + N // overall delete buffer value_type delete_buffer[delete_buffer_size + 1]; value_type* delete_buffer_current_min; // current start of delete_buffer value_type* delete_buffer_end; // end of delete_buffer comparator_type cmp; // insert buffer insert_heap_type insert_heap; // how many groups are active unsigned_type num_active_groups; // total size not counting insert_heap and delete_buffer size_type size_; private: void init(); void refill_delete_buffer(); size_type refill_group_buffer(unsigned_type k); unsigned_type make_space_available(unsigned_type level); void empty_insert_heap(); value_type get_supremum() const { return cmp.min_value(); } //{ return group_buffers[0][KNN].key; } unsigned_type current_delete_buffer_size() const { return delete_buffer_end - delete_buffer_current_min; } unsigned_type current_group_buffer_size(unsigned_type i) const { return &(group_buffers[i][N]) - group_buffer_current_mins[i]; } public: //! \name Constructors/Destructors //! \{ //! Constructs external priority queue object. //! \param pool_ pool of blocks that will be used //! for data writing and prefetching for the disk<->memory transfers //! happening in the priority queue. Larger pool size //! helps to speed up operations. priority_queue(pool_type& pool_); //! Constructs external priority queue object. //! \param p_pool_ pool of blocks that will be used //! for data prefetching for the disk<->memory transfers //! happening in the priority queue. Larger pool size //! helps to speed up operations. //! \param w_pool_ pool of blocks that will be used //! for writing data for the memory<->disk transfers //! happening in the priority queue. Larger pool size //! helps to speed up operations. STXXL_DEPRECATED( priority_queue(prefetch_pool& p_pool_, write_pool& w_pool_) ); //! Constructs external priority queue object. //! \param p_pool_mem memory (in bytes) for prefetch pool that will be used //! for data prefetching for the disk<->memory transfers //! happening in the priority queue. Larger pool size //! helps to speed up operations. //! \param w_pool_mem memory (in bytes) for buffered write pool that will be used //! for writing data for the memory<->disk transfers //! happening in the priority queue. Larger pool size //! helps to speed up operations. priority_queue(unsigned_type p_pool_mem, unsigned_type w_pool_mem); virtual ~priority_queue(); //! \} #if 0 //! swap this priority queue with another one. //! Implementation correctness is questionable. void swap(priority_queue& obj) { //swap_1D_arrays(int_mergers,obj.int_mergers,num_int_groups); // does not work in g++ 3.4.3 :( bug? for (unsigned_type i = 0; i < num_int_groups; ++i) std::swap(int_mergers[i], obj.int_mergers[i]); //std::swap(pool,obj.pool); //std::swap(pool_owned, obj.pool_owned); std::swap(ext_mergers, obj.ext_mergers); for (unsigned_type i1 = 0; i1 < total_num_groups; ++i1) for (unsigned_type i2 = 0; i2 < (N + 1); ++i2) std::swap(group_buffers[i1][i2], obj.group_buffers[i1][i2]); STXXL_STATIC_ASSERT(false); // Shoot yourself in the foot: group_buffer_current_mins contains pointers into group_buffers ... // either recompute them or add/subtract (&this->group_buffers[0][0] - &obj->group_buffers[0][0]) swap_1D_arrays(group_buffer_current_mins, obj.group_buffer_current_mins, total_num_groups); swap_1D_arrays(delete_buffer, obj.delete_buffer, delete_buffer_size + 1); std::swap(delete_buffer_current_min, obj.delete_buffer_current_min); std::swap(delete_buffer_end, obj.delete_buffer_end); std::swap(cmp, obj.cmp); std::swap(insert_heap, obj.insert_heap); std::swap(num_active_groups, obj.num_active_groups); std::swap(size_, obj.size_); } #endif //! \name Capacity //! \{ //! Returns number of elements contained. //! \return number of elements contained size_type size() const; //! Returns true if queue has no elements. //! \return \b true if queue has no elements, \b false otherwise bool empty() const { return (size() == 0); } //! \} //! \name Operators //! \{ //! Returns "largest" element. //! //! Returns a const reference to the element at the top of the //! priority_queue. The element at the top is guaranteed to be the largest //! element in the \b priority queue, as determined by the comparison //! function \b comparator_type (the same as the second parameter of //! PRIORITY_QUEUE_GENERATOR utility class). That is, for every other //! element \b x in the priority_queue, \b comparator_type(Q.top(), x) is //! false. Precondition: \c empty() is false. const value_type & top() const; //! \} //! \name Modifiers //! \{ //! Removes the element at the top. //! //! Removes the element at the top of the priority_queue, that is, the //! largest element in the \b priority_queue. Precondition: \c empty() is //! \b false. Postcondition: \c size() will be decremented by 1. void pop(); //! Inserts x into the priority_queue. //! //! Inserts x into the priority_queue. Postcondition: \c size() will be //! incremented by 1. void push(const value_type& obj); //! \} //! \name Miscellaneous //! \{ //! Number of bytes consumed by the \b priority_queue from the internal //! memory not including pools (see the constructor) unsigned_type mem_cons() const { unsigned_type dynam_alloc_mem = 0; //dynam_alloc_mem += w_pool.mem_cons(); //dynam_alloc_mem += p_pool.mem_cons(); for (int i = 0; i < num_int_groups; ++i) dynam_alloc_mem += int_mergers[i].mem_cons(); for (int i = 0; i < num_ext_groups; ++i) dynam_alloc_mem += ext_mergers[i]->mem_cons(); return (sizeof(*this) + sizeof(ext_merger_type) * num_ext_groups + dynam_alloc_mem); } void dump_sizes() const; void dump_params() const; //! \} }; template inline typename priority_queue::size_type priority_queue::size() const { return size_ + insert_heap.size() - 1 + (delete_buffer_end - delete_buffer_current_min); } template inline const typename priority_queue::value_type & priority_queue::top() const { assert(!insert_heap.empty()); const typename priority_queue::value_type& t = insert_heap.top(); if (/*(!insert_heap.empty()) && */ cmp(*delete_buffer_current_min, t)) return t; else return *delete_buffer_current_min; } template inline void priority_queue::pop() { //STXXL_VERBOSE1("priority_queue::pop()"); assert(!insert_heap.empty()); if (/*(!insert_heap.empty()) && */ cmp(*delete_buffer_current_min, insert_heap.top())) insert_heap.pop(); else { assert(delete_buffer_current_min < delete_buffer_end); ++delete_buffer_current_min; if (delete_buffer_current_min == delete_buffer_end) refill_delete_buffer(); } } template inline void priority_queue::push(const value_type& obj) { //STXXL_VERBOSE3("priority_queue::push("<< obj <<")"); assert(int_mergers->not_sentinel(obj)); if (insert_heap.size() == N + 1) empty_insert_heap(); assert(!insert_heap.empty()); insert_heap.push(obj); } //////////////////////////////////////////////////////////////// template priority_queue::priority_queue(pool_type& pool_) : pool(&pool_), pool_owned(false), delete_buffer_end(delete_buffer + delete_buffer_size), insert_heap(N + 2), num_active_groups(0), size_(0) { STXXL_VERBOSE_PQ("priority_queue(pool)"); init(); } // DEPRECATED template priority_queue::priority_queue(prefetch_pool& p_pool_, write_pool& w_pool_) : pool(new pool_type(p_pool_, w_pool_)), pool_owned(true), delete_buffer_end(delete_buffer + delete_buffer_size), insert_heap(N + 2), num_active_groups(0), size_(0) { STXXL_VERBOSE_PQ("priority_queue(p_pool, w_pool)"); init(); } template priority_queue::priority_queue(unsigned_type p_pool_mem, unsigned_type w_pool_mem) : pool(new pool_type(p_pool_mem / BlockSize, w_pool_mem / BlockSize)), pool_owned(true), delete_buffer_end(delete_buffer + delete_buffer_size), insert_heap(N + 2), num_active_groups(0), size_(0) { STXXL_VERBOSE_PQ("priority_queue(pool sizes)"); init(); } template void priority_queue::init() { assert(!cmp(cmp.min_value(), cmp.min_value())); // verify strict weak ordering ext_mergers = new ext_merger_type*[num_ext_groups]; for (unsigned_type j = 0; j < num_ext_groups; ++j) { ext_mergers[j] = new ext_merger_type; ext_mergers[j]->set_pool(pool); } value_type sentinel = cmp.min_value(); insert_heap.push(sentinel); // always keep the sentinel delete_buffer[delete_buffer_size] = sentinel; // sentinel delete_buffer_current_min = delete_buffer_end; // empty for (unsigned_type i = 0; i < total_num_groups; i++) { group_buffers[i][N] = sentinel; // sentinel group_buffer_current_mins[i] = &(group_buffers[i][N]); // empty } } template priority_queue::~priority_queue() { STXXL_VERBOSE_PQ("~priority_queue()"); if (pool_owned) delete pool; for (unsigned_type j = 0; j < num_ext_groups; ++j) delete ext_mergers[j]; delete[] ext_mergers; } //--------------------- Buffer refilling ------------------------------- // refill group_buffers[j] and return number of elements found template typename priority_queue::size_type priority_queue::refill_group_buffer(unsigned_type group) { STXXL_VERBOSE_PQ("refill_group_buffer(" << group << ")"); value_type* target; size_type length; size_type group_size = (group < num_int_groups) ? int_mergers[group].size() : ext_mergers[group - num_int_groups]->size(); // elements left in segments unsigned_type left_elements = group_buffers[group] + N - group_buffer_current_mins[group]; //elements left in target buffer if (group_size + left_elements >= size_type(N)) { // buffer will be filled completely target = group_buffers[group]; length = N - left_elements; } else { target = group_buffers[group] + N - group_size - left_elements; length = group_size; } if (length > 0) { // shift remaininig elements to front memmove(target, group_buffer_current_mins[group], left_elements * sizeof(value_type)); group_buffer_current_mins[group] = target; // fill remaining space from group if (group < num_int_groups) int_mergers[group].multi_merge(target + left_elements, (unsigned_type)length); else ext_mergers[group - num_int_groups]->multi_merge( target + left_elements, target + left_elements + length); } //STXXL_MSG(length + left_elements); //std::copy(target,target + length + left_elements,std::ostream_iterator(std::cout, "\n")); #if STXXL_CHECK_ORDER_IN_SORTS priority_queue_local::invert_order inv_cmp(cmp); if (!stxxl::is_sorted(group_buffer_current_mins[group], group_buffers[group] + N, inv_cmp)) { STXXL_VERBOSE_PQ("refill_grp... length: " << length << " left_elements: " << left_elements); for (value_type* v = group_buffer_current_mins[group] + 1; v < group_buffer_current_mins[group] + left_elements; ++v) { if (inv_cmp(*v, *(v - 1))) { STXXL_MSG("Error in buffer " << group << " at position " << (v - group_buffer_current_mins[group] - 1) << "/" << (v - group_buffer_current_mins[group]) << " " << *(v - 2) << " " << *(v - 1) << " " << *v << " " << *(v + 1)); } } assert(false); } #endif return length + left_elements; } template void priority_queue::refill_delete_buffer() { STXXL_VERBOSE_PQ("refill_delete_buffer()"); size_type total_group_size = 0; //num_active_groups is <= 4 for (unsigned_type i = num_active_groups; i > 0; ) { --i; if ((group_buffers[i] + N) - group_buffer_current_mins[i] < delete_buffer_size) { size_type length = refill_group_buffer(i); // max active level dry now? if (length == 0 && unsigned(i) == num_active_groups - 1) --num_active_groups; total_group_size += length; } else total_group_size += delete_buffer_size; // actually only a sufficient lower bound } size_type length; if (total_group_size >= delete_buffer_size) // buffer can be filled completely { length = delete_buffer_size; // amount to be copied size_ -= size_type(delete_buffer_size); // amount left in group_buffers } else { length = total_group_size; assert(size_ == length); // trees and group_buffers get empty size_ = 0; } priority_queue_local::invert_order inv_cmp(cmp); // now call simplified refill routines // which can make the assumption that // they find all they are asked in the buffers delete_buffer_current_min = delete_buffer_end - length; STXXL_VERBOSE_PQ("refill_del... Active groups = " << num_active_groups); switch (num_active_groups) { case 0: break; case 1: std::copy(group_buffer_current_mins[0], group_buffer_current_mins[0] + length, delete_buffer_current_min); group_buffer_current_mins[0] += length; break; case 2: #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER { std::pair seqs[2] = { std::make_pair(group_buffer_current_mins[0], group_buffers[0] + N), std::make_pair(group_buffer_current_mins[1], group_buffers[1] + N) }; parallel::multiway_merge_sentinel(seqs, seqs + 2, delete_buffer_current_min, inv_cmp, length); //sequence iterators are progressed appropriately group_buffer_current_mins[0] = seqs[0].first; group_buffer_current_mins[1] = seqs[1].first; } #else priority_queue_local::merge_iterator( group_buffer_current_mins[0], group_buffer_current_mins[1], delete_buffer_current_min, length, cmp); #endif break; case 3: #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER { std::pair seqs[3] = { std::make_pair(group_buffer_current_mins[0], group_buffers[0] + N), std::make_pair(group_buffer_current_mins[1], group_buffers[1] + N), std::make_pair(group_buffer_current_mins[2], group_buffers[2] + N) }; parallel::multiway_merge_sentinel(seqs, seqs + 3, delete_buffer_current_min, inv_cmp, length); //sequence iterators are progressed appropriately group_buffer_current_mins[0] = seqs[0].first; group_buffer_current_mins[1] = seqs[1].first; group_buffer_current_mins[2] = seqs[2].first; } #else priority_queue_local::merge3_iterator( group_buffer_current_mins[0], group_buffer_current_mins[1], group_buffer_current_mins[2], delete_buffer_current_min, length, cmp); #endif break; case 4: #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER { std::pair seqs[4] = { std::make_pair(group_buffer_current_mins[0], group_buffers[0] + N), std::make_pair(group_buffer_current_mins[1], group_buffers[1] + N), std::make_pair(group_buffer_current_mins[2], group_buffers[2] + N), std::make_pair(group_buffer_current_mins[3], group_buffers[3] + N) }; parallel::multiway_merge_sentinel(seqs, seqs + 4, delete_buffer_current_min, inv_cmp, length); //sequence iterators are progressed appropriately group_buffer_current_mins[0] = seqs[0].first; group_buffer_current_mins[1] = seqs[1].first; group_buffer_current_mins[2] = seqs[2].first; group_buffer_current_mins[3] = seqs[3].first; } #else priority_queue_local::merge4_iterator( group_buffer_current_mins[0], group_buffer_current_mins[1], group_buffer_current_mins[2], group_buffer_current_mins[3], delete_buffer_current_min, length, cmp); //side effect free #endif break; default: STXXL_THROW2(std::runtime_error, "priority_queue<...>::refill_delete_buffer()", "Overflow! The number of buffers on 2nd level in stxxl::priority_queue is currently limited to 4"); } #if STXXL_CHECK_ORDER_IN_SORTS if (!stxxl::is_sorted(delete_buffer_current_min, delete_buffer_end, inv_cmp)) { for (value_type* v = delete_buffer_current_min + 1; v < delete_buffer_end; ++v) { if (inv_cmp(*v, *(v - 1))) { STXXL_MSG("Error at position " << (v - delete_buffer_current_min - 1) << "/" << (v - delete_buffer_current_min) << " " << *(v - 1) << " " << *v); } } assert(false); } #endif //std::copy(delete_buffer_current_min,delete_buffer_current_min + length,std::ostream_iterator(std::cout, "\n")); } //-------------------------------------------------------------------- // check if space is available on level k and // empty this level if necessary leading to a recursive call. // return the level where space was finally available template unsigned_type priority_queue::make_space_available(unsigned_type level) { STXXL_VERBOSE_PQ("make_space_available(" << level << ")"); unsigned_type finalLevel; assert(level < total_num_groups); assert(level <= num_active_groups); if (level == num_active_groups) ++num_active_groups; const bool spaceIsAvailable_ = (level < num_int_groups) ? int_mergers[level].is_space_available() : (ext_mergers[level - num_int_groups]->is_space_available()); if (spaceIsAvailable_) { finalLevel = level; } else if (level == total_num_groups - 1) { size_type capacity = N; for (int i = 0; i < num_int_groups; ++i) capacity *= IntKMAX; for (int i = 0; i < num_ext_groups; ++i) capacity *= ExtKMAX; STXXL_ERRMSG("priority_queue OVERFLOW - all groups full, size=" << size() << ", capacity(last externel group (" << num_int_groups + num_ext_groups - 1 << "))=" << capacity); dump_sizes(); unsigned_type extLevel = level - num_int_groups; const size_type segmentSize = ext_mergers[extLevel]->size(); STXXL_VERBOSE1("Inserting segment into last level external: " << level << " " << segmentSize); ext_merger_type* overflow_merger = new ext_merger_type; overflow_merger->set_pool(pool); overflow_merger->insert_segment(*ext_mergers[extLevel], segmentSize); std::swap(ext_mergers[extLevel], overflow_merger); delete overflow_merger; finalLevel = level; } else { finalLevel = make_space_available(level + 1); if (level < num_int_groups - 1) // from internal to internal tree { unsigned_type segmentSize = int_mergers[level].size(); value_type* newSegment = new value_type[segmentSize + 1]; int_mergers[level].multi_merge(newSegment, segmentSize); // empty this level newSegment[segmentSize] = delete_buffer[delete_buffer_size]; // sentinel // for queues where size << #inserts // it might make sense to stay in this level if // segmentSize < alpha * KNN * k^level for some alpha < 1 int_mergers[level + 1].insert_segment(newSegment, segmentSize); } else { if (level == num_int_groups - 1) // from internal to external tree { const unsigned_type segmentSize = int_mergers[num_int_groups - 1].size(); STXXL_VERBOSE_PQ("make_space... Inserting segment into first level external: " << level << " " << segmentSize); ext_mergers[0]->insert_segment(int_mergers[num_int_groups - 1], segmentSize); } else // from external to external tree { const size_type segmentSize = ext_mergers[level - num_int_groups]->size(); STXXL_VERBOSE_PQ("make_space... Inserting segment into second level external: " << level << " " << segmentSize); ext_mergers[level - num_int_groups + 1]->insert_segment(*ext_mergers[level - num_int_groups], segmentSize); } } } return finalLevel; } // empty the insert heap into the main data structure template void priority_queue::empty_insert_heap() { STXXL_VERBOSE_PQ("empty_insert_heap()"); assert(insert_heap.size() == (N + 1)); const value_type sup = get_supremum(); // build new segment value_type* newSegment = new value_type[N + 1]; value_type* newPos = newSegment; // put the new data there for now //insert_heap.sortTo(newSegment); value_type* SortTo = newSegment; insert_heap.sort_to(SortTo); SortTo = newSegment + N; insert_heap.clear(); insert_heap.push(*SortTo); assert(insert_heap.size() == 1); newSegment[N] = sup; // sentinel // copy the delete_buffer and group_buffers[0] to temporary storage // (the temporary can be eliminated using some dirty tricks) const unsigned_type tempSize = N + delete_buffer_size; value_type temp[tempSize + 1]; unsigned_type sz1 = current_delete_buffer_size(); unsigned_type sz2 = current_group_buffer_size(0); value_type* pos = temp + tempSize - sz1 - sz2; std::copy(delete_buffer_current_min, delete_buffer_current_min + sz1, pos); std::copy(group_buffer_current_mins[0], group_buffer_current_mins[0] + sz2, pos + sz1); temp[tempSize] = sup; // sentinel // refill delete_buffer // (using more complicated code it could be made somewhat fuller // in certain circumstances) priority_queue_local::merge_iterator(pos, newPos, delete_buffer_current_min, sz1, cmp); // refill group_buffers[0] // (as above we might want to take the opportunity // to make group_buffers[0] fuller) priority_queue_local::merge_iterator(pos, newPos, group_buffer_current_mins[0], sz2, cmp); // merge the rest to the new segment // note that merge exactly trips into the footsteps // of itself priority_queue_local::merge_iterator(pos, newPos, newSegment, (unsigned_type)N, cmp); // and insert it unsigned_type freeLevel = make_space_available(0); assert(freeLevel == 0 || int_mergers[0].size() == 0); int_mergers[0].insert_segment(newSegment, N); // get rid of invalid level 2 buffers // by inserting them into tree 0 (which is almost empty in this case) if (freeLevel > 0) { for (int_type i = freeLevel; i >= 0; i--) { // reverse order not needed // but would allow immediate refill newSegment = new value_type[current_group_buffer_size(i) + 1]; // with sentinel std::copy(group_buffer_current_mins[i], group_buffer_current_mins[i] + current_group_buffer_size(i) + 1, newSegment); int_mergers[0].insert_segment(newSegment, current_group_buffer_size(i)); group_buffer_current_mins[i] = group_buffers[i] + N; // empty } } // update size size_ += size_type(N); // special case if the tree was empty before if (delete_buffer_current_min == delete_buffer_end) refill_delete_buffer(); } template void priority_queue::dump_sizes() const { unsigned_type capacity = N; STXXL_MSG("pq::size()\t= " << size()); STXXL_MSG(" insert_heap\t= " << insert_heap.size() - 1 << "/" << capacity); STXXL_MSG(" delete_buffer\t= " << (delete_buffer_end - delete_buffer_current_min) << "/" << delete_buffer_size); for (int i = 0; i < num_int_groups; ++i) { capacity *= IntKMAX; STXXL_MSG(" grp " << i << " int" << " grpbuf=" << current_group_buffer_size(i) << " size=" << int_mergers[i].size() << "/" << capacity << " (" << (int)((double)int_mergers[i].size() * 100.0 / (double)capacity) << "%)" << " space=" << int_mergers[i].is_space_available()); } for (int i = 0; i < num_ext_groups; ++i) { capacity *= ExtKMAX; STXXL_MSG(" grp " << i + num_int_groups << " ext" << " grpbuf=" << current_group_buffer_size(i + num_int_groups) << " size=" << ext_mergers[i]->size() << "/" << capacity << " (" << (int)((double)ext_mergers[i]->size() * 100.0 / (double)capacity) << "%)" << " space=" << ext_mergers[i]->is_space_available()); } dump_params(); } template void priority_queue::dump_params() const { STXXL_MSG("params: delete_buffer_size=" << delete_buffer_size << " N=" << N << " IntKMAX=" << IntKMAX << " num_int_groups=" << num_int_groups << " ExtKMAX=" << ExtKMAX << " num_ext_groups=" << num_ext_groups << " BlockSize=" << BlockSize); } namespace priority_queue_local { struct Parameters_for_priority_queue_not_found_Increase_IntMem { enum { fits = false }; typedef Parameters_for_priority_queue_not_found_Increase_IntMem result; }; struct dummy { enum { fits = false }; typedef dummy result; }; template struct find_B_m { typedef find_B_m self_type; //! element size static const internal_size_type element_size = ElementSize; //! internal memory size of PQ static const internal_size_type intmem = IntMem; //! block size (iterates from 8 MiB downwards) static const internal_size_type B = BlockSize; //! number of blocks that fit into internal memory (M) static const internal_size_type k = IntMem / BlockSize; //! number of blocks fitting into buffers of mergers (arity of both //! mergers), increased from 1 to 2048 ?-tb static const internal_size_type m = m_; //! remaining blocks, (freely moving, not necessarily unused) ?-tb static const int_type c = k - m_; // memory occupied by block must be at least 10 times larger than size of ext sequence //! calculated boolean whether the configuration fits into internal memory. static const external_size_type fits = // need some temporary constant-size internal blocks (c > 10) && // satisfy items requirement (((k - m) * m * (m * B / (ElementSize * 4 * 1024))) >= MaxItems) && // if we have two ext mergers their degree must be at least 64=m/2 ((MaxItems < ((k - m) * m / (2 * ElementSize)) * 1024) || m >= 128); static const unsigned_type step = 1; //! if not fits, recurse into configuration with +step more internal buffers typedef typename find_B_m= k)>::result candidate1; //! if not fits, recurse into configuration with block size halved. typedef typename find_B_m::result candidate2; //! return a fitting configuration. typedef typename IF::result>::result result; }; // specialization for the case when no valid parameters are found template struct find_B_m { enum { fits = false }; typedef Parameters_for_priority_queue_not_found_Increase_IntMem result; }; // to speedup search template struct find_B_m { enum { fits = false }; typedef dummy result; }; // start search template struct find_settings { // start from block size (8*1024*1024) bytes typedef typename find_B_m::result result; }; struct Parameters_not_found_Try_to_change_the_Tune_parameter { typedef Parameters_not_found_Try_to_change_the_Tune_parameter result; }; template struct compute_N { typedef compute_N Self; static const unsigned_type X = X_; static const unsigned_type AI = AI_; static const unsigned_type N = X / (AI * AI); // two stage internal typedef typename IF<(N >= CriticalSize), Self, typename compute_N::result>::result result; }; template struct compute_N<1, X_, CriticalSize_> { typedef Parameters_not_found_Try_to_change_the_Tune_parameter result; }; } // namespace priority_queue_local //! \} //! \addtogroup stlcont //! \{ //! Priority queue type generator. \n //! Introduction to priority queue container: see \ref tutorial_pqueue tutorial. \n //! Design and Internals of priority queue container: see \ref design_pqueue. //! //! \tparam ValueType type of the contained objects (POD with no references to internal memory) //! //! \tparam CompareType the comparator type used to determine whether one element is //! smaller than another element. //! //! \tparam IntMemory upper limit for internal memory consumption in bytes. //! //! \tparam MaxItems upper limit for number of elements contained in the priority queue (in 1024 units).
    //! Example: if you are sure that priority queue contains no more than //! one million elements in a time, then the right parameter is (1000000 / 1024) = 976. //! //! \tparam Tune tuning parameter for meta-program search.
    //! Try to play with it if the code does not compile (larger than default //! values might help). Code does not compile if no suitable internal //! parameters were found for given IntMemory and MaxItems. It might also //! happen that given IntMemory is too small for given MaxItems, try larger //! values. template class PRIORITY_QUEUE_GENERATOR { public: // actual calculation of B, m, k and element_size typedef typename priority_queue_local::find_settings::result settings; enum { B = settings::B, m = settings::m, X = B * (settings::k - m) / settings::element_size, // interpretation of result Buffer1Size = 32 // fixed }; // derivation of N, AI, AE typedef typename priority_queue_local::compute_N<(1 << Tune), X, 4* Buffer1Size>::result ComputeN; enum { N = ComputeN::N, AI = ComputeN::AI, AE = (m / 2 < 2) ? 2 : (m / 2) // at least 2 }; // Estimation of maximum internal memory consumption (in bytes) static const unsigned_type EConsumption = X * settings::element_size + settings::B * AE + ((MaxItems / X) / AE) * settings::B * 1024; /* unsigned BufferSize1_ = 32, // equalize procedure call overheads etc. unsigned N_ = 512, // bandwidth unsigned IntKMAX_ = 64, // maximal arity for internal mergers unsigned IntLevels_ = 4, unsigned BlockSize = (2*1024*1024), unsigned ExtKMAX_ = 64, // maximal arity for external mergers unsigned ExtLevels_ = 2, */ typedef priority_queue > result; }; //! \} STXXL_END_NAMESPACE namespace std { template void swap(stxxl::priority_queue& a, stxxl::priority_queue& b) { a.swap(b); } } // namespace std #endif // !STXXL_CONTAINERS_PRIORITY_QUEUE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/adaptor.h000644 001411 000144 00000014454 12414452316 021002 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/adaptor.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_ADAPTOR_HEADER #define STXXL_ALGO_ADAPTOR_HEADER #include #include STXXL_BEGIN_NAMESPACE template struct runs2bid_array_adaptor : public two2one_dim_array_adapter_base, PosType> { typedef runs2bid_array_adaptor self_type; typedef BID data_type; enum { block_size = BlockSize }; unsigned_type dim_size; typedef two2one_dim_array_adapter_base, PosType> parent_type; using parent_type::array; using parent_type::pos; runs2bid_array_adaptor(RunType** a, PosType p, unsigned_type d) : two2one_dim_array_adapter_base, PosType>(a, p), dim_size(d) { } runs2bid_array_adaptor(const self_type& a) : two2one_dim_array_adapter_base, PosType>(a), dim_size(a.dim_size) { } const self_type& operator = (const self_type& a) { array = a.array; pos = a.pos; dim_size = a.dim_size; return *this; } data_type& operator * () { CHECK_RUN_BOUNDS(pos); return (BID&)((*(array[(pos) % dim_size]))[(pos) / dim_size].bid); } const data_type* operator -> () const { CHECK_RUN_BOUNDS(pos); return &((*(array[(pos) % dim_size])[(pos) / dim_size].bid)); } data_type& operator [] (PosType n) const { n += pos; CHECK_RUN_BOUNDS(n); return (BID&)((*(array[(n) % dim_size]))[(n) / dim_size].bid); } }; BLOCK_ADAPTOR_OPERATORS(runs2bid_array_adaptor) template struct runs2bid_array_adaptor2 : public two2one_dim_array_adapter_base, PosType> { typedef runs2bid_array_adaptor2 self_type; typedef BID data_type; typedef two2one_dim_array_adapter_base, PosType> base_type; using base_type::pos; using base_type::array; enum { block_size = BlockSize }; PosType w, h, K; runs2bid_array_adaptor2(RunType** a, PosType p, int_type _w, int_type _h) : two2one_dim_array_adapter_base, PosType>(a, p), w(_w), h(_h), K(_w * _h) { } runs2bid_array_adaptor2(const self_type& a) : two2one_dim_array_adapter_base, PosType>(a), w(a.w), h(a.h), K(a.K) { } const self_type& operator = (const self_type& a) { array = a.array; pos = a.pos; w = a.w; h = a.h; K = a.K; return *this; } data_type& operator * () { PosType i = pos - K; if (i < 0) return (BID&)((*(array[(pos) % w]))[(pos) / w].bid); PosType _w = w; _w--; return (BID&)((*(array[(i) % _w]))[h + (i / _w)].bid); } const data_type* operator -> () const { PosType i = pos - K; if (i < 0) return &((*(array[(pos) % w])[(pos) / w].bid)); PosType _w = w; _w--; return &((*(array[(i) % _w])[h + (i / _w)].bid)); } data_type& operator [] (PosType n) const { n += pos; PosType i = n - K; if (i < 0) return (BID&)((*(array[(n) % w]))[(n) / w].bid); PosType _w = w; _w--; return (BID&)((*(array[(i) % _w]))[h + (i / _w)].bid); } }; BLOCK_ADAPTOR_OPERATORS(runs2bid_array_adaptor2) template struct trigger_entry_iterator { typedef trigger_entry_iterator self_type; typedef typename std::iterator_traits::value_type::bid_type bid_type; // STL typedefs typedef bid_type value_type; typedef std::random_access_iterator_tag iterator_category; typedef int_type difference_type; typedef value_type* pointer; typedef value_type& reference; trigger_iterator_type value; trigger_entry_iterator(const self_type& a) : value(a.value) { } trigger_entry_iterator(trigger_iterator_type v) : value(v) { } bid_type& operator * () { return value->bid; } bid_type* operator -> () const { return &(value->bid); } const bid_type& operator [] (int_type n) const { return (value + n)->bid; } bid_type& operator [] (int_type n) { return (value + n)->bid; } self_type& operator ++ () { value++; return *this; } self_type operator ++ (int) { self_type tmp = *this; value++; return tmp; } self_type& operator -- () { value--; return *this; } self_type operator -- (int) { self_type tmp = *this; value--; return tmp; } bool operator == (const self_type& a) const { return value == a.value; } bool operator != (const self_type& a) const { return value != a.value; } self_type operator += (int_type n) { value += n; return *this; } self_type operator -= (int_type n) { value -= n; return *this; } int_type operator - (const self_type& a) const { return value - a.value; } int_type operator + (const self_type& a) const { return value + a.value; } }; template inline trigger_entry_iterator make_bid_iterator(Iterator iter) { return trigger_entry_iterator(iter); } STXXL_END_NAMESPACE #endif // !STXXL_ALGO_ADAPTOR_HEADER stxxl-1.4.1/include/stxxl/bits/algo/losertree.h000644 001411 000144 00000016736 12414452316 021361 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/losertree.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 1999 Peter Sanders * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_LOSERTREE_HEADER #define STXXL_ALGO_LOSERTREE_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE template class loser_tree : private noncopyable { int logK; int_type k; int_type* entry; RunCursorType* current; RunCursorCmpType cmp; int_type init_winner(int_type root) { if (root >= k) { return root - k; } else { int_type left = init_winner(2 * root); int_type right = init_winner(2 * root + 1); if (cmp(current[left], current[right])) { entry[root] = right; return left; } else { entry[root] = left; return right; } } } public: typedef typename RunCursorType::prefetcher_type prefetcher_type; typedef typename RunCursorType::value_type value_type; loser_tree( prefetcher_type* p, int_type nruns, RunCursorCmpType c) : cmp(c) { int_type i; logK = ilog2_ceil(nruns); int_type kReg = k = (int_type(1) << logK); STXXL_VERBOSE2("loser_tree: logK=" << logK << " nruns=" << nruns << " K=" << kReg); #ifdef STXXL_SORT_SINGLE_PREFETCHER current = new RunCursorType[kReg]; RunCursorType::set_prefetcher(p); #else current = new RunCursorType[kReg]; for (i = 0; i < kReg; ++i) current[i].prefetcher() = p; #endif entry = new int_type[(kReg << 1)]; // init cursors for (i = 0; i < nruns; ++i) { current[i].buffer = p->pull_block(); //current[i].pos = 0; // done in constructor entry[kReg + i] = i; } for (i = nruns; i < kReg; ++i) { current[i].make_inf(); entry[kReg + i] = i; } entry[0] = init_winner(1); } ~loser_tree() { delete[] current; delete[] entry; } void swap(loser_tree& obj) { std::swap(logK, obj.logK); std::swap(k, obj.k); std::swap(entry, obj.entry); std::swap(current, obj.current); std::swap(cmp, obj.cmp); } private: template void multi_merge_unrolled(value_type* out_first, value_type* out_last) { RunCursorType* currentE, * winnerE; int_type* regEntry = entry; int_type winnerIndex = regEntry[0]; while (LIKELY(out_first != out_last)) { winnerE = current + winnerIndex; *(out_first) = winnerE->current(); ++out_first; ++(*winnerE); #define TreeStep(L) \ if (LogK >= L) \ { \ currentE = current + \ regEntry[(winnerIndex + (1 << LogK)) >> (((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0)]; \ if (cmp(*currentE, *winnerE)) \ { \ std::swap(regEntry[(winnerIndex + (1 << LogK)) \ >> (((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0)], winnerIndex); \ winnerE = currentE; \ } \ } TreeStep(10); TreeStep(9); TreeStep(8); TreeStep(7); TreeStep(6); TreeStep(5); TreeStep(4); TreeStep(3); TreeStep(2); TreeStep(1); #undef TreeStep } regEntry[0] = winnerIndex; } void multi_merge_unrolled_0(value_type* out_first, value_type* out_last) { while (LIKELY(out_first != out_last)) { *out_first = current->current(); ++out_first; ++(*current); } } void multi_merge_k(value_type* out_first, value_type* out_last) { RunCursorType* currentE, * winnerE; int_type kReg = k; int_type winnerIndex = entry[0]; while (LIKELY(out_first != out_last)) { winnerE = current + winnerIndex; *(out_first) = winnerE->current(); ++out_first; ++(*winnerE); for (int_type i = (winnerIndex + kReg) >> 1; i > 0; i >>= 1) { currentE = current + entry[i]; if (cmp(*currentE, *winnerE)) { std::swap(entry[i], winnerIndex); winnerE = currentE; } } } entry[0] = winnerIndex; } public: void multi_merge(value_type* out_first, value_type* out_last) { switch (logK) { case 0: multi_merge_unrolled_0(out_first, out_last); break; case 1: multi_merge_unrolled<1>(out_first, out_last); break; case 2: multi_merge_unrolled<2>(out_first, out_last); break; case 3: multi_merge_unrolled<3>(out_first, out_last); break; case 4: multi_merge_unrolled<4>(out_first, out_last); break; case 5: multi_merge_unrolled<5>(out_first, out_last); break; case 6: multi_merge_unrolled<6>(out_first, out_last); break; case 7: multi_merge_unrolled<7>(out_first, out_last); break; case 8: multi_merge_unrolled<8>(out_first, out_last); break; case 9: multi_merge_unrolled<9>(out_first, out_last); break; case 10: multi_merge_unrolled<10>(out_first, out_last); break; default: multi_merge_k(out_first, out_last); break; } } }; STXXL_END_NAMESPACE namespace std { template void swap(stxxl::loser_tree& a, stxxl::loser_tree& b) { a.swap(b); } } // namespace std #endif // !STXXL_ALGO_LOSERTREE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/ksort.h000644 001411 000144 00000107537 12414452316 020517 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/ksort.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008-2011 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_KSORT_HEADER #define STXXL_ALGO_KSORT_HEADER #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define INTERLEAVED_ALLOC #define OPT_MERGING STXXL_BEGIN_NAMESPACE //! \addtogroup stllayer //! \defgroup stlalgo Algorithms //! \ingroup stllayer //! Algorithms with STL-compatible interface //! \{ /*! \internal */ namespace ksort_local { template struct trigger_entry { typedef BIDType bid_type; typedef KeyType key_type; bid_type bid; key_type key; operator bid_type () { return bid; } }; template inline bool operator < (const trigger_entry& a, const trigger_entry& b) { return (a.key < b.key); } template inline bool operator > (const trigger_entry& a, const trigger_entry& b) { return (a.key > b.key); } template struct type_key { typedef KeyType key_type; key_type key; Type* ptr; type_key() { } type_key(key_type k, Type* p) : key(k), ptr(p) { } }; template bool operator < (const type_key& a, const type_key& b) { return a.key < b.key; } template bool operator > (const type_key& a, const type_key& b) { return a.key > b.key; } template struct write_completion_handler { BlockType* block; BidType bid; request_ptr* req; void operator () (request* /*completed_req*/) { * req = block->read(bid); } }; template inline void write_out( TypeKey* begin, TypeKey* end, BlockType*& cur_blk, const BlockType* end_blk, int_type& out_block, int_type& out_pos, RunType& run, write_completion_handler*& next_read, typename BlockType::bid_type*& bids, request_ptr* write_reqs, request_ptr* read_reqs, InputBidIterator& it, KeyExtractor keyobj) { typedef typename BlockType::type type; type* elem = cur_blk->elem; for (TypeKey* p = begin; p < end; p++) { elem[out_pos++] = *(p->ptr); if (out_pos >= BlockType::size) { run[out_block].key = keyobj(*(cur_blk->elem)); if (cur_blk < end_blk) { next_read->block = cur_blk; next_read->req = read_reqs + out_block; read_reqs[out_block] = NULL; bids[out_block] = next_read->bid = *(it++); write_reqs[out_block] = cur_blk->write( run[out_block].bid, // postpone read of block from next run // after write of block from this run *(next_read++)); } else { write_reqs[out_block] = cur_blk->write(run[out_block].bid); } cur_blk++; elem = cur_blk->elem; out_block++; out_pos = 0; } } } template < typename BlockType, typename RunType, typename InputBidIterator, typename KeyExtractor> void create_runs( InputBidIterator it, RunType** runs, const unsigned_type nruns, const unsigned_type m2, KeyExtractor keyobj) { typedef typename BlockType::value_type type; typedef typename BlockType::bid_type bid_type; typedef typename KeyExtractor::key_type key_type; typedef type_key type_key_; block_manager* bm = block_manager::get_instance(); BlockType* Blocks1 = new BlockType[m2]; BlockType* Blocks2 = new BlockType[m2]; bid_type* bids = new bid_type[m2]; type_key_* refs1 = new type_key_[m2 * Blocks1->size]; type_key_* refs2 = new type_key_[m2 * Blocks1->size]; request_ptr* read_reqs = new request_ptr[m2]; request_ptr* write_reqs = new request_ptr[m2]; write_completion_handler* next_run_reads = new write_completion_handler[m2]; RunType* run; run = *runs; int_type run_size = (*runs)->size(); key_type offset = 0; const int log_k1 = ilog2_ceil((m2 * BlockType::size * sizeof(type_key_) / STXXL_L2_SIZE) ? (m2 * BlockType::size * sizeof(type_key_) / STXXL_L2_SIZE) : 2); const int log_k2 = ilog2_floor(m2 * Blocks1->size) - log_k1 - 1; STXXL_VERBOSE("log_k1: " << log_k1 << " log_k2:" << log_k2); const int_type k1 = int_type(1) << log_k1; const int_type k2 = int_type(1) << log_k2; int_type* bucket1 = new int_type[k1]; int_type* bucket2 = new int_type[k2]; int_type i; disk_queues::get_instance()->set_priority_op(request_queue::WRITE); for (i = 0; i < run_size; i++) { bids[i] = *(it++); read_reqs[i] = Blocks1[i].read(bids[i]); } unsigned_type k = 0; const int shift1 = (int)(sizeof(key_type) * 8 - log_k1); const int shift2 = shift1 - log_k2; STXXL_VERBOSE("shift1: " << shift1 << " shift2:" << shift2); for ( ; k < nruns; k++) { run = runs[k]; run_size = run->size(); std::fill(bucket1, bucket1 + k1, 0); type_key_* ref_ptr = refs1; for (i = 0; i < run_size; i++) { if (k) write_reqs[i]->wait(); read_reqs[i]->wait(); bm->delete_block(bids[i]); classify_block(Blocks1[i].begin(), Blocks1[i].end(), ref_ptr, bucket1, offset, shift1, keyobj); } exclusive_prefix_sum(bucket1, k1); classify(refs1, refs1 + run_size * Blocks1->size, refs2, bucket1, offset, shift1); int_type out_block = 0; int_type out_pos = 0; unsigned_type next_run_size = (k < nruns - 1) ? (runs[k + 1]->size()) : 0; // recurse on each bucket type_key_* c = refs2; type_key_* d = refs1; BlockType* cur_blk = Blocks2; BlockType* end_blk = Blocks2 + next_run_size; write_completion_handler* next_read = next_run_reads; for (i = 0; i < k1; i++) { type_key_* cEnd = refs2 + bucket1[i]; type_key_* dEnd = refs1 + bucket1[i]; l1sort(c, cEnd, d, bucket2, k2, offset + (key_type(1) << key_type(shift1)) * key_type(i), shift2); // key_type,key_type,... paranoia write_out( d, dEnd, cur_blk, end_blk, out_block, out_pos, *run, next_read, bids, write_reqs, read_reqs, it, keyobj); c = cEnd; d = dEnd; } std::swap(Blocks1, Blocks2); } wait_all(write_reqs, m2); delete[] bucket1; delete[] bucket2; delete[] refs1; delete[] refs2; delete[] Blocks1; delete[] Blocks2; delete[] bids; delete[] next_run_reads; delete[] read_reqs; delete[] write_reqs; } template struct run_cursor2_cmp : public std::binary_function< run_cursor2, run_cursor2, bool > { typedef run_cursor2 cursor_type; KeyExtractor keyobj; run_cursor2_cmp(KeyExtractor _keyobj) : keyobj(_keyobj) { } inline bool operator () (const cursor_type& a, const cursor_type& b) const { if (UNLIKELY(b.empty())) return true; // sentinel emulation if (UNLIKELY(a.empty())) return false; //sentinel emulation return (keyobj(a.current()) < keyobj(b.current())); } private: run_cursor2_cmp() { } }; template class key_comparison : public std::binary_function { KeyExtractor ke; public: key_comparison() { } key_comparison(KeyExtractor ke_) : ke(ke_) { } bool operator () (const RecordType& a, const RecordType& b) const { return ke(a) < ke(b); } }; template bool check_ksorted_runs(RunType** runs, unsigned_type nruns, unsigned_type m, KeyExtractor keyext) { typedef BlockType block_type; typedef typename BlockType::value_type value_type; STXXL_MSG("check_ksorted_runs Runs: " << nruns); unsigned_type irun = 0; for (irun = 0; irun < nruns; ++irun) { const unsigned_type nblocks_per_run = runs[irun]->size(); unsigned_type blocks_left = nblocks_per_run; block_type* blocks = new block_type[m]; request_ptr* reqs = new request_ptr[m]; value_type last = keyext.min_value(); for (unsigned_type off = 0; off < nblocks_per_run; off += m) { const unsigned_type nblocks = STXXL_MIN(blocks_left, m); const unsigned_type nelements = nblocks * block_type::size; blocks_left -= nblocks; for (unsigned_type j = 0; j < nblocks; ++j) { reqs[j] = blocks[j].read((*runs[irun])[off + j].bid); } wait_all(reqs, reqs + nblocks); if (off && (keyext(blocks[0][0]) < keyext(last))) { STXXL_MSG("check_sorted_runs wrong first value in the run " << irun); STXXL_MSG(" first value: " << blocks[0][0] << " with key" << keyext(blocks[0][0])); STXXL_MSG(" last value: " << last << " with key" << keyext(last)); for (unsigned_type k = 0; k < block_type::size; ++k) STXXL_MSG("Element " << k << " in the block is :" << blocks[0][k] << " key: " << keyext(blocks[0][k])); delete[] reqs; delete[] blocks; return false; } for (unsigned_type j = 0; j < nblocks; ++j) { if (keyext(blocks[j][0]) != (*runs[irun])[off + j].key) { STXXL_MSG("check_sorted_runs wrong trigger in the run " << irun << " block " << (off + j)); STXXL_MSG(" trigger value: " << (*runs[irun])[off + j].key); STXXL_MSG("Data in the block:"); for (unsigned_type k = 0; k < block_type::size; ++k) STXXL_MSG("Element " << k << " in the block is :" << blocks[j][k] << " with key: " << keyext(blocks[j][k])); STXXL_MSG("BIDS:"); for (unsigned_type k = 0; k < nblocks; ++k) { if (k == j) STXXL_MSG("Bad one comes next."); STXXL_MSG("BID " << (off + k) << " is: " << ((*runs[irun])[off + k].bid)); } delete[] reqs; delete[] blocks; return false; } } if (!stxxl::is_sorted(make_element_iterator(blocks, 0), make_element_iterator(blocks, nelements), key_comparison())) { STXXL_MSG("check_sorted_runs wrong order in the run " << irun); STXXL_MSG("Data in blocks:"); for (unsigned_type j = 0; j < nblocks; ++j) { for (unsigned_type k = 0; k < block_type::size; ++k) STXXL_MSG(" Element " << k << " in block " << (off + j) << " is :" << blocks[j][k] << " with key: " << keyext(blocks[j][k])); } STXXL_MSG("BIDS:"); for (unsigned_type k = 0; k < nblocks; ++k) { STXXL_MSG("BID " << (k + off) << " is: " << ((*runs[irun])[k + off].bid)); } delete[] reqs; delete[] blocks; return false; } last = blocks[nblocks - 1][block_type::size - 1]; } assert(blocks_left == 0); delete[] reqs; delete[] blocks; } return true; } template void merge_runs(RunType** in_runs, unsigned_type nruns, RunType* out_run, unsigned_type _m, KeyExtractor keyobj) { typedef BlockType block_type; typedef block_prefetcher prefetcher_type; typedef run_cursor2 run_cursor_type; unsigned_type i; RunType consume_seq(out_run->size()); int_type* prefetch_seq = new int_type[out_run->size()]; typename RunType::iterator copy_start = consume_seq.begin(); for (i = 0; i < nruns; i++) { // TODO: try to avoid copy copy_start = std::copy( in_runs[i]->begin(), in_runs[i]->end(), copy_start); } std::stable_sort(consume_seq.begin(), consume_seq.end() _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL); size_t disks_number = config::get_instance()->disks_number(); #ifdef PLAY_WITH_OPT_PREF const int_type n_write_buffers = 4 * disks_number; #else const int_type n_prefetch_buffers = STXXL_MAX(int_type(2 * disks_number), (3 * (int_type(_m) - int_type(nruns)) / 4)); STXXL_VERBOSE("Prefetch buffers " << n_prefetch_buffers); const int_type n_write_buffers = STXXL_MAX(int_type(2 * disks_number), int_type(_m) - int_type(nruns) - int_type(n_prefetch_buffers)); STXXL_VERBOSE("Write buffers " << n_write_buffers); // heuristic const int_type n_opt_prefetch_buffers = 2 * int_type(disks_number) + (3 * (int_type(n_prefetch_buffers) - int_type(2 * disks_number))) / 10; STXXL_VERBOSE("Prefetch buffers " << n_opt_prefetch_buffers); #endif #if STXXL_SORT_OPTIMAL_PREFETCHING compute_prefetch_schedule( consume_seq, prefetch_seq, n_opt_prefetch_buffers, config::get_instance()->get_max_device_id()); #else for (i = 0; i < out_run->size(); i++) prefetch_seq[i] = i; #endif prefetcher_type prefetcher(consume_seq.begin(), consume_seq.end(), prefetch_seq, nruns + n_prefetch_buffers); buffered_writer writer(n_write_buffers, n_write_buffers / 2); unsigned_type out_run_size = out_run->size(); run_cursor2_cmp cmp(keyobj); loser_tree< run_cursor_type, run_cursor2_cmp > losers(&prefetcher, nruns, cmp); block_type* out_buffer = writer.get_free_block(); for (i = 0; i < out_run_size; i++) { losers.multi_merge(out_buffer->elem, out_buffer->elem + block_type::size); (*out_run)[i].key = keyobj(out_buffer->elem[0]); out_buffer = writer.write(out_buffer, (*out_run)[i].bid); } delete[] prefetch_seq; block_manager* bm = block_manager::get_instance(); for (i = 0; i < nruns; i++) { unsigned_type sz = in_runs[i]->size(); for (unsigned_type j = 0; j < sz; j++) bm->delete_block((*in_runs[i])[j].bid); delete in_runs[i]; } } template simple_vector< trigger_entry >* ksort_blocks(InputBidIterator input_bids, unsigned_type _n, unsigned_type _m, KeyExtractor keyobj) { typedef BlockType block_type; typedef typename BlockType::value_type type; typedef typename KeyExtractor::key_type key_type; typedef typename BlockType::bid_type bid_type; typedef trigger_entry trigger_entry_type; typedef simple_vector run_type; typedef typename interleaved_alloc_traits::strategy interleaved_alloc_strategy; unsigned_type m2 = div_ceil(_m, 2); const unsigned_type m2_rf = m2 * block_type::raw_size / (block_type::raw_size + block_type::size * sizeof(type_key)); STXXL_VERBOSE("Reducing number of blocks in a run from " << m2 << " to " << m2_rf << " due to key size: " << sizeof(typename KeyExtractor::key_type) << " bytes"); m2 = m2_rf; unsigned_type full_runs = _n / m2; unsigned_type partial_runs = ((_n % m2) ? 1 : 0); unsigned_type nruns = full_runs + partial_runs; unsigned_type i; block_manager* mng = block_manager::get_instance(); STXXL_VERBOSE("n=" << _n << " nruns=" << nruns << "=" << full_runs << "+" << partial_runs); double begin = timestamp(), after_runs_creation, end; run_type** runs = new run_type*[nruns]; for (i = 0; i < full_runs; i++) runs[i] = new run_type(m2); #ifdef INTERLEAVED_ALLOC if (partial_runs) { unsigned_type last_run_size = _n - full_runs * m2; runs[i] = new run_type(last_run_size); mng->new_blocks(interleaved_alloc_strategy(nruns, AllocStrategy()), runs2bid_array_adaptor2 (runs, 0, nruns, last_run_size), runs2bid_array_adaptor2 (runs, _n, nruns, last_run_size)); } else mng->new_blocks(interleaved_alloc_strategy(nruns, AllocStrategy()), runs2bid_array_adaptor (runs, 0, nruns), runs2bid_array_adaptor (runs, _n, nruns)); #else if (partial_runs) runs[i] = new run_type(_n - full_runs * m2); for (i = 0; i < nruns; i++) { mng->new_blocks(AllocStrategy(), make_bid_iterator(runs[i]->begin()), make_bid_iterator(runs[i]->end())); } #endif create_runs( input_bids, runs, nruns, m2, keyobj); after_runs_creation = timestamp(); double io_wait_after_rf = stats::get_instance()->get_io_wait_time(); disk_queues::get_instance()->set_priority_op(request_queue::WRITE); const int_type merge_factor = optimal_merge_factor(nruns, _m); run_type** new_runs; while (nruns > 1) { int_type new_nruns = div_ceil(nruns, merge_factor); STXXL_VERBOSE("Starting new merge phase: nruns: " << nruns << " opt_merge_factor: " << merge_factor << " m:" << _m << " new_nruns: " << new_nruns); new_runs = new run_type*[new_nruns]; int_type runs_left = nruns; int_type cur_out_run = 0; int_type blocks_in_new_run = 0; while (runs_left > 0) { int_type runs2merge = STXXL_MIN(runs_left, merge_factor); blocks_in_new_run = 0; for (unsigned_type i = nruns - runs_left; i < (nruns - runs_left + runs2merge); i++) blocks_in_new_run += runs[i]->size(); // allocate run new_runs[cur_out_run++] = new run_type(blocks_in_new_run); runs_left -= runs2merge; } // allocate blocks in the new runs if (cur_out_run == 1 && blocks_in_new_run == int_type(_n) && !input_bids->is_managed()) { // if we sort a file we can reuse the input bids for the output InputBidIterator cur = input_bids; for (int_type i = 0; cur != (input_bids + _n); ++cur) { (*new_runs[0])[i++].bid = *cur; } bid_type& firstBID = (*new_runs[0])[0].bid; if (firstBID.is_managed()) { // the first block does not belong to the file // need to reallocate it mng->new_block(FR(), firstBID); } bid_type& lastBID = (*new_runs[0])[_n - 1].bid; if (lastBID.is_managed()) { // the first block does not belong to the file // need to reallocate it mng->new_block(FR(), lastBID); } } else { mng->new_blocks(interleaved_alloc_strategy(new_nruns, AllocStrategy()), runs2bid_array_adaptor2(new_runs, 0, new_nruns, blocks_in_new_run), runs2bid_array_adaptor2(new_runs, _n, new_nruns, blocks_in_new_run)); } // merge all runs_left = nruns; cur_out_run = 0; while (runs_left > 0) { int_type runs2merge = STXXL_MIN(runs_left, merge_factor); #if STXXL_CHECK_ORDER_IN_SORTS assert((check_ksorted_runs(runs + nruns - runs_left, runs2merge, m2, keyobj))); #endif STXXL_VERBOSE("Merging " << runs2merge << " runs"); merge_runs(runs + nruns - runs_left, runs2merge, *(new_runs + (cur_out_run++)), _m, keyobj); runs_left -= runs2merge; } nruns = new_nruns; delete[] runs; runs = new_runs; } run_type* result = *runs; delete[] runs; end = timestamp(); STXXL_VERBOSE("Elapsed time : " << end - begin << " s. Run creation time: " << after_runs_creation - begin << " s"); STXXL_VERBOSE("Time in I/O wait(rf): " << io_wait_after_rf << " s"); STXXL_VERBOSE(*stats::get_instance()); return result; } } // namespace ksort_local /*! * Sort records with integer keys, see \ref design_algo_ksort. * * stxxl::ksort sorts the elements in [first, last) into ascending order, * meaning that if \c i and \c j are any two valid iterators in [first, last) * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as * std::sort and stxxl::sort, stxxl::ksort is not guaranteed to be stable. That * is, suppose that \c *i and \c *j are equivalent: neither one is less than * the other. It is not guaranteed that the relative order of these two * elements will be preserved by stxxl::ksort. * * The two versions of stxxl::ksort differ in how they define whether one * element is less than another. The first version assumes that the elements * have \c key() member function that returns an integral key (32 or 64 bit), * as well as the minimum and the maximum element values. The second version * compares objects extracting the keys using \c keyobj object, that is in turn * provides min and max element values. * * The sorter's internal memory consumption is bounded by \c M bytes. * * \param first object of model of \c ext_random_access_iterator concept * \param last object of model of \c ext_random_access_iterator concept * \param keyobj \link design_algo_ksort_key_extractor key extractor \endlink object * \param M amount of memory for internal use (in bytes) */ template void ksort(ExtIterator first, ExtIterator last, KeyExtractor keyobj, unsigned_type M) { typedef simple_vector< ksort_local::trigger_entry< typename ExtIterator::bid_type, typename KeyExtractor::key_type > > run_type; typedef typename ExtIterator::vector_type::value_type value_type; typedef typename ExtIterator::bid_type bid_type; typedef typename ExtIterator::block_type block_type; typedef typename ExtIterator::vector_type::alloc_strategy_type alloc_strategy_type; typedef typename ExtIterator::bids_container_iterator bids_container_iterator; unsigned_type n = 0; block_manager* mng = block_manager::get_instance(); first.flush(); if ((last - first) * sizeof(value_type) < M) { stl_in_memory_sort(first, last, ksort_local::key_comparison(keyobj)); } else { assert(2 * block_type::raw_size <= M); if (first.block_offset()) { if (last.block_offset()) // first and last element reside // not in the beginning of the block { block_type* first_block = new block_type; block_type* last_block = new block_type; bid_type first_bid, last_bid; request_ptr req; req = first_block->read(*first.bid()); mng->new_block(FR(), first_bid); // try to overlap mng->new_block(FR(), last_bid); req->wait(); req = last_block->read(*last.bid()); unsigned_type i = 0; for ( ; i < first.block_offset(); i++) { first_block->elem[i] = keyobj.min_value(); } req->wait(); req = first_block->write(first_bid); for (i = last.block_offset(); i < block_type::size; i++) { last_block->elem[i] = keyobj.max_value(); } req->wait(); req = last_block->write(last_bid); n = last.bid() - first.bid() + 1; std::swap(first_bid, *first.bid()); std::swap(last_bid, *last.bid()); req->wait(); delete first_block; delete last_block; run_type* out = ksort_local::ksort_blocks< block_type, alloc_strategy_type, bids_container_iterator, KeyExtractor >(first.bid(), n, M / block_type::raw_size, keyobj); first_block = new block_type; last_block = new block_type; block_type* sorted_first_block = new block_type; block_type* sorted_last_block = new block_type; request_ptr* reqs = new request_ptr[2]; reqs[0] = first_block->read(first_bid); reqs[1] = sorted_first_block->read((*(out->begin())).bid); wait_all(reqs, 2); reqs[0] = last_block->read(last_bid); reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); for (i = first.block_offset(); i < block_type::size; i++) { first_block->elem[i] = sorted_first_block->elem[i]; } wait_all(reqs, 2); req = first_block->write(first_bid); for (i = 0; i < last.block_offset(); i++) { last_block->elem[i] = sorted_last_block->elem[i]; } req->wait(); req = last_block->write(last_bid); mng->delete_block(out->begin()->bid); mng->delete_block((*out)[out->size() - 1].bid); *first.bid() = first_bid; *last.bid() = last_bid; typename run_type::iterator it = out->begin(); it++; bids_container_iterator cur_bid = first.bid(); cur_bid++; for ( ; cur_bid != last.bid(); cur_bid++, it++) { *cur_bid = (*it).bid; } delete first_block; delete sorted_first_block; delete sorted_last_block; delete[] reqs; delete out; req->wait(); delete last_block; } else { // first element resides // not in the beginning of the block block_type* first_block = new block_type; bid_type first_bid; request_ptr req; req = first_block->read(*first.bid()); mng->new_block(FR(), first_bid); // try to overlap req->wait(); unsigned_type i = 0; for ( ; i < first.block_offset(); i++) { first_block->elem[i] = keyobj.min_value(); } req = first_block->write(first_bid); n = last.bid() - first.bid(); std::swap(first_bid, *first.bid()); req->wait(); delete first_block; run_type* out = ksort_local::ksort_blocks< block_type, alloc_strategy_type, bids_container_iterator, KeyExtractor >(first.bid(), n, M / block_type::raw_size, keyobj); first_block = new block_type; block_type* sorted_first_block = new block_type; request_ptr* reqs = new request_ptr[2]; reqs[0] = first_block->read(first_bid); reqs[1] = sorted_first_block->read((*(out->begin())).bid); wait_all(reqs, 2); for (i = first.block_offset(); i < block_type::size; i++) { first_block->elem[i] = sorted_first_block->elem[i]; } req = first_block->write(first_bid); mng->delete_block(out->begin()->bid); *first.bid() = first_bid; typename run_type::iterator it = out->begin(); it++; bids_container_iterator cur_bid = first.bid(); cur_bid++; for ( ; cur_bid != last.bid(); cur_bid++, it++) { *cur_bid = (*it).bid; } *cur_bid = (*it).bid; delete sorted_first_block; delete[] reqs; delete out; req->wait(); delete first_block; } } else { if (last.block_offset()) // last element resides // not in the beginning of the block { block_type* last_block = new block_type; bid_type last_bid; request_ptr req; unsigned_type i; req = last_block->read(*last.bid()); mng->new_block(FR(), last_bid); req->wait(); for (i = last.block_offset(); i < block_type::size; i++) { last_block->elem[i] = keyobj.max_value(); } req = last_block->write(last_bid); n = last.bid() - first.bid() + 1; std::swap(last_bid, *last.bid()); req->wait(); delete last_block; run_type* out = ksort_local::ksort_blocks< block_type, alloc_strategy_type, bids_container_iterator, KeyExtractor >(first.bid(), n, M / block_type::raw_size, keyobj); last_block = new block_type; block_type* sorted_last_block = new block_type; request_ptr* reqs = new request_ptr[2]; reqs[0] = last_block->read(last_bid); reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); wait_all(reqs, 2); for (i = 0; i < last.block_offset(); i++) { last_block->elem[i] = sorted_last_block->elem[i]; } req = last_block->write(last_bid); mng->delete_block((*out)[out->size() - 1].bid); *last.bid() = last_bid; typename run_type::iterator it = out->begin(); bids_container_iterator cur_bid = first.bid(); for ( ; cur_bid != last.bid(); cur_bid++, it++) { *cur_bid = (*it).bid; } delete sorted_last_block; delete[] reqs; delete out; req->wait(); delete last_block; } else { // first and last element reside in the beginning of blocks n = last.bid() - first.bid(); run_type* out = ksort_local::ksort_blocks< block_type, alloc_strategy_type, bids_container_iterator, KeyExtractor >(first.bid(), n, M / block_type::raw_size, keyobj); typename run_type::iterator it = out->begin(); bids_container_iterator cur_bid = first.bid(); for ( ; cur_bid != last.bid(); cur_bid++, it++) { *cur_bid = (*it).bid; } delete out; } } } #if STXXL_CHECK_ORDER_IN_SORTS typedef typename ExtIterator::const_iterator const_iterator; STXXL_ASSERT(stxxl::is_sorted(const_iterator(first), const_iterator(last), ksort_local::key_comparison())); #endif } template struct ksort_defaultkey { typedef typename RecordType::key_type key_type; key_type operator () (const RecordType& obj) const { return obj.key(); } RecordType max_value() const { return RecordType::max_value(); } RecordType min_value() const { return RecordType::min_value(); } }; /*! * Sort records with integer keys, see \ref design_algo_ksort. * * stxxl::ksort sorts the elements in [first, last) into ascending order, * meaning that if \c i and \c j are any two valid iterators in [first, last) * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as * std::sort and stxxl::sort, stxxl::ksort is not guaranteed to be stable. That * is, suppose that \c *i and \c *j are equivalent: neither one is less than * the other. It is not guaranteed that the relative order of these two * elements will be preserved by stxxl::ksort. * * \param first object of model of \c ext_random_access_iterator concept * \param last object of model of \c ext_random_access_iterator concept * \param M amount of buffers for internal use * \remark Order in the result is non-stable */ template void ksort(ExtIterator first, ExtIterator last, unsigned_type M) { ksort(first, last, ksort_defaultkey(), M); } //! \} STXXL_END_NAMESPACE #endif // !STXXL_ALGO_KSORT_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/async_schedule.h000644 001411 000144 00000004451 12414452316 022335 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/async_schedule.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_ASYNC_SCHEDULE_HEADER #define STXXL_ALGO_ASYNC_SCHEDULE_HEADER // Implements the "prudent prefetching" as described in // D. Hutchinson, P. Sanders, J. S. Vitter: Duality between prefetching // and queued writing on parallel disks, 2005 // DOI: 10.1137/S0097539703431573 #include #include #include STXXL_BEGIN_NAMESPACE void compute_prefetch_schedule( const int_type* first, const int_type* last, int_type* out_first, int_type m, int_type D); inline void compute_prefetch_schedule( int_type* first, int_type* last, int_type* out_first, int_type m, int_type D) { compute_prefetch_schedule(static_cast(first), last, out_first, m, D); } template void compute_prefetch_schedule( const RunType& input, int_type* out_first, int_type m, int_type D) { const int_type L = input.size(); simple_vector disks(L); for (int_type i = 0; i < L; ++i) disks[i] = input[i].bid.storage->get_device_id(); compute_prefetch_schedule(disks.begin(), disks.end(), out_first, m, D); } template void compute_prefetch_schedule( BidIteratorType input_begin, BidIteratorType input_end, int_type* out_first, int_type m, int_type D) { const int_type L = input_end - input_begin; simple_vector disks(L); int_type i = 0; for (BidIteratorType it = input_begin; it != input_end; ++it, ++i) disks[i] = it->storage->get_device_id(); compute_prefetch_schedule(disks.begin(), disks.end(), out_first, m, D); } STXXL_END_NAMESPACE #endif // !STXXL_ALGO_ASYNC_SCHEDULE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/inmemsort.h000644 001411 000144 00000004022 12414452316 021353 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/inmemsort.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_INMEMSORT_HEADER #define STXXL_ALGO_INMEMSORT_HEADER #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE template void stl_in_memory_sort(ExtIterator first, ExtIterator last, StrictWeakOrdering cmp) { typedef typename ExtIterator::block_type block_type; STXXL_VERBOSE("stl_in_memory_sort, range: " << (last - first)); first.flush(); unsigned_type nblocks = last.bid() - first.bid() + (last.block_offset() ? 1 : 0); simple_vector blocks(nblocks); simple_vector reqs(nblocks); unsigned_type i; for (i = 0; i < nblocks; ++i) reqs[i] = blocks[i].read(*(first.bid() + i)); wait_all(reqs.begin(), nblocks); unsigned_type last_block_correction = last.block_offset() ? (block_type::size - last.block_offset()) : 0; check_sort_settings(); potentially_parallel:: sort(make_element_iterator(blocks.begin(), first.block_offset()), make_element_iterator(blocks.begin(), nblocks * block_type::size - last_block_correction), cmp); for (i = 0; i < nblocks; ++i) reqs[i] = blocks[i].write(*(first.bid() + i)); wait_all(reqs.begin(), nblocks); } STXXL_END_NAMESPACE #endif // !STXXL_ALGO_INMEMSORT_HEADER stxxl-1.4.1/include/stxxl/bits/algo/sort_base.h000644 001411 000144 00000002612 12405375303 021322 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/sort_base.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_SORT_BASE_HEADER #define STXXL_ALGO_SORT_BASE_HEADER #include #include #ifndef STXXL_NO_WARN_RECURSIVE_SORT #define STXXL_WARNMSG_RECURSIVE_SORT STXXL_ERRMSG #else #define STXXL_WARNMSG_RECURSIVE_SORT STXXL_VERBOSE #endif #ifndef STXXL_SORT_OPTIMAL_PREFETCHING #define STXXL_SORT_OPTIMAL_PREFETCHING 1 #endif #ifndef STXXL_CHECK_ORDER_IN_SORTS #define STXXL_CHECK_ORDER_IN_SORTS 0 #endif #ifndef STXXL_L2_SIZE #define STXXL_L2_SIZE (512 * 1024) #endif STXXL_BEGIN_NAMESPACE // Optimal merging: merge r = pow(nruns,1/ceil(log(nruns)/log(m))) runs at once inline unsigned_type optimal_merge_factor(unsigned_type num_runs, unsigned_type max_concurrent_runs) { return unsigned_type(ceil(pow(double(num_runs), 1. / ceil(log(double(num_runs)) / log(double(max_concurrent_runs)))))); } STXXL_END_NAMESPACE #endif // !STXXL_ALGO_SORT_BASE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/intksort.h000644 001411 000144 00000021740 12414452316 021221 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/intksort.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Peter Sanders * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_INTKSORT_HEADER #define STXXL_ALGO_INTKSORT_HEADER #include #include #include #include #include STXXL_BEGIN_NAMESPACE template static void count(TypeKey* a, TypeKey* aEnd, int_type* bucket, int_type K, typename TypeKey::key_type offset, unsigned shift) { // reset buckets std::fill(bucket, bucket + K, 0); // count occupancies for (TypeKey* p = a; p < aEnd; p++) { int_type i = (int_type)((p->key - offset) >> shift); /* if (!(i < K && i >= 0)) { STXXL_ERRMSG("i: " << i); abort(); } */ bucket[i]++; } } static inline void exclusive_prefix_sum(int_type* bucket, int_type K) { int_type sum = 0; for (int_type i = 0; i < K; i++) { int_type current = bucket[i]; bucket[i] = sum; sum += current; } } // distribute input a to output b using bucket for the starting indices template static void classify(TypeKey* a, TypeKey* aEnd, TypeKey* b, int_type* bucket, typename TypeKey::key_type offset, unsigned shift) { for (TypeKey* p = a; p < aEnd; p++) { int_type i = (int_type)((p->key - offset) >> shift); int_type bi = bucket[i]; b[bi] = *p; bucket[i] = bi + 1; } } template inline void sort2(Type& a, Type& b) { if (b < a) std::swap(a, b); } template inline void sort3(Type& a, Type& b, Type& c) { Type temp; if (b < a) { if (c < a) { // b , c < a if (b < c) { // b < c < a temp = a; a = b; b = c; c = temp; } else { // c <=b < a std::swap(c, a); } } else { // b < a <=c std::swap(a, b); } } else { // a <=b if (c < a) { // c < a <=b temp = a; a = c; c = b; b = temp; } else { // a <=b , c if (c < b) { // a <=c < b std::swap(b, c); } } } // Assert1 (!(b < a) && !(c < b)); } template inline void sort4(Type& a, Type& b, Type& c, Type& d) { sort2(a, b); sort2(c, d); // a < b ; c < d if (c < a) { // c minimal, a < b if (d < a) { // c < d < a < b std::swap(a, c); std::swap(b, d); } else { // c < a < {db} if (d < b) { // c < a < d < b Type temp = a; a = c; c = d; d = b; b = temp; } else { // c < a < b < d Type temp = a; a = c; c = b; b = temp; } } } else { // a minimal ; c < d if (c < b) { // c < (bd) if (d < b) { // c < d < b Type temp = b; b = c; c = d; d = temp; } else { // a < c < b < d std::swap(b, c); } } // else sorted } //Assert1 (!(b < a) && !(c < b) & !(d < c)); } template inline void sort5(Type& a, Type& b, Type& c, Type& d, Type& e) { sort2(a, b); sort2(d, e); if (d < a) { std::swap(a, d); std::swap(b, e); } // a < d < e, a < b if (d < c) { std::swap(c, d); // a minimal, c < {de} sort2(d, e); } else { // a inline void insertion_sort(Type* a, Type* aEnd) { Type* pp; for (Type* p = a + 1; p < aEnd; p++) { // Invariant a..p-1 is sorted; Type t = *p; if (t < *a) { // new minimum // move stuff to the right for (pp = p; pp != a; pp--) { *pp = *(pp - 1); } *pp = t; } else { // now we can use *a as a sentinel for (pp = p; t < *(pp - 1); pp--) { *pp = *(pp - 1); } *pp = t; } } } // sort each bucket // bucket[i] is an index one off to the right from // the end of the i-th bucket template static void cleanup(Type* b, int_type* bucket, int_type K) { Type* c = b; for (int_type i = 0; i < K; i++) { Type* cEnd = b + bucket[i]; switch (cEnd - c) { case 0: break; case 1: break; case 2: sort2(c[0], c[1]); break; case 3: sort3(c[0], c[1], c[2]); break; case 4: sort4(c[0], c[1], c[2], c[3]); break; case 5: #if 0 sort5(c[0], c[1], c[2], c[3], c[4]); break; #endif case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: insertion_sort(c, cEnd); break; default: check_sort_settings(); potentially_parallel:: sort(c, cEnd); } c = cEnd; } } // do a single level MDS radix sort // using bucket[0..K-1] as a counter array // and using (key(x) - offset) >> shift to index buckets. // and using (key(x) - offset) >> shift to index buckets. // the input comes from a..aEnd-1 // the output goes to b template void l1sort(TypeKey* a, TypeKey* aEnd, TypeKey* b, int_type* bucket, int_type K, typename TypeKey::key_type offset, int shift) { count(a, aEnd, bucket, K, offset, shift); exclusive_prefix_sum(bucket, K); classify(a, aEnd, b, bucket, offset, shift); cleanup(b, bucket, K); } template void classify_block(Type* begin, Type* end, TypeKey*& out, int_type* bucket, typename KeyExtractor::key_type offset, unsigned shift, KeyExtractor keyobj) { assert(shift < (sizeof(typename KeyExtractor::key_type) * 8 + 1)); for (Type* p = begin; p < end; p++, out++) // count & create references { out->ptr = p; typename KeyExtractor::key_type key = keyobj(*p); int_type ibucket = (int_type)((key - offset) >> shift); out->key = key; bucket[ibucket]++; } } template void classify_block(Type* begin, Type* end, TypeKey*& out, int_type* bucket, typename Type::key_type offset, unsigned shift, const int_type K, KeyExtractor keyobj) { assert(shift < (sizeof(typename Type::key_type) * 8 + 1)); for (Type* p = begin; p < end; p++, out++) // count & create references { out->ptr = p; typename Type::key_type key = keyobj(*p); int_type ibucket = (key - offset) >> shift; /* if (!(ibucket < K && ibucket >= 0)) { STXXL_ERRMSG("ibucket: " << ibucket << " K:" << K); abort(); } */ out->key = key; bucket[ibucket]++; } STXXL_UNUSED(K); } STXXL_END_NAMESPACE #endif // !STXXL_ALGO_INTKSORT_HEADER stxxl-1.4.1/include/stxxl/bits/algo/random_shuffle.h000644 001411 000144 00000032055 12414452316 022341 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/random_shuffle.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Manuel Krings * Copyright (C) 2007 Markus Westphal * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_RANDOM_SHUFFLE_HEADER #define STXXL_ALGO_RANDOM_SHUFFLE_HEADER // TODO: improve main memory consumption in recursion // (free stacks buffers) // TODO: shuffle small input in internal memory #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup stlalgo //! \{ //! External equivalent of std::random_shuffle //! \param first begin of the range to shuffle //! \param last end of the range to shuffle //! \param rand random number generator object (functor) //! \param M number of bytes for internal use //! \param AS parallel disk allocation strategy //! //! - BlockSize size of the block to use for external memory data structures //! - PageSize page size in blocks to use for external memory data structures template void random_shuffle(ExtIterator first, ExtIterator last, RandomNumberGenerator& rand, unsigned_type M, AllocStrategy AS = STXXL_DEFAULT_ALLOC_STRATEGY()) { STXXL_UNUSED(AS); // FIXME: Why is this not being used? typedef typename ExtIterator::value_type value_type; typedef typename STACK_GENERATOR< value_type, external, grow_shrink2, PageSize, BlockSize, void, 0, AllocStrategy >::result stack_type; typedef typename stack_type::block_type block_type; STXXL_VERBOSE1("random_shuffle: Plain Version"); STXXL_STATIC_ASSERT(int(BlockSize) < 0 && "This implementation was never tested. Please report to the stxxl developers if you have an ExtIterator that works with this implementation."); int64 n = last - first; // the number of input elements // make sure we have at least 6 blocks + 1 page if (M < 6 * BlockSize + PageSize * BlockSize) { STXXL_ERRMSG("random_shuffle: insufficient memory, " << M << " bytes supplied,"); M = 6 * BlockSize + PageSize * BlockSize; STXXL_ERRMSG("random_shuffle: increasing to " << M << " bytes (6 blocks + 1 page)"); } int_type k = M / (3 * BlockSize); // number of buckets int64 i, j, size = 0; value_type* temp_array; typedef typename VECTOR_GENERATOR< value_type, PageSize, 4, BlockSize, AllocStrategy >::result temp_vector_type; temp_vector_type* temp_vector; STXXL_VERBOSE1("random_shuffle: " << M / BlockSize - k << " write buffers for " << k << " buckets"); read_write_pool pool(0, M / BlockSize - k); // no read buffers and M/B-k write buffers stack_type** buckets; // create and put buckets into container buckets = new stack_type*[k]; for (j = 0; j < k; j++) buckets[j] = new stack_type(pool, 0); ///// Reading input ///////////////////// typedef typename stream::streamify_traits::stream_type input_stream; input_stream in = stream::streamify(first, last); // distribute input into random buckets int_type random_bucket = 0; for (i = 0; i < n; ++i) { random_bucket = rand(k); buckets[random_bucket]->push(*in); // reading the current input element ++in; // go to the next input element } ///// Processing ////////////////////// // resize buffers pool.resize_write(0); pool.resize_prefetch(PageSize); unsigned_type space_left = M - k * BlockSize - PageSize * BlockSize; // remaining int space ExtIterator Writer = first; ExtIterator it = first; for (i = 0; i < k; i++) { STXXL_VERBOSE1("random_shuffle: bucket no " << i << " contains " << buckets[i]->size() << " elements"); } // shuffle each bucket for (i = 0; i < k; i++) { buckets[i]->set_prefetch_aggr(PageSize); size = buckets[i]->size(); // does the bucket fit into memory? if (size * sizeof(value_type) < space_left) { STXXL_VERBOSE1("random_shuffle: no recursion"); // copy bucket into temp. array temp_array = new value_type[size]; for (j = 0; j < size; j++) { temp_array[j] = buckets[i]->top(); buckets[i]->pop(); } // shuffle potentially_parallel:: random_shuffle(temp_array, temp_array + size, rand); // write back for (j = 0; j < size; j++) { *Writer = temp_array[j]; ++Writer; } // free memory delete[] temp_array; } else { STXXL_VERBOSE1("random_shuffle: recursion"); // copy bucket into temp. stxxl::vector temp_vector = new temp_vector_type(size); for (j = 0; j < size; j++) { (*temp_vector)[j] = buckets[i]->top(); buckets[i]->pop(); } pool.resize_prefetch(0); space_left += PageSize * BlockSize; STXXL_VERBOSE1("random_shuffle: Space left: " << space_left); // recursive shuffle stxxl::random_shuffle(temp_vector->begin(), temp_vector->end(), rand, space_left); pool.resize_prefetch(PageSize); // write back for (j = 0; j < size; j++) { *Writer = (*temp_vector)[j]; ++Writer; } // free memory delete temp_vector; } // free bucket delete buckets[i]; space_left += BlockSize; } delete[] buckets; } //! External equivalent of std::random_shuffle (specialization for stxxl::vector) //! \param first begin of the range to shuffle //! \param last end of the range to shuffle //! \param rand random number generator object (functor) //! \param M number of bytes for internal use template void random_shuffle( stxxl::vector_iterator first, stxxl::vector_iterator last, RandomNumberGenerator& rand, unsigned_type M) { typedef stxxl::vector_iterator ExtIterator; typedef typename ExtIterator::value_type value_type; typedef typename ExtIterator::bids_container_iterator bids_container_iterator; typedef typename stxxl::STACK_GENERATOR::result stack_type; typedef typename stack_type::block_type block_type; STXXL_VERBOSE1("random_shuffle: Vector Version"); // make sure we have at least 6 blocks + 1 page if (M < 6 * BlockSize + PageSize * BlockSize) { STXXL_ERRMSG("random_shuffle: insufficient memory, " << M << " bytes supplied,"); M = 6 * BlockSize + PageSize * BlockSize; STXXL_ERRMSG("random_shuffle: increasing to " << M << " bytes (6 blocks + 1 page)"); } stxxl::int64 n = last - first; // the number of input elements int_type k = M / (3 * BlockSize); // number of buckets stxxl::int64 i, j, size = 0; value_type* temp_array; typedef typename stxxl::VECTOR_GENERATOR< value_type, PageSize, 4, BlockSize, AllocStrategy >::result temp_vector_type; temp_vector_type* temp_vector; // no read buffers and M/B-k write buffers stxxl::read_write_pool pool(0, M / BlockSize - k); stack_type** buckets; // create and put buckets into container buckets = new stack_type*[k]; for (j = 0; j < k; j++) buckets[j] = new stack_type(pool, 0); typedef buf_istream buf_istream_type; typedef buf_ostream buf_ostream_type; first.flush(); // flush container // create prefetching stream, buf_istream_type in(first.bid(), last.bid() + ((last.block_offset()) ? 1 : 0), 2); // create buffered write stream for blocks buf_ostream_type out(first.bid(), 2); ExtIterator _cur = first - first.block_offset(); // leave part of the block before _begin untouched (e.g. copy) for ( ; _cur != first; ++_cur) { typename ExtIterator::value_type tmp; in >> tmp; out << tmp; } ///// Reading input ///////////////////// // distribute input into random buckets int_type random_bucket = 0; for (i = 0; i < n; ++i, ++_cur) { random_bucket = rand((unsigned)k); typename ExtIterator::value_type tmp; in >> tmp; buckets[random_bucket]->push(tmp); // reading the current input element } ///// Processing ////////////////////// // resize buffers pool.resize_write(0); pool.resize_prefetch(PageSize); // remaining int space unsigned_type space_left = M - k * BlockSize - PageSize * BlockSize; for (i = 0; i < k; i++) { STXXL_VERBOSE1("random_shuffle: bucket no " << i << " contains " << buckets[i]->size() << " elements"); } // shuffle each bucket for (i = 0; i < k; i++) { buckets[i]->set_prefetch_aggr(PageSize); size = buckets[i]->size(); // does the bucket fit into memory? if (size * sizeof(value_type) < space_left) { STXXL_VERBOSE1("random_shuffle: no recursion"); // copy bucket into temp. array temp_array = new value_type[(size_t)size]; for (j = 0; j < size; j++) { temp_array[j] = buckets[i]->top(); buckets[i]->pop(); } // shuffle potentially_parallel:: random_shuffle(temp_array, temp_array + size, rand); // write back for (j = 0; j < size; j++) { typename ExtIterator::value_type tmp; tmp = temp_array[j]; out << tmp; } // free memory delete[] temp_array; } else { STXXL_VERBOSE1("random_shuffle: recursion"); // copy bucket into temp. stxxl::vector temp_vector = new temp_vector_type(size); for (j = 0; j < size; j++) { (*temp_vector)[j] = buckets[i]->top(); buckets[i]->pop(); } pool.resize_prefetch(0); space_left += PageSize * BlockSize; STXXL_VERBOSE1("random_shuffle: Space left: " << space_left); // recursive shuffle stxxl::random_shuffle(temp_vector->begin(), temp_vector->end(), rand, space_left); pool.resize_prefetch(PageSize); // write back for (j = 0; j < size; j++) { typename ExtIterator::value_type tmp; tmp = (*temp_vector)[j]; out << tmp; } // free memory delete temp_vector; } // free bucket delete buckets[i]; space_left += BlockSize; } delete[] buckets; // leave part of the block after _end untouched if (last.block_offset()) { ExtIterator last_block_end = last + (block_type::size - last.block_offset()); for ( ; _cur != last_block_end; ++_cur) { typename ExtIterator::value_type tmp; in >> tmp; out << tmp; } } } //! External equivalent of std::random_shuffle (specialization for stxxl::vector) //! \param first begin of the range to shuffle //! \param last end of the range to shuffle //! \param M number of bytes for internal use template inline void random_shuffle( stxxl::vector_iterator first, stxxl::vector_iterator last, unsigned_type M) { stxxl::random_number<> rand; stxxl::random_shuffle(first, last, rand, M); } //! \} STXXL_END_NAMESPACE #endif // !STXXL_ALGO_RANDOM_SHUFFLE_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/run_cursor.h000644 001411 000144 00000006002 12414452316 021537 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/run_cursor.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_RUN_CURSOR_HEADER #define STXXL_ALGO_RUN_CURSOR_HEADER #include #include STXXL_BEGIN_NAMESPACE template struct run_cursor { unsigned_type pos; BlockType* buffer; run_cursor() : pos(0), buffer(NULL) { } inline typename BlockType::const_reference current() const { return (*buffer)[pos]; } inline void operator ++ () { ++pos; } }; #ifdef STXXL_SORT_SINGLE_PREFETCHER template struct have_prefetcher { static void* untyped_prefetcher; }; #endif template struct run_cursor2 : public run_cursor #ifdef STXXL_SORT_SINGLE_PREFETCHER , public have_prefetcher<> #endif { typedef BlockType block_type; typedef PrefetcherType prefetcher_type; typedef run_cursor2 _Self; typedef typename block_type::value_type value_type; using run_cursor::pos; using run_cursor::buffer; #ifdef STXXL_SORT_SINGLE_PREFETCHER static prefetcher_type* const prefetcher() // sorry, a hack { return reinterpret_cast(untyped_prefetcher); } static void set_prefetcher(prefetcher_type* pfptr) { untyped_prefetcher = pfptr; } run_cursor2() { } #else prefetcher_type* prefetcher_; prefetcher_type* & prefetcher() // sorry, a hack { return prefetcher_; } run_cursor2(prefetcher_type* p = NULL) : prefetcher_(p) { } #endif inline bool empty() const { return (pos >= block_type::size); } inline void operator ++ () { assert(!empty()); ++pos; if (UNLIKELY(pos >= block_type::size)) { if (prefetcher()->block_consumed(buffer)) pos = 0; } } inline void make_inf() { pos = block_type::size; } }; #ifdef STXXL_SORT_SINGLE_PREFETCHER template void* have_prefetcher::untyped_prefetcher = NULL; #endif #if 0 template struct run_cursor_cmp { typedef run_cursor cursor_type; inline bool operator () (const cursor_type& a, const cursor_type& b) // greater or equal { return !((*a.buffer)[a.pos] < (*b.buffer)[b.pos]); } }; #endif STXXL_END_NAMESPACE #endif // !STXXL_ALGO_RUN_CURSOR_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/sort.h000644 001411 000144 00000103204 12414452316 020327 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/sort.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2003 Roman Dementiev * Copyright (C) 2006 Johannes Singler * Copyright (C) 2008-2011 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_SORT_HEADER #define STXXL_ALGO_SORT_HEADER #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup stlalgo //! \{ /*! \internal */ namespace sort_local { template struct read_next_after_write_completed { typedef BlockType block_type; block_type* block; BidType bid; request_ptr* req; void operator () (request* /*completed_req*/) { * req = block->read(bid); } }; template < typename BlockType, typename RunType, typename InputBidIterator, typename ValueCmp> void create_runs( InputBidIterator it, RunType** runs, int_type nruns, int_type _m, ValueCmp cmp) { typedef BlockType block_type; typedef RunType run_type; typedef typename block_type::bid_type bid_type; STXXL_VERBOSE1("stxxl::create_runs nruns=" << nruns << " m=" << _m); int_type m2 = _m / 2; block_manager* bm = block_manager::get_instance(); block_type* Blocks1 = new block_type[m2]; block_type* Blocks2 = new block_type[m2]; bid_type* bids1 = new bid_type[m2]; bid_type* bids2 = new bid_type[m2]; request_ptr* read_reqs1 = new request_ptr[m2]; request_ptr* read_reqs2 = new request_ptr[m2]; request_ptr* write_reqs = new request_ptr[m2]; read_next_after_write_completed* next_run_reads = new read_next_after_write_completed[m2]; disk_queues::get_instance()->set_priority_op(request_queue::WRITE); int_type i; int_type run_size = 0; assert(nruns >= 2); run_size = runs[0]->size(); assert(run_size == m2); for (i = 0; i < run_size; ++i) { STXXL_VERBOSE1("stxxl::create_runs posting read " << Blocks1[i].elem); bids1[i] = *(it++); read_reqs1[i] = Blocks1[i].read(bids1[i]); } run_size = runs[1]->size(); for (i = 0; i < run_size; ++i) { STXXL_VERBOSE1("stxxl::create_runs posting read " << Blocks2[i].elem); bids2[i] = *(it++); read_reqs2[i] = Blocks2[i].read(bids2[i]); } for (int_type k = 0; k < nruns - 1; ++k) { run_type* run = runs[k]; run_size = run->size(); assert(run_size == m2); int_type next_run_size = runs[k + 1]->size(); STXXL_ASSERT((next_run_size == m2) || (next_run_size <= m2 && k == nruns - 2)); STXXL_VERBOSE1("stxxl::create_runs start waiting read_reqs1"); wait_all(read_reqs1, run_size); STXXL_VERBOSE1("stxxl::create_runs finish waiting read_reqs1"); for (i = 0; i < run_size; ++i) bm->delete_block(bids1[i]); check_sort_settings(); potentially_parallel:: sort(make_element_iterator(Blocks1, 0), make_element_iterator(Blocks1, run_size * block_type::size), cmp); STXXL_VERBOSE1("stxxl::create_runs start waiting write_reqs"); if (k > 0) wait_all(write_reqs, m2); STXXL_VERBOSE1("stxxl::create_runs finish waiting write_reqs"); int_type runplus2size = (k < nruns - 2) ? runs[k + 2]->size() : 0; for (i = 0; i < m2; ++i) { STXXL_VERBOSE1("stxxl::create_runs posting write " << Blocks1[i].elem); (*run)[i].value = Blocks1[i][0]; if (i >= runplus2size) { write_reqs[i] = Blocks1[i].write((*run)[i].bid); } else { next_run_reads[i].block = Blocks1 + i; next_run_reads[i].req = read_reqs1 + i; bids1[i] = next_run_reads[i].bid = *(it++); write_reqs[i] = Blocks1[i].write((*run)[i].bid, next_run_reads[i]); } } std::swap(Blocks1, Blocks2); std::swap(bids1, bids2); std::swap(read_reqs1, read_reqs2); } run_type* run = runs[nruns - 1]; run_size = run->size(); STXXL_VERBOSE1("stxxl::create_runs start waiting read_reqs1"); wait_all(read_reqs1, run_size); STXXL_VERBOSE1("stxxl::create_runs finish waiting read_reqs1"); for (i = 0; i < run_size; ++i) bm->delete_block(bids1[i]); check_sort_settings(); potentially_parallel:: sort(make_element_iterator(Blocks1, 0), make_element_iterator(Blocks1, run_size * block_type::size), cmp); STXXL_VERBOSE1("stxxl::create_runs start waiting write_reqs"); wait_all(write_reqs, m2); STXXL_VERBOSE1("stxxl::create_runs finish waiting write_reqs"); for (i = 0; i < run_size; ++i) { STXXL_VERBOSE1("stxxl::create_runs posting write " << Blocks1[i].elem); (*run)[i].value = Blocks1[i][0]; write_reqs[i] = Blocks1[i].write((*run)[i].bid); } STXXL_VERBOSE1("stxxl::create_runs start waiting write_reqs"); wait_all(write_reqs, run_size); STXXL_VERBOSE1("stxxl::create_runs finish waiting write_reqs"); delete[] Blocks1; delete[] Blocks2; delete[] bids1; delete[] bids2; delete[] read_reqs1; delete[] read_reqs2; delete[] write_reqs; delete[] next_run_reads; } template bool check_sorted_runs(RunType** runs, unsigned_type nruns, unsigned_type m, ValueCmp cmp) { typedef BlockType block_type; typedef typename block_type::value_type value_type; STXXL_MSG("check_sorted_runs Runs: " << nruns); unsigned_type irun = 0; for (irun = 0; irun < nruns; ++irun) { const unsigned_type nblocks_per_run = runs[irun]->size(); unsigned_type blocks_left = nblocks_per_run; block_type* blocks = new block_type[m]; request_ptr* reqs = new request_ptr[m]; value_type last = cmp.min_value(); for (unsigned_type off = 0; off < nblocks_per_run; off += m) { const unsigned_type nblocks = STXXL_MIN(blocks_left, m); const unsigned_type nelements = nblocks * block_type::size; blocks_left -= nblocks; for (unsigned_type j = 0; j < nblocks; ++j) { reqs[j] = blocks[j].read((*runs[irun])[j + off].bid); } wait_all(reqs, reqs + nblocks); if (off && cmp(blocks[0][0], last)) { STXXL_MSG("check_sorted_runs wrong first value in the run " << irun); STXXL_MSG(" first value: " << blocks[0][0]); STXXL_MSG(" last value: " << last); for (unsigned_type k = 0; k < block_type::size; ++k) STXXL_MSG("Element " << k << " in the block is :" << blocks[0][k]); delete[] reqs; delete[] blocks; return false; } for (unsigned_type j = 0; j < nblocks; ++j) { if (!(blocks[j][0] == (*runs[irun])[j + off].value)) { STXXL_MSG("check_sorted_runs wrong trigger in the run " << irun << " block " << (j + off)); STXXL_MSG(" trigger value: " << (*runs[irun])[j + off].value); STXXL_MSG("Data in the block:"); for (unsigned_type k = 0; k < block_type::size; ++k) STXXL_MSG("Element " << k << " in the block is :" << blocks[j][k]); STXXL_MSG("BIDS:"); for (unsigned_type k = 0; k < nblocks; ++k) { if (k == j) STXXL_MSG("Bad one comes next."); STXXL_MSG("BID " << (k + off) << " is: " << ((*runs[irun])[k + off].bid)); } delete[] reqs; delete[] blocks; return false; } } if (!stxxl::is_sorted(make_element_iterator(blocks, 0), make_element_iterator(blocks, nelements), cmp)) { STXXL_MSG("check_sorted_runs wrong order in the run " << irun); STXXL_MSG("Data in blocks:"); for (unsigned_type j = 0; j < nblocks; ++j) { for (unsigned_type k = 0; k < block_type::size; ++k) STXXL_MSG(" Element " << k << " in block " << (j + off) << " is :" << blocks[j][k]); } STXXL_MSG("BIDS:"); for (unsigned_type k = 0; k < nblocks; ++k) { STXXL_MSG("BID " << (k + off) << " is: " << ((*runs[irun])[k + off].bid)); } delete[] reqs; delete[] blocks; return false; } last = blocks[nblocks - 1][block_type::size - 1]; } assert(blocks_left == 0); delete[] reqs; delete[] blocks; } return true; } template void merge_runs(RunType** in_runs, int_type nruns, RunType* out_run, unsigned_type _m, ValueCmp cmp) { typedef BlockType block_type; typedef RunType run_type; typedef ValueCmp value_cmp; typedef typename run_type::value_type trigger_entry_type; typedef block_prefetcher prefetcher_type; typedef run_cursor2 run_cursor_type; typedef sort_helper::run_cursor2_cmp run_cursor2_cmp_type; run_type consume_seq(out_run->size()); int_type* prefetch_seq = new int_type[out_run->size()]; typename run_type::iterator copy_start = consume_seq.begin(); for (int_type i = 0; i < nruns; i++) { // \todo: try to avoid copy copy_start = std::copy( in_runs[i]->begin(), in_runs[i]->end(), copy_start); } std::stable_sort(consume_seq.begin(), consume_seq.end(), sort_helper::trigger_entry_cmp(cmp) _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL); int_type disks_number = config::get_instance()->disks_number(); #ifdef PLAY_WITH_OPT_PREF const int_type n_write_buffers = 4 * disks_number; #else const int_type n_prefetch_buffers = STXXL_MAX(2 * disks_number, (3 * (int_type(_m) - nruns) / 4)); const int_type n_write_buffers = STXXL_MAX(2 * disks_number, int_type(_m) - nruns - n_prefetch_buffers); #if STXXL_SORT_OPTIMAL_PREFETCHING // heuristic const int_type n_opt_prefetch_buffers = 2 * disks_number + (3 * (n_prefetch_buffers - 2 * disks_number)) / 10; #endif #endif #if STXXL_SORT_OPTIMAL_PREFETCHING compute_prefetch_schedule( consume_seq, prefetch_seq, n_opt_prefetch_buffers, config::get_instance()->get_max_device_id()); #else for (unsigned_type i = 0; i < out_run->size(); i++) prefetch_seq[i] = i; #endif prefetcher_type prefetcher(consume_seq.begin(), consume_seq.end(), prefetch_seq, nruns + n_prefetch_buffers); buffered_writer writer(n_write_buffers, n_write_buffers / 2); int_type out_run_size = out_run->size(); block_type* out_buffer = writer.get_free_block(); //If parallelism is activated, one can still fall back to the //native merge routine by setting stxxl::SETTINGS::native_merge= true, //otherwise, it is used anyway. if (do_parallel_merge()) { #if STXXL_PARALLEL_MULTIWAY_MERGE // begin of STL-style merging typedef stxxl::int64 diff_type; typedef std::pair sequence; std::vector seqs(nruns); std::vector buffers(nruns); for (int_type i = 0; i < nruns; i++) // initialize sequences { buffers[i] = prefetcher.pull_block(); // get first block of each run seqs[i] = std::make_pair(buffers[i]->begin(), buffers[i]->end()); // this memory location stays the same, only the data is exchanged } #if STXXL_CHECK_ORDER_IN_SORTS value_type last_elem = cmp.min_value(); #endif diff_type num_currently_mergeable = 0; for (int_type j = 0; j < out_run_size; ++j) // for the whole output run, out_run_size is in blocks { diff_type rest = block_type::size; // elements still to merge for this output block STXXL_VERBOSE1("output block " << j); do { if (num_currently_mergeable < rest) { if (prefetcher.empty()) { // anything remaining is already in memory num_currently_mergeable = (out_run_size - j) * block_type::size - (block_type::size - rest); } else { num_currently_mergeable = sort_helper::count_elements_less_equal( seqs, consume_seq[prefetcher.pos()].value, cmp); } } diff_type output_size = STXXL_MIN(num_currently_mergeable, rest); // at most rest elements STXXL_VERBOSE1("before merge " << output_size); stxxl::parallel::multiway_merge(seqs.begin(), seqs.end(), out_buffer->end() - rest, cmp, output_size); // sequence iterators are progressed appropriately rest -= output_size; num_currently_mergeable -= output_size; STXXL_VERBOSE1("after merge"); sort_helper::refill_or_remove_empty_sequences(seqs, buffers, prefetcher); } while (rest > 0 && seqs.size() > 0); #if STXXL_CHECK_ORDER_IN_SORTS if (!stxxl::is_sorted(out_buffer->begin(), out_buffer->end(), cmp)) { for (value_type* i = out_buffer->begin() + 1; i != out_buffer->end(); i++) if (cmp(*i, *(i - 1))) { STXXL_VERBOSE1("Error at position " << (i - out_buffer->begin())); } assert(false); } if (j > 0) // do not check in first iteration assert(cmp((*out_buffer)[0], last_elem) == false); last_elem = (*out_buffer)[block_type::size - 1]; #endif (*out_run)[j].value = (*out_buffer)[0]; // save smallest value out_buffer = writer.write(out_buffer, (*out_run)[j].bid); } // end of STL-style merging #else STXXL_THROW_UNREACHABLE(); #endif } else { // begin of native merging procedure loser_tree losers(&prefetcher, nruns, run_cursor2_cmp_type(cmp)); #if STXXL_CHECK_ORDER_IN_SORTS value_type last_elem = cmp.min_value(); #endif for (int_type i = 0; i < out_run_size; ++i) { losers.multi_merge(out_buffer->elem, out_buffer->elem + block_type::size); (*out_run)[i].value = *(out_buffer->elem); #if STXXL_CHECK_ORDER_IN_SORTS assert(stxxl::is_sorted( out_buffer->begin(), out_buffer->end(), cmp)); if (i) assert(cmp(*(out_buffer->elem), last_elem) == false); last_elem = (*out_buffer).elem[block_type::size - 1]; #endif out_buffer = writer.write(out_buffer, (*out_run)[i].bid); } // end of native merging procedure } delete[] prefetch_seq; block_manager* bm = block_manager::get_instance(); for (int_type i = 0; i < nruns; ++i) { unsigned_type sz = in_runs[i]->size(); for (unsigned_type j = 0; j < sz; ++j) bm->delete_block((*in_runs[i])[j].bid); delete in_runs[i]; } } template simple_vector >* sort_blocks(InputBidIterator input_bids, unsigned_type _n, unsigned_type _m, ValueCmp cmp) { typedef BlockType block_type; typedef AllocStrategy alloc_strategy; typedef InputBidIterator input_bid_iterator; typedef ValueCmp value_cmp; typedef typename block_type::bid_type bid_type; typedef sort_helper::trigger_entry trigger_entry_type; typedef simple_vector run_type; typedef typename interleaved_alloc_traits::strategy interleaved_alloc_strategy; unsigned_type m2 = _m / 2; unsigned_type full_runs = _n / m2; unsigned_type partial_runs = ((_n % m2) ? 1 : 0); unsigned_type nruns = full_runs + partial_runs; unsigned_type i; block_manager* mng = block_manager::get_instance(); //STXXL_VERBOSE ("n=" << _n << " nruns=" << nruns << "=" << full_runs << "+" << partial_runs); double begin = timestamp(), after_runs_creation, end; run_type** runs = new run_type*[nruns]; for (i = 0; i < full_runs; i++) runs[i] = new run_type(m2); if (partial_runs) runs[i] = new run_type(_n - full_runs * m2); for (i = 0; i < nruns; ++i) mng->new_blocks(alloc_strategy(), make_bid_iterator(runs[i]->begin()), make_bid_iterator(runs[i]->end())); sort_local::create_runs(input_bids, runs, nruns, _m, cmp); after_runs_creation = timestamp(); double io_wait_after_rf = stats::get_instance()->get_io_wait_time(); disk_queues::get_instance()->set_priority_op(request_queue::WRITE); const int_type merge_factor = optimal_merge_factor(nruns, _m); run_type** new_runs; while (nruns > 1) { int_type new_nruns = div_ceil(nruns, merge_factor); STXXL_VERBOSE("Starting new merge phase: nruns: " << nruns << " opt_merge_factor: " << merge_factor << " m:" << _m << " new_nruns: " << new_nruns); new_runs = new run_type*[new_nruns]; int_type runs_left = nruns; int_type cur_out_run = 0; int_type blocks_in_new_run = 0; while (runs_left > 0) { int_type runs2merge = STXXL_MIN(runs_left, merge_factor); blocks_in_new_run = 0; for (unsigned_type i = nruns - runs_left; i < (nruns - runs_left + runs2merge); i++) blocks_in_new_run += runs[i]->size(); // allocate run new_runs[cur_out_run++] = new run_type(blocks_in_new_run); runs_left -= runs2merge; } // allocate blocks for the new runs if (cur_out_run == 1 && blocks_in_new_run == int_type(_n) && !input_bids->is_managed()) { // if we sort a file we can reuse the input bids for the output input_bid_iterator cur = input_bids; for (int_type i = 0; cur != (input_bids + _n); ++cur) { (*new_runs[0])[i++].bid = *cur; } bid_type& firstBID = (*new_runs[0])[0].bid; if (firstBID.is_managed()) { // the first block does not belong to the file // need to reallocate it mng->new_block(FR(), firstBID); } bid_type& lastBID = (*new_runs[0])[_n - 1].bid; if (lastBID.is_managed()) { // the first block does not belong to the file // need to reallocate it mng->new_block(FR(), lastBID); } } else { mng->new_blocks(interleaved_alloc_strategy(new_nruns, alloc_strategy()), runs2bid_array_adaptor2(new_runs, 0, new_nruns, blocks_in_new_run), runs2bid_array_adaptor2(new_runs, _n, new_nruns, blocks_in_new_run)); } // merge all runs_left = nruns; cur_out_run = 0; while (runs_left > 0) { int_type runs2merge = STXXL_MIN(runs_left, merge_factor); #if STXXL_CHECK_ORDER_IN_SORTS assert((check_sorted_runs(runs + nruns - runs_left, runs2merge, m2, cmp))); #endif STXXL_VERBOSE("Merging " << runs2merge << " runs"); merge_runs(runs + nruns - runs_left, runs2merge, *(new_runs + (cur_out_run++)), _m, cmp ); runs_left -= runs2merge; } nruns = new_nruns; delete[] runs; runs = new_runs; } run_type* result = *runs; delete[] runs; end = timestamp(); STXXL_VERBOSE("Elapsed time : " << end - begin << " s. Run creation time: " << after_runs_creation - begin << " s"); STXXL_VERBOSE("Time in I/O wait(rf): " << io_wait_after_rf << " s"); STXXL_VERBOSE(*stats::get_instance()); return result; } } // namespace sort_local /*! * Sort records comparison-based, see \ref design_algo_sort. * * stxxl::sort sorts the elements in [first, last) into ascending order, * meaning that if \c i and \c j are any two valid iterators in [first, last) * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as * std::sort, stxxl::sort is not guaranteed to be stable. That is, suppose that * \c *i and \c *j are equivalent: neither one is less than the other. It is * not guaranteed that the relative order of these two elements will be * preserved by stxxl::sort. * * The order is defined by the \c cmp parameter. The sorter's internal memory * consumption is bounded by \a M bytes. * * \param first object of model of \c ext_random_access_iterator concept * \param last object of model of \c ext_random_access_iterator concept * \param cmp comparison object of \ref StrictWeakOrdering * \param M amount of memory for internal use (in bytes) */ template void sort(ExtIterator first, ExtIterator last, StrictWeakOrdering cmp, unsigned_type M) { sort_helper::verify_sentinel_strict_weak_ordering(cmp); typedef typename ExtIterator::vector_type::value_type value_type; typedef typename ExtIterator::block_type block_type; typedef typename ExtIterator::bid_type bid_type; typedef typename ExtIterator::vector_type::alloc_strategy_type alloc_strategy_type; typedef typename ExtIterator::bids_container_iterator bids_container_iterator; typedef simple_vector > run_type; unsigned_type n = 0; block_manager* mng = block_manager::get_instance(); first.flush(); if ((last - first) * sizeof(value_type) * sort_memory_usage_factor() < M) { stl_in_memory_sort(first, last, cmp); } else { if (!(2 * block_type::raw_size * (unsigned_type)sort_memory_usage_factor() <= M)) { throw bad_parameter("stxxl::sort(): INSUFFICIENT MEMORY provided, please increase parameter 'M'"); } if (first.block_offset()) { if (last.block_offset()) // first and last element are // not the first elements of their block { block_type* first_block = new block_type; block_type* last_block = new block_type; typename ExtIterator::bid_type first_bid, last_bid; request_ptr req; req = first_block->read(*first.bid()); mng->new_block(FR(), first_bid); // try to overlap mng->new_block(FR(), last_bid); req->wait(); req = last_block->read(*last.bid()); unsigned_type i = 0; for ( ; i < first.block_offset(); ++i) { first_block->elem[i] = cmp.min_value(); } req->wait(); req = first_block->write(first_bid); for (i = last.block_offset(); i < block_type::size; ++i) { last_block->elem[i] = cmp.max_value(); } req->wait(); req = last_block->write(last_bid); n = last.bid() - first.bid() + 1; std::swap(first_bid, *first.bid()); std::swap(last_bid, *last.bid()); req->wait(); delete first_block; delete last_block; run_type* out = sort_local::sort_blocks< block_type, alloc_strategy_type, bids_container_iterator >(first.bid(), n, M / sort_memory_usage_factor() / block_type::raw_size, cmp); first_block = new block_type; last_block = new block_type; block_type* sorted_first_block = new block_type; block_type* sorted_last_block = new block_type; request_ptr* reqs = new request_ptr[2]; reqs[0] = first_block->read(first_bid); reqs[1] = sorted_first_block->read((*(out->begin())).bid); reqs[0]->wait(); reqs[1]->wait(); reqs[0] = last_block->read(last_bid); reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); for (i = first.block_offset(); i < block_type::size; i++) { first_block->elem[i] = sorted_first_block->elem[i]; } reqs[0]->wait(); reqs[1]->wait(); req = first_block->write(first_bid); for (i = 0; i < last.block_offset(); ++i) { last_block->elem[i] = sorted_last_block->elem[i]; } req->wait(); req = last_block->write(last_bid); mng->delete_block(out->begin()->bid); mng->delete_block((*out)[out->size() - 1].bid); *first.bid() = first_bid; *last.bid() = last_bid; typename run_type::iterator it = out->begin(); ++it; bids_container_iterator cur_bid = first.bid(); ++cur_bid; for ( ; cur_bid != last.bid(); ++cur_bid, ++it) { *cur_bid = (*it).bid; } delete first_block; delete sorted_first_block; delete sorted_last_block; delete[] reqs; delete out; req->wait(); delete last_block; } else { // first element is // not the first element of its block block_type* first_block = new block_type; bid_type first_bid; request_ptr req; req = first_block->read(*first.bid()); mng->new_block(FR(), first_bid); // try to overlap req->wait(); unsigned_type i = 0; for ( ; i < first.block_offset(); ++i) { first_block->elem[i] = cmp.min_value(); } req = first_block->write(first_bid); n = last.bid() - first.bid(); std::swap(first_bid, *first.bid()); req->wait(); delete first_block; run_type* out = sort_local::sort_blocks< block_type, alloc_strategy_type, bids_container_iterator >(first.bid(), n, M / sort_memory_usage_factor() / block_type::raw_size, cmp); first_block = new block_type; block_type* sorted_first_block = new block_type; request_ptr* reqs = new request_ptr[2]; reqs[0] = first_block->read(first_bid); reqs[1] = sorted_first_block->read((*(out->begin())).bid); reqs[0]->wait(); reqs[1]->wait(); for (i = first.block_offset(); i < block_type::size; ++i) { first_block->elem[i] = sorted_first_block->elem[i]; } req = first_block->write(first_bid); mng->delete_block(out->begin()->bid); *first.bid() = first_bid; typename run_type::iterator it = out->begin(); ++it; bids_container_iterator cur_bid = first.bid(); ++cur_bid; for ( ; cur_bid != last.bid(); ++cur_bid, ++it) { *cur_bid = (*it).bid; } *cur_bid = (*it).bid; delete sorted_first_block; delete[] reqs; delete out; req->wait(); delete first_block; } } else { if (last.block_offset()) // last is // not the first element of its block { block_type* last_block = new block_type; bid_type last_bid; request_ptr req; unsigned_type i; req = last_block->read(*last.bid()); mng->new_block(FR(), last_bid); req->wait(); for (i = last.block_offset(); i < block_type::size; ++i) { last_block->elem[i] = cmp.max_value(); } req = last_block->write(last_bid); n = last.bid() - first.bid() + 1; std::swap(last_bid, *last.bid()); req->wait(); delete last_block; run_type* out = sort_local::sort_blocks< block_type, alloc_strategy_type, bids_container_iterator >(first.bid(), n, M / sort_memory_usage_factor() / block_type::raw_size, cmp); last_block = new block_type; block_type* sorted_last_block = new block_type; request_ptr* reqs = new request_ptr[2]; reqs[0] = last_block->read(last_bid); reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); reqs[0]->wait(); reqs[1]->wait(); for (i = 0; i < last.block_offset(); ++i) { last_block->elem[i] = sorted_last_block->elem[i]; } req = last_block->write(last_bid); mng->delete_block((*out)[out->size() - 1].bid); *last.bid() = last_bid; typename run_type::iterator it = out->begin(); bids_container_iterator cur_bid = first.bid(); for ( ; cur_bid != last.bid(); ++cur_bid, ++it) { *cur_bid = (*it).bid; } delete sorted_last_block; delete[] reqs; delete out; req->wait(); delete last_block; } else { // first and last element are first elements of their of blocks n = last.bid() - first.bid(); run_type* out = sort_local::sort_blocks< block_type, alloc_strategy_type, bids_container_iterator >(first.bid(), n, M / sort_memory_usage_factor() / block_type::raw_size, cmp); typename run_type::iterator it = out->begin(); bids_container_iterator cur_bid = first.bid(); for ( ; cur_bid != last.bid(); ++cur_bid, ++it) { *cur_bid = (*it).bid; } delete out; } } } #if STXXL_CHECK_ORDER_IN_SORTS typedef typename ExtIterator::const_iterator const_iterator; assert(stxxl::is_sorted(const_iterator(first), const_iterator(last), cmp)); #endif } //! \} STXXL_END_NAMESPACE #endif // !STXXL_ALGO_SORT_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/stable_ksort.h000644 001411 000144 00000044255 12414452316 022046 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/stable_ksort.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_STABLE_KSORT_HEADER #define STXXL_ALGO_STABLE_KSORT_HEADER // it is a first try: distribution sort without sampling // I rework the stable_ksort when I would have a time #include #include #include #include #include #include #include #ifndef STXXL_VERBOSE_STABLE_KSORT #define STXXL_VERBOSE_STABLE_KSORT STXXL_VERBOSE1 #endif STXXL_BEGIN_NAMESPACE //! \addtogroup stlalgo //! \{ /*! \internal */ namespace stable_ksort_local { template void classify_block(Type* begin, Type* end, TypeKey*& out, int_type* bucket, typename Type::key_type offset, unsigned shift) { for (Type* p = begin; p < end; p++, out++) // count & create references { out->ptr = p; typename Type::key_type key = p->key(); int_type ibucket = (int_type)((key - offset) >> shift); out->key = key; bucket[ibucket]++; } } template struct type_key { typedef typename Type::key_type key_type; key_type key; Type* ptr; type_key() { } type_key(key_type k, Type* p) : key(k), ptr(p) { } }; template bool operator < (const type_key& a, const type_key& b) { return a.key < b.key; } template bool operator > (const type_key& a, const type_key& b) { return a.key > b.key; } template class bid_sequence { public: typedef BIDType bid_type; typedef bid_type& reference; typedef AllocStrategy alloc_strategy; typedef typename simple_vector::size_type size_type; typedef typename simple_vector::iterator iterator; protected: simple_vector* bids; alloc_strategy alloc_strategy_; public: bid_sequence() : bids(NULL) { } bid_sequence(size_type size_) { bids = new simple_vector(size_); block_manager* mng = block_manager::get_instance(); mng->new_blocks(alloc_strategy_, bids->begin(), bids->end()); } void init(size_type size_) { bids = new simple_vector(size_); block_manager* mng = block_manager::get_instance(); mng->new_blocks(alloc_strategy_, bids->begin(), bids->end()); } reference operator [] (size_type i) { size_type size_ = size(); // cache size in a register if (i < size_) return *(bids->begin() + i); block_manager* mng = block_manager::get_instance(); simple_vector* larger_bids = new simple_vector((i + 1) * 2); std::copy(bids->begin(), bids->end(), larger_bids->begin()); mng->new_blocks(alloc_strategy_, larger_bids->begin() + size_, larger_bids->end()); delete bids; bids = larger_bids; return *(larger_bids->begin() + i); } size_type size() { return bids->size(); } iterator begin() { return bids->begin(); } ~bid_sequence() { block_manager::get_instance()->delete_blocks(bids->begin(), bids->end()); delete bids; } }; template void distribute( bid_sequence* bucket_bids, int64* bucket_sizes, const int_type nbuckets, const int_type lognbuckets, ExtIterator first, ExtIterator last, const int_type nread_buffers, const int_type nwrite_buffers) { typedef typename ExtIterator::vector_type::value_type value_type; typedef typename value_type::key_type key_type; typedef typename ExtIterator::block_type block_type; typedef typename ExtIterator::bids_container_iterator bids_container_iterator; typedef buf_istream buf_istream_type; int_type i = 0; buf_istream_type in(first.bid(), last.bid() + ((first.block_offset()) ? 1 : 0), nread_buffers); buffered_writer out( nbuckets + nwrite_buffers, nwrite_buffers); unsigned_type* bucket_block_offsets = new unsigned_type[nbuckets]; unsigned_type* bucket_iblock = new unsigned_type[nbuckets]; block_type** bucket_blocks = new block_type*[nbuckets]; std::fill(bucket_sizes, bucket_sizes + nbuckets, 0); std::fill(bucket_iblock, bucket_iblock + nbuckets, 0); std::fill(bucket_block_offsets, bucket_block_offsets + nbuckets, 0); for (i = 0; i < nbuckets; i++) bucket_blocks[i] = out.get_free_block(); ExtIterator cur = first - first.block_offset(); // skip part of the block before first untouched for ( ; cur != first; cur++) ++in; const int_type shift = sizeof(key_type) * 8 - lognbuckets; // search in the the range [_begin,_end) STXXL_VERBOSE_STABLE_KSORT("Shift by: " << shift << " bits, lognbuckets: " << lognbuckets); for ( ; cur != last; cur++) { key_type cur_key = in.current().key(); int_type ibucket = (int_type)(cur_key >> shift); int_type block_offset = bucket_block_offsets[ibucket]; in >> (bucket_blocks[ibucket]->elem[block_offset++]); if (block_offset == block_type::size) { block_offset = 0; int_type iblock = bucket_iblock[ibucket]++; bucket_blocks[ibucket] = out.write(bucket_blocks[ibucket], bucket_bids[ibucket][iblock]); } bucket_block_offsets[ibucket] = block_offset; } for (i = 0; i < nbuckets; i++) { if (bucket_block_offsets[i]) { out.write(bucket_blocks[i], bucket_bids[i][bucket_iblock[i]]); } bucket_sizes[i] = int64(block_type::size) * bucket_iblock[i] + bucket_block_offsets[i]; STXXL_VERBOSE_STABLE_KSORT("Bucket " << i << " has size " << bucket_sizes[i] << ", estimated size: " << ((last - first) / int64(nbuckets))); } delete[] bucket_blocks; delete[] bucket_block_offsets; delete[] bucket_iblock; } } // namespace stable_ksort_local //! Sort records with integer keys //! \param first object of model of \c ext_random_access_iterator concept //! \param last object of model of \c ext_random_access_iterator concept //! \param M amount of memory for internal use (in bytes) //! \remark Elements must provide a method key() which returns the integer key. //! \remark Not yet fully implemented, it assumes that the keys are uniformly //! distributed between [0,std::numeric_limits::max(). template void stable_ksort(ExtIterator first, ExtIterator last, unsigned_type M) { STXXL_MSG("Warning: stable_ksort is not yet fully implemented, it assumes that the keys are uniformly distributed between [0,std::numeric_limits::max()]"); typedef typename ExtIterator::vector_type::value_type value_type; typedef typename value_type::key_type key_type; typedef typename ExtIterator::block_type block_type; typedef typename ExtIterator::bids_container_iterator bids_container_iterator; typedef typename block_type::bid_type bid_type; typedef typename ExtIterator::vector_type::alloc_strategy_type alloc_strategy; typedef stable_ksort_local::bid_sequence bucket_bids_type; typedef stable_ksort_local::type_key type_key_; first.flush(); // flush container double begin = timestamp(); unsigned_type i = 0; config* cfg = config::get_instance(); const unsigned_type m = M / block_type::raw_size; assert(2 * block_type::raw_size <= M); const unsigned_type write_buffers_multiple = 2; const unsigned_type read_buffers_multiple = 2; const unsigned_type ndisks = cfg->disks_number(); const unsigned_type min_num_read_write_buffers = (write_buffers_multiple + read_buffers_multiple) * ndisks; const unsigned_type nmaxbuckets = m - min_num_read_write_buffers; const unsigned int lognbuckets = ilog2_floor(nmaxbuckets); const unsigned_type nbuckets = unsigned_type(1) << lognbuckets; const unsigned_type est_bucket_size = (unsigned_type)div_ceil((last - first) / nbuckets, block_type::size); //in blocks if (m < min_num_read_write_buffers + 2 || nbuckets < 2) { STXXL_ERRMSG("stxxl::stable_ksort: Not enough memory. Blocks available: " << m << ", required for r/w buffers: " << min_num_read_write_buffers << ", required for buckets: 2, nbuckets: " << nbuckets); throw bad_parameter("stxxl::stable_ksort(): INSUFFICIENT MEMORY provided, please increase parameter 'M'"); } STXXL_VERBOSE_STABLE_KSORT("Elements to sort: " << (last - first)); STXXL_VERBOSE_STABLE_KSORT("Number of buckets has to be reduced from " << nmaxbuckets << " to " << nbuckets); const unsigned_type nread_buffers = (m - nbuckets) * read_buffers_multiple / (read_buffers_multiple + write_buffers_multiple); const unsigned_type nwrite_buffers = (m - nbuckets) * write_buffers_multiple / (read_buffers_multiple + write_buffers_multiple); STXXL_VERBOSE_STABLE_KSORT("Read buffers in distribution phase: " << nread_buffers); STXXL_VERBOSE_STABLE_KSORT("Write buffers in distribution phase: " << nwrite_buffers); bucket_bids_type* bucket_bids = new bucket_bids_type[nbuckets]; for (i = 0; i < nbuckets; ++i) bucket_bids[i].init(est_bucket_size); int64* bucket_sizes = new int64[nbuckets]; disk_queues::get_instance()->set_priority_op(request_queue::WRITE); stable_ksort_local::distribute( bucket_bids, bucket_sizes, nbuckets, lognbuckets, first, last, nread_buffers, nwrite_buffers); double dist_end = timestamp(), end; double io_wait_after_d = stats::get_instance()->get_io_wait_time(); { // sort buckets unsigned_type write_buffers_multiple_bs = 2; unsigned_type max_bucket_size_bl = (m - write_buffers_multiple_bs * ndisks) / 2; // in number of blocks int64 max_bucket_size_rec = int64(max_bucket_size_bl) * block_type::size; // in number of records int64 max_bucket_size_act = 0; // actual max bucket size // establish output stream for (i = 0; i < nbuckets; i++) { max_bucket_size_act = STXXL_MAX(bucket_sizes[i], max_bucket_size_act); if (bucket_sizes[i] > max_bucket_size_rec) { STXXL_ERRMSG("Bucket " << i << " is too large: " << bucket_sizes[i] << " records, maximum: " << max_bucket_size_rec); STXXL_ERRMSG("Recursion on buckets is not yet implemented, aborting."); abort(); } } // here we can increase write_buffers_multiple_b knowing max(bucket_sizes[i]) // ... and decrease max_bucket_size_bl const int_type max_bucket_size_act_bl = (int_type)div_ceil(max_bucket_size_act, block_type::size); STXXL_VERBOSE_STABLE_KSORT("Reducing required number of required blocks per bucket from " << max_bucket_size_bl << " to " << max_bucket_size_act_bl); max_bucket_size_rec = max_bucket_size_act; max_bucket_size_bl = max_bucket_size_act_bl; const unsigned_type nwrite_buffers_bs = m - 2 * max_bucket_size_bl; STXXL_VERBOSE_STABLE_KSORT("Write buffers in bucket sorting phase: " << nwrite_buffers_bs); typedef buf_ostream buf_ostream_type; buf_ostream_type out(first.bid(), nwrite_buffers_bs); disk_queues::get_instance()->set_priority_op(request_queue::READ); if (first.block_offset()) { // has to skip part of the first block block_type* block = new block_type; request_ptr req; req = block->read(*first.bid()); req->wait(); for (i = 0; i < first.block_offset(); i++) { out << block->elem[i]; } delete block; } block_type* blocks1 = new block_type[max_bucket_size_bl]; block_type* blocks2 = new block_type[max_bucket_size_bl]; request_ptr* reqs1 = new request_ptr[max_bucket_size_bl]; request_ptr* reqs2 = new request_ptr[max_bucket_size_bl]; type_key_* refs1 = new type_key_[(size_t)max_bucket_size_rec]; type_key_* refs2 = new type_key_[(size_t)max_bucket_size_rec]; // submit reading first 2 buckets (Peter's scheme) unsigned_type nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[0], block_type::size); for (i = 0; i < nbucket_blocks; i++) reqs1[i] = blocks1[i].read(bucket_bids[0][i]); nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[1], block_type::size); for (i = 0; i < nbucket_blocks; i++) reqs2[i] = blocks2[i].read(bucket_bids[1][i]); key_type offset = 0; const unsigned log_k1 = STXXL_MAX(ilog2_ceil(max_bucket_size_rec * sizeof(type_key_) / STXXL_L2_SIZE), 1); unsigned_type k1 = unsigned_type(1) << log_k1; int_type* bucket1 = new int_type[k1]; const unsigned int shift = (unsigned int)(sizeof(key_type) * 8 - lognbuckets); const unsigned int shift1 = shift - log_k1; STXXL_VERBOSE_STABLE_KSORT("Classifying " << nbuckets << " buckets, max size:" << max_bucket_size_rec << " block size:" << block_type::size << " log_k1:" << log_k1); for (unsigned_type k = 0; k < nbuckets; k++) { nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[k], block_type::size); const unsigned log_k1_k = STXXL_MAX(ilog2_ceil(bucket_sizes[k] * sizeof(type_key_) / STXXL_L2_SIZE), 1); assert(log_k1_k <= log_k1); k1 = (unsigned_type)(1) << log_k1_k; std::fill(bucket1, bucket1 + k1, 0); STXXL_VERBOSE_STABLE_KSORT("Classifying bucket " << k << " size:" << bucket_sizes[k] << " blocks:" << nbucket_blocks << " log_k1:" << log_k1_k); // classify first nbucket_blocks-1 blocks, they are full type_key_* ref_ptr = refs1; key_type offset1 = offset + (key_type(1) << key_type(shift)) * key_type(k); for (i = 0; i < nbucket_blocks - 1; i++) { reqs1[i]->wait(); stable_ksort_local::classify_block(blocks1[i].begin(), blocks1[i].end(), ref_ptr, bucket1, offset1, shift1 /*,k1*/); } // last block might be non-full const unsigned_type last_block_size = (unsigned_type)(bucket_sizes[k] - (nbucket_blocks - 1) * block_type::size); reqs1[i]->wait(); //STXXL_MSG("block_type::size: "<ptr)); delete[] bucket2; c = cEnd; d = dEnd; } // submit next read const unsigned_type bucket2submit = k + 2; if (bucket2submit < nbuckets) { nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[bucket2submit], block_type::size); for (i = 0; i < nbucket_blocks; i++) reqs1[i] = blocks1[i].read(bucket_bids[bucket2submit][i]); } std::swap(blocks1, blocks2); std::swap(reqs1, reqs2); } delete[] bucket1; delete[] refs1; delete[] refs2; delete[] blocks1; delete[] blocks2; delete[] reqs1; delete[] reqs2; delete[] bucket_bids; delete[] bucket_sizes; if (last.block_offset()) { // has to skip part of the first block block_type* block = new block_type; request_ptr req = block->read(*last.bid()); req->wait(); for (i = last.block_offset(); i < block_type::size; i++) { out << block->elem[i]; } delete block; } end = timestamp(); } STXXL_VERBOSE("Elapsed time : " << end - begin << " s. Distribution time: " << dist_end - begin << " s"); STXXL_VERBOSE("Time in I/O wait(ds): " << io_wait_after_d << " s"); STXXL_VERBOSE(*stats::get_instance()); } //! \} STXXL_END_NAMESPACE #endif // !STXXL_ALGO_STABLE_KSORT_HEADER stxxl-1.4.1/include/stxxl/bits/algo/sort_helper.h000644 001411 000144 00000011446 12414452316 021674 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/sort_helper.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2003 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_SORT_HELPER_HEADER #define STXXL_ALGO_SORT_HELPER_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \internal namespace sort_helper { template inline void verify_sentinel_strict_weak_ordering(StrictWeakOrdering cmp) { STXXL_ASSERT(!cmp(cmp.min_value(), cmp.min_value())); STXXL_ASSERT(cmp(cmp.min_value(), cmp.max_value())); STXXL_ASSERT(!cmp(cmp.max_value(), cmp.min_value())); STXXL_ASSERT(!cmp(cmp.max_value(), cmp.max_value())); } template struct trigger_entry { typedef BlockType block_type; typedef typename block_type::bid_type bid_type; typedef ValueType value_type; bid_type bid; value_type value; operator bid_type () { return bid; } }; template struct trigger_entry_cmp : public std::binary_function { typedef TriggerEntryType trigger_entry_type; ValueCmp cmp; trigger_entry_cmp(ValueCmp c) : cmp(c) { } trigger_entry_cmp(const trigger_entry_cmp& a) : cmp(a.cmp) { } bool operator () (const trigger_entry_type& a, const trigger_entry_type& b) const { return cmp(a.value, b.value); } }; template struct run_cursor2_cmp : public std::binary_function< run_cursor2, run_cursor2, bool > { typedef BlockType block_type; typedef PrefetcherType prefetcher_type; typedef ValueCmp value_cmp; typedef run_cursor2 cursor_type; value_cmp cmp; run_cursor2_cmp(value_cmp c) : cmp(c) { } run_cursor2_cmp(const run_cursor2_cmp& a) : cmp(a.cmp) { } inline bool operator () (const cursor_type& a, const cursor_type& b) const { if (UNLIKELY(b.empty())) return true; // sentinel emulation if (UNLIKELY(a.empty())) return false; // sentinel emulation return (cmp(a.current(), b.current())); } }; // this function is used by parallel mergers template inline unsigned_type count_elements_less_equal(const SequenceVector& seqs, const ValueType& bound, Comparator cmp) { typedef typename SequenceVector::size_type seqs_size_type; typedef typename SequenceVector::value_type::first_type iterator; unsigned_type count = 0; for (seqs_size_type i = 0; i < seqs.size(); ++i) { iterator position = std::upper_bound(seqs[i].first, seqs[i].second, bound, cmp); STXXL_VERBOSE1("less equal than " << position - seqs[i].first); count += position - seqs[i].first; } STXXL_VERBOSE1("finished loop"); return count; } // this function is used by parallel mergers template inline void refill_or_remove_empty_sequences(SequenceVector& seqs, BufferPtrVector& buffers, Prefetcher& prefetcher) { typedef typename SequenceVector::size_type seqs_size_type; for (seqs_size_type i = 0; i < seqs.size(); ++i) { if (seqs[i].first == seqs[i].second) // run empty { if (prefetcher.block_consumed(buffers[i])) { seqs[i].first = buffers[i]->begin(); // reset iterator seqs[i].second = buffers[i]->end(); STXXL_VERBOSE1("block ran empty " << i); } else { seqs.erase(seqs.begin() + i); // remove this sequence buffers.erase(buffers.begin() + i); STXXL_VERBOSE1("seq removed " << i); --i; // don't skip the next sequence } } } } } // namespace sort_helper STXXL_END_NAMESPACE #endif // !STXXL_ALGO_SORT_HELPER_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/algo/scan.h000644 001411 000144 00000025354 12414452316 020275 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/algo/scan.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_ALGO_SCAN_HEADER #define STXXL_ALGO_SCAN_HEADER #include #include #include #include STXXL_BEGIN_NAMESPACE //! \addtogroup stlalgo //! \{ /*! * External equivalent of std::for_each, see \ref design_algo_foreach. * * stxxl::for_each applies the function object \c functor to each element in * the range [first, last); \c functor's return value, if any, is * ignored. Applications are performed in forward order, i.e. from first to * last. stxxl::for_each returns the function object after it has been applied * to each element. To overlap I/O and computation \c nbuffers used (a value * at least \a D is recommended). The size of the buffers is derived from the * container that is pointed by the iterators. * * \remark The implementation exploits STXXL buffered streams (computation and I/O overlapped). * * \param begin object of model of \c ext_random_access_iterator concept * \param end object of model of \c ext_random_access_iterator concept * \param functor function object of model of \c std::UnaryFunction concept * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D ) * \return function object \c functor after it has been applied to the each element of the given range * * \warning nested stxxl::for_each are not supported */ template UnaryFunction for_each(ExtIterator begin, ExtIterator end, UnaryFunction functor, int_type nbuffers = 0) { if (begin == end) return functor; typedef typename ExtIterator::value_type value_type; typedef buf_istream< typename ExtIterator::block_type, typename ExtIterator::bids_container_iterator > buf_istream_type; begin.flush(); // flush container if (nbuffers == 0) nbuffers = 2 * config::get_instance()->disks_number(); // create prefetching stream, buf_istream_type in(begin.bid(), end.bid() + ((end.block_offset()) ? 1 : 0), nbuffers); ExtIterator cur = begin - begin.block_offset(); // leave part of the block before begin untouched (e.g. copy) for ( ; cur != begin; ++cur) { value_type tmp; in >> tmp; } // apply functor to the range [begin,end) for ( ; cur != end; ++cur) { value_type tmp; in >> tmp; functor(tmp); } // leave part of the block after end untouched if (end.block_offset()) { ExtIterator last_block_end = end - end.block_offset() + ExtIterator::block_type::size; for ( ; cur != last_block_end; ++cur) { value_type tmp; in >> tmp; } } return functor; } /*! * External equivalent of std::for_each (mutating), see \ref design_algo_foreachm * * stxxl::for_each_m applies the function object \c functor to each element in * the range [first, last); \c functor's return value, if any, is * ignored. Applications are performed in forward order, i.e. from first to * last. stxxl::for_each_m returns the function object after it has been * applied to each element. To overlap I/O and computation \c nbuffers are used * (a value at least \a 2D is recommended). The size of the buffers is derived * from the container that is pointed by the iterators. * * \remark The implementation exploits STXXL buffered streams (computation and * I/O overlapped) * * \param begin object of model of \c ext_random_access_iterator concept * \param end object of model of \c ext_random_access_iterator concept * \param functor object of model of \c std::UnaryFunction concept * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D ) * \return function object \c functor after it has been applied to the each element of the given range * * \warning nested stxxl::for_each_m are not supported */ template UnaryFunction for_each_m(ExtIterator begin, ExtIterator end, UnaryFunction functor, int_type nbuffers = 0) { if (begin == end) return functor; typedef typename ExtIterator::value_type value_type; typedef buf_istream< typename ExtIterator::block_type, typename ExtIterator::bids_container_iterator > buf_istream_type; typedef buf_ostream< typename ExtIterator::block_type, typename ExtIterator::bids_container_iterator > buf_ostream_type; begin.flush(); // flush container if (nbuffers == 0) nbuffers = 2 * config::get_instance()->disks_number(); // create prefetching stream, buf_istream_type in(begin.bid(), end.bid() + ((end.block_offset()) ? 1 : 0), nbuffers / 2); // create buffered write stream for blocks buf_ostream_type out(begin.bid(), nbuffers / 2); // REMARK: these two streams do I/O while // functor is being computed (overlapping for free) ExtIterator cur = begin - begin.block_offset(); // leave part of the block before begin untouched (e.g. copy) for ( ; cur != begin; ++cur) { value_type tmp; in >> tmp; out << tmp; } // apply functor to the range [begin,end) for ( ; cur != end; ++cur) { value_type tmp; in >> tmp; functor(tmp); out << tmp; } // leave part of the block after end untouched if (end.block_offset()) { ExtIterator _last_block_end = end - end.block_offset() + ExtIterator::block_type::size; for ( ; cur != _last_block_end; ++cur) { value_type tmp; in >> tmp; out << tmp; } } return functor; } /*! * External equivalent of std::generate, see \ref design_algo_generate. * * Generate assigns the result of invoking \c generator, a function object that * takes no arguments, to each element in the range [first, last). To overlap * I/O and computation \c nbuffers are used (a value at least \a D is * recommended). The size of the buffers is derived from the container that is * pointed by the iterators. * * \remark The implementation exploits STXXL buffered streams (computation and * I/O overlapped). * * \param begin object of model of \c ext_random_access_iterator concept * \param end object of model of \c ext_random_access_iterator concept * \param generator function object of model of \c std::generator concept * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D, or zero for automaticl 2*D) */ template void generate(ExtIterator begin, ExtIterator end, Generator generator, int_type nbuffers = 0) { typedef typename ExtIterator::block_type block_type; typedef buf_ostream< block_type, typename ExtIterator::bids_container_iterator > buf_ostream_type; while (begin.block_offset()) // go to the beginning of the block // of the external vector { if (begin == end) return; *begin = generator(); ++begin; } begin.flush(); // flush container if (nbuffers == 0) nbuffers = 2 * config::get_instance()->disks_number(); // create buffered write stream for blocks buf_ostream_type outstream(begin.bid(), nbuffers); assert(begin.block_offset() == 0); // delay calling block_externally_updated() until the block is // completely filled (and written out) in outstream typename ExtIterator::const_iterator prev_block = begin; while (end != begin) { if (begin.block_offset() == 0) { if (prev_block != begin) { prev_block.block_externally_updated(); prev_block = begin; } } *outstream = generator(); ++begin; ++outstream; } typename ExtIterator::const_iterator out = begin; while (out.block_offset()) // filling the rest of the block { *outstream = *out; ++out; ++outstream; } if (prev_block != out) prev_block.block_externally_updated(); begin.flush(); } /*! * External equivalent of std::find, see \ref design_algo_find. * * Returns the first iterator \a i in the range [first, last) such that *i * == value. Returns last if no such iterator exists. To overlap I/O and * computation \c nbuffers are used (a value at least \a D is recommended). The * size of the buffers is derived from the container that is pointed by the * iterators. * * \remark The implementation exploits STXXL buffered streams (computation and * I/O overlapped). * * \param begin object of model of \c ext_random_access_iterator concept * \param end object of model of \c ext_random_access_iterator concept * \param value value that is equality comparable to the ExtIterator's value type * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D) * \return first iterator \c i in the range [begin,end) such that *( \c i ) == \c value, if no * such exists then \c end */ template ExtIterator find(ExtIterator begin, ExtIterator end, const EqualityComparable& value, int_type nbuffers = 0) { if (begin == end) return end; typedef buf_istream< typename ExtIterator::block_type, typename ExtIterator::bids_container_iterator > buf_istream_type; begin.flush(); // flush container if (nbuffers == 0) nbuffers = 2 * config::get_instance()->disks_number(); // create prefetching stream, buf_istream_type in(begin.bid(), end.bid() + ((end.block_offset()) ? 1 : 0), nbuffers); ExtIterator cur = begin - begin.block_offset(); // skip part of the block before begin untouched for ( ; cur != begin; ++cur) ++in; // search in the the range [begin,end) for ( ; cur != end; ++cur) { typename ExtIterator::value_type tmp; in >> tmp; if (tmp == value) return cur; } return cur; } //! \} STXXL_END_NAMESPACE #endif // !STXXL_ALGO_SCAN_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/msvc_compatibility.h000644 001411 000144 00000002035 12405153572 022321 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/msvc_compatibility.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009, 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MSVC_COMPATIBILITY_HEADER #define STXXL_MSVC_COMPATIBILITY_HEADER #include #if STXXL_MSVC #include inline double log2(double x) { return (log(x) / log(2.)); } // http://msdn.microsoft.com/en-us/library/2ts7cx93.aspx #define snprintf _snprintf // http://msdn.microsoft.com/en-us/library/h80404d3.aspx #define strtoll _strtoi64 // http://msdn.microsoft.com/en-us/library/85zk715d.aspx #define strtoull _strtoui64 #endif // STXXL_MSVC #endif // !STXXL_MSVC_COMPATIBILITY_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/singleton.h000644 001411 000144 00000004274 12405375303 020427 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/singleton.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008, 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_SINGLETON_HEADER #define STXXL_SINGLETON_HEADER #include #include #include #include #include STXXL_BEGIN_NAMESPACE template class singleton : private noncopyable { typedef INSTANCE instance_type; typedef instance_type* instance_pointer; typedef volatile instance_pointer volatile_instance_pointer; static volatile_instance_pointer instance; static instance_pointer create_instance(); static void destroy_instance(); public: inline static instance_pointer get_instance() { if (!instance) return create_instance(); return instance; } }; template typename singleton::instance_pointer singleton::create_instance() { static mutex create_mutex; scoped_mutex_lock instance_write_lock(create_mutex); if (!instance) { instance = new instance_type(); if (destroy_on_exit) register_exit_handler(destroy_instance); } return instance; } template void singleton::destroy_instance() { instance_pointer inst = instance; //instance = NULL; instance = reinterpret_cast(unsigned_type(-1)); // bomb if used again delete inst; } template typename singleton::volatile_instance_pointer singleton::instance = NULL; STXXL_END_NAMESPACE #endif // !STXXL_SINGLETON_HEADER stxxl-1.4.1/include/stxxl/bits/parallel.h000644 001411 000144 00000014253 12405375303 020217 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/parallel.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2011 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_PARALLEL_HEADER #define STXXL_PARALLEL_HEADER #include #undef STXXL_PARALLEL #undef STXXL_PARALLEL_MODE #if defined(_GLIBCXX_PARALLEL) || STXXL_PARALLEL_MODE_EXPLICIT #define STXXL_PARALLEL_MODE #endif #if defined(STXXL_PARALLEL_MODE) #define STXXL_PARALLEL 1 #else #define STXXL_PARALLEL 0 #endif #include #ifdef STXXL_PARALLEL_MODE #include #endif #if STXXL_PARALLEL #include #endif #include #include #include #if defined(_GLIBCXX_PARALLEL) //use _STXXL_FORCE_SEQUENTIAL to tag calls which are not worthwhile parallelizing #define _STXXL_FORCE_SEQUENTIAL , __gnu_parallel::sequential_tag() #else #define _STXXL_FORCE_SEQUENTIAL #endif #if 0 // sorting triggers is done sequentially #define _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL _STXXL_FORCE_SEQUENTIAL #else // sorting triggers may be parallelized #define _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL #endif #if !STXXL_PARALLEL #undef STXXL_PARALLEL_MULTIWAY_MERGE #define STXXL_PARALLEL_MULTIWAY_MERGE 0 #endif #if defined(STXXL_PARALLEL_MODE) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40400) #undef STXXL_PARALLEL_MULTIWAY_MERGE #define STXXL_PARALLEL_MULTIWAY_MERGE 0 #endif #if !defined(STXXL_PARALLEL_MULTIWAY_MERGE) #define STXXL_PARALLEL_MULTIWAY_MERGE 1 #endif #if !defined(STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD) #define STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD 0 #endif #if STXXL_PARALLEL_MODE_EXPLICIT #include #else #include #endif STXXL_BEGIN_NAMESPACE inline unsigned sort_memory_usage_factor() { #if STXXL_PARALLEL && !STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD && defined(STXXL_PARALLEL_MODE) return (__gnu_parallel::_Settings::get().sort_algorithm == __gnu_parallel::MWMS && omp_get_max_threads() > 1) ? 2 : 1; //memory overhead for multiway mergesort #else return 1; //no overhead #endif } inline void check_sort_settings() { #if STXXL_PARALLEL && defined(STXXL_PARALLEL_MODE) && !defined(STXXL_NO_WARN_OMP_NESTED) static bool did_warn = false; if (!did_warn) { if (__gnu_parallel::_Settings::get().sort_algorithm != __gnu_parallel::MWMS) { if (omp_get_max_threads() <= 2) { did_warn = true; // no problem with at most 2 threads, no need to check again } else if (!omp_get_nested()) { STXXL_ERRMSG("Inefficient settings detected. To get full potential from your CPU it is recommended to set OMP_NESTED=TRUE in the environment."); did_warn = true; } } } #else // nothing to check #endif } inline bool do_parallel_merge() { #if STXXL_PARALLEL_MULTIWAY_MERGE && defined(STXXL_PARALLEL_MODE) return !stxxl::SETTINGS::native_merge && omp_get_max_threads() >= 1; #else return false; #endif } namespace potentially_parallel { #if STXXL_PARALLEL_MODE_EXPLICIT using __gnu_parallel::sort; using __gnu_parallel::random_shuffle; #else using std::sort; using std::random_shuffle; #endif } // namespace potentially_parallel namespace parallel { #if STXXL_PARALLEL /*! Multi-way merging dispatcher. * @param seqs_begin Begin iterator of iterator pair input sequence. * @param seqs_end End iterator of iterator pair input sequence. * @param target Begin iterator out output sequence. * @param comp Comparator. * @param length Maximum length to merge. * @return End iterator of output sequence. */ template RandomAccessIterator3 multiway_merge(RandomAccessIteratorPairIterator seqs_begin, RandomAccessIteratorPairIterator seqs_end, RandomAccessIterator3 target, Comparator comp, DiffType length) { #if defined(STXXL_PARALLEL_MODE) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400) return __gnu_parallel::multiway_merge(seqs_begin, seqs_end, target, length, comp); #elif defined(STXXL_PARALLEL_MODE) return __gnu_parallel::multiway_merge(seqs_begin, seqs_end, target, comp, length); #else #error "no implementation found for multiway_merge()" #endif } /*! Multi-way merging front-end. * @param seqs_begin Begin iterator of iterator pair input sequence. * @param seqs_end End iterator of iterator pair input sequence. * @param target Begin iterator out output sequence. * @param comp Comparator. * @param length Maximum length to merge. * @return End iterator of output sequence. * @pre For each @c i, @c seqs_begin[i].second must be the end marker of the sequence, but also reference the one more sentinel element. */ template RandomAccessIterator3 multiway_merge_sentinel(RandomAccessIteratorPairIterator seqs_begin, RandomAccessIteratorPairIterator seqs_end, RandomAccessIterator3 target, Comparator comp, DiffType length) { #if defined(STXXL_PARALLEL_MODE) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400) return __gnu_parallel::multiway_merge_sentinels(seqs_begin, seqs_end, target, length, comp); #elif defined(STXXL_PARALLEL_MODE) return __gnu_parallel::multiway_merge_sentinels(seqs_begin, seqs_end, target, comp, length); #else #error "no implementation found for multiway_merge_sentinel()" #endif } #endif } // namespace parallel STXXL_END_NAMESPACE #endif // !STXXL_PARALLEL_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/bits/noncopyable.h000644 001411 000144 00000002075 12405153572 020735 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/noncopyable.h * * Inspired by boost::noncopyable. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_NONCOPYABLE_HEADER #define STXXL_NONCOPYABLE_HEADER #include #include #if STXXL_BOOST_CONFIG #include #endif STXXL_BEGIN_NAMESPACE #if STXXL_BOOST_CONFIG typedef boost::noncopyable noncopyable; #else class noncopyable { protected: noncopyable() { } private: // copying and assignment is not allowed noncopyable(const noncopyable&); const noncopyable& operator = (const noncopyable&); }; #endif STXXL_END_NAMESPACE #endif // !STXXL_NONCOPYABLE_HEADER stxxl-1.4.1/include/stxxl/bits/unused.h000644 001411 000144 00000001431 12405375303 017720 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl/bits/unused.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_UNUSED_HEADER #define STXXL_UNUSED_HEADER #include STXXL_BEGIN_NAMESPACE template inline void STXXL_UNUSED(const U&) { } STXXL_END_NAMESPACE #endif // !STXXL_UNUSED_HEADER // vim: et:ts=4:sw=4 stxxl-1.4.1/include/stxxl/all000644 001411 000144 00000000773 12405153572 016010 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/all * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/mallocstats000644 001411 000144 00000001025 12405153572 017555 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/mallocstats * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/queue000644 001411 000144 00000001023 12405153572 016351 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/queue * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/timer000644 001411 000144 00000001017 12405153572 016350 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/timer * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl/sequence000644 001411 000144 00000001016 12405153572 017037 0ustar00tbusers000000 000000 // -*- mode: c++ -*- /*************************************************************************** * include/stxxl/sequence * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/include/stxxl.h000644 001411 000144 00000002010 12410750556 015451 0ustar00tbusers000000 000000 /*************************************************************************** * include/stxxl.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2007 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_MAIN_HEADER #define STXXL_MAIN_HEADER #include #include #include #include #include #include #if ! defined(__GNUG__) || ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 30400) // map does not work with g++ 3.3 #include #endif #include #include #include #include #include #include #include #endif // STXXL_MAIN_HEADER stxxl-1.4.1/examples/applications/skew3.cpp000644 001411 000144 00000126743 12411366426 020563 0ustar00tbusers000000 000000 /*************************************************************************** * examples/applications/skew3.cpp * * Implementation of the external memory suffix sorting algorithm DC3 aka * skew3 as described in Roman Dementiev, Juha Kaerkkaeinen, Jens Mehnert and * Peter Sanders. "Better External Memory Suffix Array Construction". Journal * of Experimental Algorithmics (JEA), volume 12, 2008. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004 Jens Mehnert * Copyright (C) 2012-2013 Timo Bingmann * Copyright (C) 2012-2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using stxxl::uint64; using stxxl::internal_size_type; using stxxl::external_size_type; namespace stream = stxxl::stream; // 1 GiB ram used by external data structures / 1 MiB block size internal_size_type ram_use = 1024 * 1024 * 1024; // alphabet data type typedef unsigned char alphabet_type; // calculation data type typedef external_size_type size_type; /// Suffix Array checker for correctness verification /** * Algorithm to check whether the suffix array is correct. Loosely based on the * ideas of Kaerkkaeinen und Burghardt, originally implemented in STXXL by Jens * Mehnert (2004), reimplemented using triples by Timo Bingmann (2012). * * @param InputT is the original text, from which the suffix array was build * @param InputSA is the suffix array from InputT * * Note: ISA := The inverse of SA */ template bool sacheck(InputT& inputT, InputSA& inputSA) { typedef typename InputSA::value_type offset_type; typedef stxxl::tuple pair_type; typedef stxxl::tuple triple_type; // *** Pipeline Declaration *** // Build tuples with index: (SA[i]) -> (i, SA[i]) typedef stxxl::stream::counter index_counter_type; index_counter_type index_counter; typedef stream::make_tuple tuple_index_sa_type; tuple_index_sa_type tuple_index_sa(index_counter, inputSA); // take (i, SA[i]) and sort to (ISA[i], i) typedef stxxl::tuple_less2nd pair_less_type; typedef typename stream::sort build_isa_type; build_isa_type build_isa(tuple_index_sa, pair_less_type(), ram_use / 3); // build (ISA[i], T[i], ISA[i+1]) and sort to (i, T[SA[i]], ISA[SA[i]+1]) typedef stxxl::tuple_less1st triple_less_type; // comparison relation typedef typename stream::use_push triple_push_type; // indicator use push() typedef typename stream::runs_creator triple_rc_type; typedef typename stream::runs_merger triple_rm_type; triple_rc_type triple_rc(triple_less_type(), ram_use / 3); // ************************* Process ****************************** // loop 1: read ISA and check for a permutation. Simultaneously create runs // of triples by iterating ISA and T. size_type totalSize; { offset_type prev_isa = (*build_isa).first; offset_type counter = 0; while (!build_isa.empty()) { if ((*build_isa).second != counter) { std::cout << "Error: suffix array is not a permutation of 0..n-1." << std::endl; return false; } ++counter; ++build_isa; // ISA is one in front of T if (!build_isa.empty()) { triple_rc.push(triple_type(prev_isa, *inputT, (*build_isa).first)); prev_isa = (*build_isa).first; } ++inputT; } totalSize = counter; } if (totalSize == 1) return true; // ************************************************************************ // loop 2: read triples (i,T[SA[i]],ISA[SA[i]+1]) and check for correct // ordering. triple_rm_type triple_rm(triple_rc.result(), triple_less_type(), ram_use / 3); { triple_type prev_triple = *triple_rm; size_type counter = 0; ++triple_rm; while (!triple_rm.empty()) { const triple_type& this_triple = *triple_rm; if (prev_triple.second > this_triple.second) { // simple check of first character of suffix std::cout << "Error: suffix array position " << counter << " ordered incorrectly." << std::endl; return false; } else if (prev_triple.second == this_triple.second) { if (this_triple.third == (offset_type)totalSize) { // last suffix of string must be first among those with same // first character std::cout << "Error: suffix array position " << counter << " ordered incorrectly." << std::endl; return false; } if (prev_triple.third != (offset_type)totalSize && prev_triple.third > this_triple.third) { // positions SA[i] and SA[i-1] has same first character but // their suffixes are ordered incorrectly: the suffix // position of SA[i] is given by ISA[SA[i]] std::cout << "Error: suffix array position " << counter << " ordered incorrectly." << std::endl; return false; } } prev_triple = this_triple; ++triple_rm; ++counter; } } return true; } template bool sacheck_vectors(InputT& inputT, InputSA& inputSA) { typename stream::streamify_traits::stream_type streamT = stream::streamify(inputT.begin(), inputT.end()); typename stream::streamify_traits::stream_type streamSA = stream::streamify(inputSA.begin(), inputSA.end()); return sacheck(streamT, streamSA); } /// DC3 aka skew algorithm /* * DC3 aka skew algorithm a short description. T := input string * The recursion works as follows: * Step 1: a) pick all mod1/mod2 triples (i.e. triples T[i,i+2] at position i mod 3 != 0) (-> extract_mod12 class) * b) sort mod1/mod2 triples lexicographically (-> build_sa class) * c) give mod1/mod2 triples lexicographical ascending names n (-> naming class) * d) check lexicographical names for uniqueness (-> naming class) * If yes: proceed to next Step, If no: set T := lexicographical names and run Step 1 again * Step 2: a) by sorting the lexicographical names n we receive ranks r * b) construct mod0-quints, mod1-quads and mod2-quints (-> build_sa class) * c) prepare for merging by: * sort mod0-quints by 2 components, sort mod1-quads / mod2-quints by one component (-> build_sa class) * c) merge mod0-quints, mod1-quads and mod2-quints (-> merge_sa class) * Step 3: a) return Suffix Array of T * * @param offset_type later suffix array data type */ template class skew { public: // 2-tuple, 3-tuple, 4-tuple (=quads), 5-tuple(=quints) definition typedef stxxl::tuple skew_pair_type; typedef stxxl::tuple skew_triple_type; typedef stxxl::tuple skew_quad_type; typedef stxxl::tuple skew_quint_type; typedef typename stxxl::VECTOR_GENERATOR::result offset_array_type; typedef stream::vector_iterator2stream offset_array_it_rg; /** Comparison function for the mod0 tuples. */ struct less_mod0 { typedef skew_quint_type value_type; bool operator () (const value_type& a, const value_type& b) const { if (a.second == b.second) return a.fourth < b.fourth; else return a.second < b.second; } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; typedef stxxl::tuple_less2nd less_mod1; typedef stxxl::tuple_less2nd less_mod2; /** Put the (0 mod 2) [which are the 1,2 mod 3 tuples] tuples at the begin. */ struct less_skew { typedef skew_pair_type value_type; bool operator () (const value_type& a, const value_type& b) const { if ((a.first & 1) == (b.first & 1)) return a.first < b.first; else return (a.first & 1) < (b.first & 1); } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; /** Sort skew_quad datatype. */ template struct less_quad { typedef stxxl::tuple value_type; bool operator () (const value_type& a, const value_type& b) const { if (a.second == b.second) { if (a.third == b.third) return a.fourth < b.fourth; else return a.third < b.third; } else return a.second < b.second; } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; /** Check, if last two components of tree quads are equal. */ template static inline bool quad_eq(const quad_type& a, const quad_type& b) { return (a.second == b.second) && (a.third == b.third) && (a.fourth == b.fourth); } /** Naming pipe for the conventional skew algorithm without discarding. */ template class naming { public: typedef typename Input::value_type quad_type; typedef skew_pair_type value_type; private: Input& A; bool& unique; offset_type lexname; quad_type prev; skew_pair_type result; public: naming(Input& A_, bool& unique_) : A(A_), unique(unique_), lexname(0) { assert(!A.empty()); unique = true; prev = *A; result.first = prev.first; result.second = lexname; } const value_type& operator * () const { return result; } naming& operator ++ () { assert(!A.empty()); ++A; if (A.empty()) return *this; quad_type curr = *A; if (!quad_eq(prev, curr)) { ++lexname; } else { if (!A.empty() && curr.second != offset_type(0)) { unique = false; } } result.first = curr.first; result.second = lexname; prev = curr; return *this; } bool empty() const { return A.empty(); } }; /** Create tuples of 2 components until one of the input streams are empty. */ template class make_pairs { public: typedef stxxl::tuple value_type; private: InputA& A; InputB& B; value_type result; public: make_pairs(InputA& a, InputB& b) : A(a), B(b) { assert(!A.empty()); assert(!B.empty()); if (!empty()) { result = value_type(*A, *B + add_alphabet); } } const value_type& operator * () const { return result; } make_pairs& operator ++ () { assert(!A.empty()); assert(!B.empty()); ++A; ++B; if (!A.empty() && !B.empty()) { result = value_type(*A, *B + add_alphabet); } return *this; } bool empty() const { return (A.empty() || B.empty()); } }; /** * Collect three characters t_i, t_{i+1}, t_{i+2} beginning at the index * i. Since we need at least one unique endcaracter, we free the first * characters i.e. we map (t_i) -> (i,t_i,t_{i+1},t_{i+2}) * * @param Input holds all characters t_i from input string t * @param alphabet_type * @param add_alphabet */ template class make_quads { public: typedef stxxl::tuple value_type; private: Input& A; value_type current; offset_type counter; unsigned int z3z; // = counter mod 3, ("+",Z/3Z) is cheaper than % bool finished; offset_array_type& text; public: make_quads(Input& data_in_, offset_array_type& text_) : A(data_in_), current(0, 0, 0, 0), counter(0), z3z(0), finished(false), text(text_) { assert(!A.empty()); current.first = counter; current.second = (*A).second + add_alphabet; ++A; if (!A.empty()) { current.third = (*A).second + add_alphabet; ++A; } else { current.third = 0; current.fourth = 0; } if (!A.empty()) { current.fourth = (*A).second + add_alphabet; } else { current.fourth = 0; } } const value_type& operator * () const { return current; } make_quads& operator ++ () { assert(!A.empty() || !finished); if (current.second != offset_type(0)) { text.push_back(current.second); } // Calculate module if (++z3z == 3) z3z = 0; current.first = ++counter; current.second = current.third; current.third = current.fourth; if (!A.empty()) ++A; if (!A.empty()) { current.fourth = (*A).second + add_alphabet; } else { current.fourth = 0; } // Inserts a dummy tuple for input sizes of n%3==1 if ((current.second == offset_type(0)) && (z3z != 1)) { finished = true; } return *this; } bool empty() const { return (A.empty() && finished); } }; /** Drop 1/3 of the input. More exactly the offsets at positions (0 mod * 3). Index begins with 0. */ template class extract_mod12 { public: typedef typename Input::value_type value_type; private: Input& A; offset_type counter; offset_type output_counter; value_type result; public: extract_mod12(Input& A_) : A(A_), counter(0), output_counter(0) { assert(!A.empty()); ++A, ++counter; // skip 0 = mod0 offset if (!A.empty()) { result = *A; result.first = output_counter; } } const value_type& operator * () const { return result; } extract_mod12& operator ++ () { assert(!A.empty()); ++A, ++counter, ++output_counter; if (!A.empty() && (counter % 3) == 0) { // skip mod0 offsets ++A, ++counter; } if (!A.empty()) { result = *A; result.first = output_counter; } return *this; } bool empty() const { return A.empty(); } }; /** Create the suffix array from the current sub problem by simple * comparison-based merging. More precisely: compare characters(out of * text t) and ranks(out of ISA12) of the following constellation: * Input constellation: * @param Mod0 5-tuple (quint): * @param Mod1 4-tuple (quad): * @param Mod2 5-tuple (quint): */ template class merge_sa { public: typedef offset_type value_type; private: Mod0& A; Mod1& B; Mod2& C; skew_quint_type s0; skew_quad_type s1; skew_quint_type s2; int selected; bool done[3]; offset_type index; offset_type merge_result; bool cmp_mod1_less_mod2() { assert(!done[1] && !done[2]); return s1.second < s2.second; } bool cmp_mod0_less_mod2() { assert(!done[0] && !done[2]); if (s0.second == s2.third) { if (s0.third == s2.fourth) return s0.fifth < s2.fifth; else return s0.third < s2.fourth; } else return s0.second < s2.third; } bool cmp_mod0_less_mod1() { assert(!done[0] && !done[1]); if (s0.second == s1.third) return s0.fourth < s1.fourth; else return s0.second < s1.third; } void merge() { assert(!done[0] || !done[1] || !done[2]); if (done[0]) { if (done[2] || (!done[1] && cmp_mod1_less_mod2())) { selected = 1; merge_result = s1.first; } else { selected = 2; merge_result = s2.first; } } else if (done[1] || cmp_mod0_less_mod1()) { if (done[2] || cmp_mod0_less_mod2()) { selected = 0; merge_result = s0.first; } else { selected = 2; merge_result = s2.first; } } else { if (done[2] || cmp_mod1_less_mod2()) { selected = 1; merge_result = s1.first; } else { selected = 2; merge_result = s2.first; } } assert(!done[selected]); } public: bool empty() const { return (A.empty() && B.empty() && C.empty()); } merge_sa(Mod0& x1, Mod1& x2, Mod2& x3) : A(x1), B(x2), C(x3), selected(-1), index(0) { assert(!A.empty()); assert(!B.empty()); assert(!C.empty()); done[0] = false; done[1] = false; done[2] = false; s0 = *A; s1 = *B; s2 = *C; merge(); } const value_type& operator * () const { return merge_result; } merge_sa& operator ++ () { if (selected == 0) { assert(!A.empty()); ++A; if (!A.empty()) s0 = *A; else done[0] = true; } else if (selected == 1) { assert(!B.empty()); ++B; if (!B.empty()) s1 = *B; else done[1] = true; } else { assert(!C.empty()); assert(selected == 2); ++C; if (!C.empty()) s2 = *C; else done[2] = true; } ++index; if (!empty()) merge(); return *this; } }; /** Helper function for computing the size of the 2/3 subproblem. */ static inline size_type subp_size(size_type n) { return (n / 3) * 2 + ((n % 3) == 2); } /** * Sort mod0-quints / mod1-quads / mod2-quints and run merge_sa class to * merge them together. * @param S input string pipe type. * @param Mod1 mod1 tuples input pipe type. * @param Mod2 mod2 tuples input pipe type. */ template class build_sa { public: typedef offset_type value_type; static const unsigned int add_rank = 1; // free first rank to mark ranks beyond end of input private: // mod1 types typedef typename stream::use_push mod1_push_type; typedef typename stream::runs_creator mod1_runs_type; typedef typename mod1_runs_type::sorted_runs_type sorted_mod1_runs_type; typedef typename stream::runs_merger mod1_rm_type; // mod2 types typedef typename stream::use_push mod2_push_type; typedef typename stream::runs_creator mod2_runs_type; typedef typename mod2_runs_type::sorted_runs_type sorted_mod2_runs_type; typedef typename stream::runs_merger mod2_rm_type; // mod0 types typedef typename stream::use_push mod0_push_type; typedef typename stream::runs_creator mod0_runs_type; typedef typename mod0_runs_type::sorted_runs_type sorted_mod0_runs_type; typedef typename stream::runs_merger mod0_rm_type; // Merge type typedef merge_sa merge_sa_type; // Functions less_mod0 c0; less_mod1 c1; less_mod2 c2; // Runs merger mod1_rm_type* mod1_result; mod2_rm_type* mod2_result; mod0_rm_type* mod0_result; // Merger merge_sa_type* vmerge_sa; // Input S& source; Mod1& mod_1; Mod2& mod_2; // Tmp variables offset_type t[3]; offset_type old_t2; offset_type old_mod2; bool exists[3]; offset_type mod_one; offset_type mod_two; offset_type index; // Empty_flag bool ready; // Result value_type result; public: build_sa(S& source_, Mod1& mod_1_, Mod2& mod_2_, size_type a_size, size_t memsize) : source(source_), mod_1(mod_1_), mod_2(mod_2_), index(0), ready(false) { assert(!source_.empty()); // Runs storage // input: ISA_1,2 from previous level mod0_runs_type mod0_runs(c0, memsize / 4); mod1_runs_type mod1_runs(c1, memsize / 4); mod2_runs_type mod2_runs(c2, memsize / 4); while (!source.empty()) { exists[0] = false; exists[1] = false; exists[2] = false; if (!source.empty()) { t[0] = *source; ++source; exists[0] = true; } if (!source.empty()) { assert(!mod_1.empty()); t[1] = *source; ++source; mod_one = *mod_1 + add_rank; ++mod_1; exists[1] = true; } if (!source.empty()) { assert(!mod_2.empty()); t[2] = *source; ++source; mod_two = *mod_2 + add_rank; ++mod_2; exists[2] = true; } // Check special cases in the middle of "source" // Cases are cx|xc cxx|cxx and cxxc|xxc assert(t[0] != offset_type(0)); assert(t[1] != offset_type(0)); assert(t[2] != offset_type(0)); // Mod 0 : (index0,char0,char1,mod1,mod2) // Mod 1 : (index1,mod1,char1,mod2) // Mod 2 : (index2,mod2) if (exists[2]) { // Nothing is missed mod0_runs.push(skew_quint_type(index, t[0], t[1], mod_one, mod_two)); mod1_runs.push(skew_quad_type(index + 1, mod_one, t[1], mod_two)); if (index != offset_type(0)) { mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], mod_one)); } } else if (exists[1]) { // Last element missed mod0_runs.push(skew_quint_type(index, t[0], t[1], mod_one, 0)); mod1_runs.push(skew_quad_type(index + 1, mod_one, t[1], 0)); if (index != offset_type(0)) { mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], mod_one)); } } else { // Only one element left assert(exists[0]); mod0_runs.push(skew_quint_type(index, t[0], 0, 0, 0)); if (index != offset_type(0)) { mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], 0)); } } old_mod2 = mod_two; old_t2 = t[2]; index += 3; } if ((a_size % 3) == 0) { // changed if (index != offset_type(0)) { mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, 0, 0)); } } mod0_runs.deallocate(); mod1_runs.deallocate(); mod2_runs.deallocate(); std::cout << "merging S0 = " << mod0_runs.size() << ", S1 = " << mod1_runs.size() << ", S2 = " << mod2_runs.size() << " tuples" << std::endl; // Prepare for merging mod0_result = new mod0_rm_type(mod0_runs.result(), less_mod0(), memsize / 5); mod1_result = new mod1_rm_type(mod1_runs.result(), less_mod1(), memsize / 5); mod2_result = new mod2_rm_type(mod2_runs.result(), less_mod2(), memsize / 5); // output: ISA_1,2 for next level vmerge_sa = new merge_sa_type(*mod0_result, *mod1_result, *mod2_result); // read first suffix result = *(*vmerge_sa); } const value_type& operator * () const { return result; } build_sa& operator ++ () { assert(vmerge_sa != 0 && !vmerge_sa->empty()); ++(*vmerge_sa); if (!vmerge_sa->empty()) { result = *(*vmerge_sa); } else { // cleaning up assert(vmerge_sa->empty()); ready = true; assert(vmerge_sa != NULL); delete vmerge_sa, vmerge_sa = NULL; assert(mod0_result != NULL && mod1_result != NULL && mod2_result != NULL); delete mod0_result, mod0_result = NULL; delete mod1_result, mod1_result = NULL; delete mod2_result, mod2_result = NULL; } return *this; } ~build_sa() { if (vmerge_sa) delete vmerge_sa; if (mod0_result) delete mod0_result; if (mod1_result) delete mod1_result; if (mod2_result) delete mod2_result; } bool empty() const { return ready; } }; /** The skew algorithm. * @param Input type of the input pipe. */ template class algorithm { public: typedef offset_type value_type; typedef typename Input::value_type alphabet_type; protected: // finished reading final suffix array bool finished; // current recursion depth unsigned int rec_depth; protected: // generate (i) sequence typedef stxxl::stream::counter counter_stream_type; // Sorter typedef stxxl::tuple_less1st mod12cmp; typedef stxxl::sorter mod12_sorter_type; // Additional streaming items typedef stream::choose isa_second_type; typedef build_sa buildSA_type; typedef make_pairs precompute_isa_type; // Real recursive skew3 implementation // This part is the core of the skew algorithm and runs all class objects in their respective order template buildSA_type * skew3(RecInputType& p_Input) { // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) typedef make_quads make_quads_input_type; // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) with i = 1,2 mod 3 typedef extract_mod12 mod12_quads_input_type; // sort (i,t_i,t_{i+1},t_{i+2}) by (t_i,t_{i+1},t_{i+2}) typedef typename stream::sort > sort_mod12_input_type; // name (i,t_i,t_{i+1},t_{i+2}) -> (i,n_i) typedef naming naming_input_type; mod12_sorter_type m1_sorter(mod12cmp(), ram_use / 5); mod12_sorter_type m2_sorter(mod12cmp(), ram_use / 5); // sorted mod1 runs -concatenate- sorted mod2 runs typedef stxxl::stream::concatenate concatenation_type; // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) offset_array_type text; make_quads_input_type quads_input(p_Input, text); // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) with i = 1,2 mod 3 mod12_quads_input_type mod12_quads_input(quads_input); // sort (i,t_i,t_{i+1},t_{i+2}) by (t_i,t_i+1},t_{i+2}) sort_mod12_input_type sort_mod12_input(mod12_quads_input, less_quad(), ram_use / 5); // name (i,t_i,t_{i+1},t_{i+2}) -> (i,"n_i") bool unique = false; // is the current quad array unique? naming_input_type names_input(sort_mod12_input, unique); // create (i, s^12[i]) size_type concat_length = 0; // holds length of current S_12 while (!names_input.empty()) { const skew_pair_type& tmp = *names_input; if (tmp.first & 1) { m2_sorter.push(tmp); // sorter #2 } else { m1_sorter.push(tmp); // sorter #1 } ++names_input; concat_length++; } std::cout << "recursion string length = " << concat_length << std::endl; m1_sorter.sort(); m2_sorter.sort(); if (!unique) { std::cout << "not unique -> next recursion level = " << ++rec_depth << std::endl; // compute s^12 := lexname[S[1 mod 3]] . lexname[S[2 mod 3]], (also known as reduced recursion string 'R') concatenation_type concat_mod1mod2(m1_sorter, m2_sorter); buildSA_type* recType = skew3(concat_mod1mod2); // recursion with recursion string T' = concat_mod1mod2 lexnames std::cout << "exit recursion level = " << --rec_depth << std::endl; counter_stream_type isa_loop_index; precompute_isa_type isa_pairs(*recType, isa_loop_index); // add index as component => (SA12, i) // store beginning of mod2-tuples of s^12 in mod2_pos offset_type special = (concat_length != subp_size(text.size())); offset_type mod2_pos = offset_type((subp_size(text.size()) >> 1) + (subp_size(text.size()) & 1) + special); mod12_sorter_type isa1_pair(mod12cmp(), ram_use / 5); mod12_sorter_type isa2_pair(mod12cmp(), ram_use / 5); while (!isa_pairs.empty()) { const skew_pair_type& tmp = *isa_pairs; if (tmp.first < mod2_pos) { if (tmp.first + special < mod2_pos) // else: special sentinel tuple is dropped isa1_pair.push(tmp); // sorter #1 } else { isa2_pair.push(tmp); // sorter #2 } ++isa_pairs; } delete recType; isa1_pair.finish(); isa2_pair.finish(); offset_array_it_rg input(text.begin(), text.end()); // => (i, ISA) isa1_pair.sort(ram_use / 8); isa2_pair.sort(ram_use / 8); // pick ISA of (i, ISA) isa_second_type isa1(isa1_pair); isa_second_type isa2(isa2_pair); // prepare and run merger return new buildSA_type(input, isa1, isa2, text.size(), ram_use); } else // unique { std::cout << "unique names!" << std::endl; isa_second_type isa1(m1_sorter); isa_second_type isa2(m2_sorter); offset_array_it_rg source(text.begin(), text.end()); // prepare and run merger return new buildSA_type(source, isa1, isa2, text.size(), ram_use); } } // end of skew3() protected: // Adapt (t_i) -> (i,t_i) for input to fit to recursive call typedef make_pairs make_pairs_input_type; // points to final constructed suffix array generator buildSA_type* out_sa; public: algorithm(Input& data_in) : finished(false), rec_depth(0) { // (t_i) -> (i,t_i) counter_stream_type dummy; make_pairs_input_type pairs_input(dummy, data_in); out_sa = skew3(pairs_input); } const value_type& operator * () const { return *(*out_sa); } algorithm& operator ++ () { assert(out_sa); assert(!out_sa->empty()); ++(*out_sa); if (out_sa->empty()) { finished = true; delete out_sa; out_sa = NULL; } return *this; } ~algorithm() { if (out_sa) delete out_sa; } bool empty() const { return finished; } }; // algorithm class }; // skew class //! helper to print out readable characters. template static inline std::string dumpC(alphabet_type c) { std::ostringstream oss; if (isalnum(c)) oss << '\'' << (char)c << '\''; else oss << (int)c; return oss.str(); } //! helper stream to cut input off a specified length. template class cut_stream { public: //! same value type as input stream typedef typename InputType::value_type value_type; protected: //! instance of input stream InputType& m_input; //! counter after which the stream ends size_type m_count; public: cut_stream(InputType& input, size_type count) : m_input(input), m_count(count) { } const value_type& operator * () const { assert(m_count > 0); return *m_input; } cut_stream& operator ++ () { assert(!empty()); --m_count; ++m_input; return *this; } bool empty() const { return (m_count == 0) || m_input.empty(); } }; alphabet_type unary_generator() { return 'a'; } template int process(const std::string& input_filename, const std::string& output_filename, size_type sizelimit, bool text_output_flag, bool check_flag, bool input_verbatim) { static const size_t block_size = sizeof(offset_type) * 1024 * 1024 / 2; typedef typename stxxl::VECTOR_GENERATOR::result alphabet_vector_type; typedef typename stxxl::VECTOR_GENERATOR::result offset_vector_type; // input and output files (if supplied via command line) stxxl::syscall_file* input_file = NULL, * output_file = NULL; // input and output vectors for suffix array construction alphabet_vector_type input_vector; offset_vector_type output_vector; using stxxl::file; if (input_verbatim) { // copy input verbatim into vector input_vector.resize(input_filename.size()); std::copy(input_filename.begin(), input_filename.end(), input_vector.begin()); } else if (input_filename == "random") { if (sizelimit == std::numeric_limits::max()) { std::cout << "You must provide -s for generated inputs." << std::endl; return 1; } // fill input vector with random bytes input_vector.resize(sizelimit); stxxl::random_number8_r rand8; stxxl::generate(input_vector.begin(), input_vector.end(), rand8); } else if (input_filename == "unary") { if (sizelimit == std::numeric_limits::max()) { std::cout << "You must provide -s for generated inputs." << std::endl; return 1; } // fill input vector with random bytes input_vector.resize(sizelimit); stxxl::generate(input_vector.begin(), input_vector.end(), unary_generator); } else { // define input file object and map input_vector to input_file (no copying) input_file = new stxxl::syscall_file(input_filename, file::RDONLY | file::DIRECT); alphabet_vector_type file_input_vector(input_file); input_vector.swap(file_input_vector); } if (output_filename.size()) { // define output file object and map output_vector to output_file output_file = new stxxl::syscall_file(output_filename, file::RDWR | file::CREAT | file::DIRECT); offset_vector_type file_output_vector(output_file); output_vector.swap(file_output_vector); } // I/O measurement stxxl::stats* Stats = stxxl::stats::get_instance(); stxxl::stats_data stats_begin(*Stats); // construct skew class with bufreader input type typedef alphabet_vector_type::bufreader_type input_type; typedef cut_stream cut_input_type; typedef typename skew::template algorithm skew_type; size_type size = input_vector.size(); if (size > sizelimit) size = sizelimit; std::cout << "input size = " << size << std::endl; if (size + 3 >= std::numeric_limits::max()) { std::cout << "error: input is too long for selected word size!" << std::endl; return -1; } input_type input(input_vector); cut_input_type cut_input(input, size); skew_type skew(cut_input); // make sure output vector has the right size output_vector.resize(size); // write suffix array stream into output vector stream::materialize(skew, output_vector.begin(), output_vector.end()); std::cout << "output size = " << output_vector.size() << std::endl; std::cout << (stxxl::stats_data(*Stats) - stats_begin); // print i/o statistics if (text_output_flag) { std::cout << std::endl << "resulting suffix array:" << std::endl; for (unsigned int i = 0; i < output_vector.size(); i++) { std::cout << i << " : " << output_vector[i] << " : "; // We need a const reference because operator[] would write data const alphabet_vector_type& cinput = input_vector; for (unsigned int j = 0; output_vector[i] + j < cinput.size(); j++) { std::cout << dumpC(cinput[output_vector[i] + j]) << " "; } std::cout << std::endl; } } int ret = 0; if (check_flag) { (std::cout << "checking suffix array... ").flush(); if (!sacheck_vectors(input_vector, output_vector)) { std::cout << "failed!" << std::endl; ret = -1; } else std::cout << "ok." << std::endl; } // close file, but have to deallocate vector first! if (input_file) { input_vector = alphabet_vector_type(); delete input_file; } if (output_file) { output_vector = offset_vector_type(); delete output_file; } return ret; } int main(int argc, char* argv[]) { stxxl::cmdline_parser cp; cp.set_description("DC3 aka skew3 algorithm for external memory suffix array construction."); cp.set_author("Jens Mehnert , Timo Bingmann , Daniel Feist "); std::string input_filename, output_filename; size_type sizelimit = std::numeric_limits::max(); bool text_output_flag = false; bool check_flag = false; bool input_verbatim = false; unsigned wordsize = 32; cp.add_param_string("input", "Path to input file (or verbatim text).\n The special inputs 'random' and 'unary' generate such text on-the-fly.", input_filename); cp.add_flag('c', "check", "Check suffix array for correctness.", check_flag); cp.add_flag('t', "text", "Print out suffix array in readable text.", text_output_flag); cp.add_string('o', "output", "Output suffix array to given path.", output_filename); cp.add_flag('v', "verbatim", "Consider \"input\" as verbatim text to construct suffix array on.", input_verbatim); cp.add_bytes('s', "size", "Cut input text to given size, e.g. 2 GiB.", sizelimit); cp.add_bytes('M', "memuse", "Amount of RAM to use, default: 1 GiB.", ram_use); cp.add_uint('w', "wordsize", "Set word size of suffix array to 32, 40 or 64 bit, default: 32-bit.", wordsize); // process command line if (!cp.process(argc, argv)) return -1; if (wordsize == 32) return process(input_filename, output_filename, sizelimit, text_output_flag, check_flag, input_verbatim); else if (wordsize == 40) return process(input_filename, output_filename, sizelimit, text_output_flag, check_flag, input_verbatim); else if (wordsize == 64) return process(input_filename, output_filename, sizelimit, text_output_flag, check_flag, input_verbatim); else std::cerr << "Invalid wordsize for suffix array: 32, 40 or 64 are allowed." << std::endl; return -1; } stxxl-1.4.1/examples/applications/CMakeLists.txt000644 001411 000144 00000001362 12350112610 021532 0ustar00tbusers000000 000000 ############################################################################ # examples/applications/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ if(NOT CYGWIN AND NOT MINGW) #-tb too big to build on cygwin stxxl_build_example(skew3) if(MSVC AND BUILD_TESTS) # requires /bigobj flag to build set_target_properties(skew3 PROPERTIES COMPILE_FLAGS /bigobj) endif() stxxl_test(skew3 --size 1mib random --check) endif()stxxl-1.4.1/examples/common/cmdline.cpp000644 001411 000144 00000003121 12405152404 017714 0ustar00tbusers000000 000000 /*************************************************************************** * examples/common/cmdline.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ // [example] #include int main(int argc, char* argv[]) { stxxl::cmdline_parser cp; // add description and author cp.set_description("This may some day be a useful program, which solves " "many serious problems of the real world and achives " "global peace."); cp.set_author("Timo Bingmann "); // add an unsigned integer option --rounds unsigned int rounds = 0; cp.add_uint('r', "rounds", "N", "Run N rounds of the experiment.", rounds); // add a byte size argument which the user can enter like '1gi' stxxl::uint64 a_size = 0; cp.add_bytes('s', "size", "Number of bytes to process.", a_size); // add a required parameter std::string a_filename; cp.add_param_string("filename", "A filename to process", a_filename); // process command line if (!cp.process(argc, argv)) return -1; // some error occurred and help was always written to user. std::cout << "Command line parsed okay." << std::endl; // output for debugging cp.print_result(); // do something useful } // [example] stxxl-1.4.1/examples/common/CMakeLists.txt000644 001411 000144 00000001002 12350112610 020323 0ustar00tbusers000000 000000 ############################################################################ # examples/common/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_example(cmdline) stxxl_test(cmdline test)stxxl-1.4.1/examples/stream/stream1.cpp000644 001411 000144 00000021644 12405152404 017672 0ustar00tbusers000000 000000 /*************************************************************************** * examples/stream/stream1.cpp * * This file contains the example snippets from the stream tutorial. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012-2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include struct counter_object { // This stream produces a sequence of integers. typedef int value_type; private: // A class attribute to save the current value. int m_current_value; public: // A constructor to set the initial value to 1. counter_object() : m_current_value(1) { } // The retrieve operator returning the current value. const value_type& operator * () const { return m_current_value; } // Increment operator advancing to the next integer. counter_object& operator ++ () { ++m_current_value; return *this; } // Empty indicator, which in this case can check the current value. bool empty() const { return (m_current_value > 1000); } }; template struct squaring_object { // This stream produces a sequence of integers. typedef int value_type; private: // A reference to another stream of integers, which are our input. InputStream& m_input_stream; // A temporary value buffer to hold the current square in for retrieval. value_type m_current_value; public: // A constructor taking another stream of integers as input. squaring_object(InputStream& input_stream) : m_input_stream(input_stream) { if (!m_input_stream.empty()) { m_current_value = *m_input_stream; m_current_value = m_current_value * m_current_value; } } // The retrieve operator returning the square of the input stream. const value_type& operator * () const { return m_current_value; } // Increment operator: handled by incrementing the input stream. squaring_object& operator ++ () { ++m_input_stream; if (!m_input_stream.empty()) { m_current_value = *m_input_stream; m_current_value = m_current_value * m_current_value; } return *this; } // Empty indicator: this stream is empty when the input stream is. bool empty() const { return m_input_stream.empty(); } }; // define comparator class: compare right-most decimal and then absolute value struct CompareMod10 { // comparison operator() returning true if (a < b) inline bool operator () (int a, int b) const { if ((a % 10) == (b % 10)) return a < b; else return (a % 10) < (b % 10); } // smallest possible integer value int min_value() const { return INT_MIN; } // largest possible integer value int max_value() const { return INT_MAX; } }; int main() { { counter_object counter; while (!counter.empty()) { std::cout << *counter << " "; ++counter; } std::cout << std::endl; } { for (counter_object cnt; !cnt.empty(); ++cnt) { std::cout << *cnt << " "; } std::cout << std::endl; } { counter_object counter; squaring_object squares(counter); while (!squares.empty()) { std::cout << *squares << " "; ++squares; } std::cout << std::endl; } { std::vector intvector; // (fill intvector) // define stream class iterating over an integer vector typedef stxxl::stream::iterator2stream::const_iterator> intstream_type; // instantiate the stream object, iterate from begin to end of intvector. intstream_type intstream(intvector.begin(), intvector.end()); // plug in squaring object after vector iterator stream. squaring_object squares(intstream); } { stxxl::vector intvector; // (fill intvector) // define stream class iterating over an integer vector typedef stxxl::stream::vector_iterator2stream::const_iterator> intstream_type; // instantiate the stream object, iterate from begin to end of intvector. intstream_type intstream(intvector.begin(), intvector.end()); // plug in squaring object after vector iterator stream. squaring_object squares(intstream); } { // construct the squared counter stream counter_object counter; squaring_object squares(counter); // allocate vector of 100 integers std::vector intvector(100); // materialize 100 integers from stream and put into vector stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); } { // construct the squared counter stream counter_object counter; squaring_object squares(counter); // allocate STXXL vector of 100 integers stxxl::vector intvector(100); // materialize 100 integers from stream and put into STXXL vector stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); } { static const int ram_use = 10 * 1024 * 1024; // amount of memory to use in runs creation counter_object counter; // the counter stream from first examples // define a runs sorter for the counter stream which order by CompareMod10 object. typedef stxxl::stream::runs_creator rc_counter_type; // instance of CompareMod10 comparator class CompareMod10 comparemod10; // instance of runs_creator which reads the counter stream. rc_counter_type rc_counter(counter, comparemod10, ram_use); // define a runs merger for the sorted runs from rc_counter. typedef stxxl::stream::runs_merger rm_counter_type; // instance of runs_merger which merges sorted runs from rc_counter. rm_counter_type rm_counter(rc_counter.result(), comparemod10, ram_use); // read sorted stream: runs_merger also conforms to the stream interface. while (!rm_counter.empty()) { std::cout << *rm_counter << " "; ++rm_counter; } std::cout << std::endl; } { static const int ram_use = 10 * 1024 * 1024; // amount of memory to use in runs creation // define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. typedef stxxl::stream::runs_creator, CompareMod10> rc_counter_type; // instance of CompareMod10 comparator class. CompareMod10 comparemod10; // instance of runs_creator which waits for input. rc_counter_type rc_counter(comparemod10, ram_use); // write sequence of integers into runs for (int i = 1; i <= 1000; ++i) rc_counter.push(i); // define a runs merger for the sorted runs from rc_counter. typedef stxxl::stream::runs_merger rm_counter_type; // instance of runs_merger which merges sorted runs from rc_counter. rm_counter_type rm_counter(rc_counter.result(), comparemod10, ram_use); // read sorted stream: runs_merger also conforms to the stream interface. while (!rm_counter.empty()) { std::cout << *rm_counter << " "; ++rm_counter; } std::cout << std::endl; } { static const int ram_use = 10 * 1024 * 1024; // amount of memory to use in runs creation // define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. typedef stxxl::sorter sr_counter_type; // instance of CompareMod10 comparator class. CompareMod10 comparemod10; // instance of sorter which waits for input. sr_counter_type sr_counter(comparemod10, ram_use); // write sequence of integers into sorter, which creates sorted runs for (int i = 1; i <= 1000; ++i) sr_counter.push(i); // signal sorter that the input stream is finished and switch to output mode. sr_counter.sort(); // read sorted stream: sorter also conforms to the stream interface. while (!sr_counter.empty()) { std::cout << *sr_counter << " "; ++sr_counter; } std::cout << std::endl; } } stxxl-1.4.1/examples/stream/CMakeLists.txt000644 001411 000144 00000000776 12350112610 020347 0ustar00tbusers000000 000000 ############################################################################ # examples/stream/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_example(stream1) stxxl_test(stream1) stxxl-1.4.1/examples/containers/map1.cpp000644 001411 000144 00000003727 12405152404 020030 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/map1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include #define DATA_NODE_BLOCK_SIZE (4096) #define DATA_LEAF_BLOCK_SIZE (4096) //! [comparator] struct CompareGreater { bool operator () (const int& a, const int& b) const { return a > b; } static int max_value() { return std::numeric_limits::min(); } }; //! [comparator] int main() { // template parameter typedef stxxl::map map_type; // Constructor map(node_cache_size_in_bytes, leaf_cache_size_in_bytes) map_type my_map((map_type::node_block_type::raw_size)*3, (map_type::leaf_block_type::raw_size)*3); my_map.insert(std::pair(1, 'a')); my_map.insert(std::pair(2, 'b')); my_map.insert(std::pair(3, 'c')); my_map.insert(std::pair(4, 'd')); my_map.erase(3); map_type::iterator iter; std::cout << "my_map contains:\n"; for (iter = my_map.begin(); iter != my_map.end(); ++iter) { std::cout << iter->first << " => " << iter->second << std::endl; } map_type::iterator iter_low, iter_up; iter_low = my_map.lower_bound(1); // iter_low points to (1,a) in this case iter_up = my_map.upper_bound(3); // iter_up points to (2,b) in this case std::cout << "lower bound " << iter_low->second << ", upper bound " << iter_up->second << std::endl; return 0; } //! [example] stxxl-1.4.1/examples/containers/stack2.cpp000644 001411 000144 00000002626 12405152404 020356 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/stack2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include int main() { // template parameter typedef stxxl::STACK_GENERATOR::result simple_stack; // create stack instance simple_stack a_stack; stxxl::random_number<> random; stxxl::uint64 number_of_elements = 16 * 1024 * 1024; // routine: 1) push random values on stack and 2) pop all except the lowest value and start again for (int k = 0; k < 5; k++) { STXXL_MSG("push..."); for (stxxl::uint64 i = 0; i < number_of_elements; i++) { a_stack.push(random(123456789)); } STXXL_MSG("pop..."); for (stxxl::uint64 j = 0; j < number_of_elements - 1; j++) { a_stack.pop(); } } return 0; } stxxl-1.4.1/examples/containers/matrix1.cpp000644 001411 000144 00000003431 12405152404 020547 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/matrix1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include int main() { // Matrix dimensions int height = 3; int width = 3; int internal_memory = 64 * 1024 * 1024; const int small_block_order = 32; // must be multiple of matrix valueType in bits typedef stxxl::block_scheduler > block_schedular_type; typedef stxxl::matrix matrix_type; block_schedular_type my_bs(internal_memory); // Create 3 matrices with given dimensions matrix_type A(my_bs, height, width); matrix_type B(my_bs, height, width); matrix_type C(my_bs, height, width); typedef matrix_type::row_major_iterator row_iterator; int i = 0; // Fill matrix A with values 0,1,2,3,... for (row_iterator it_A = A.begin(); it_A != A.end(); ++it_A, ++i) { *it_A = i; } i = 0; // Fill matrix B with values 0,2,4,8,... for (row_iterator it_B = B.begin(); it_B != B.end(); ++it_B, ++i) { *it_B = i * 2; } // Multiply matrix A and B and store result in matrix C C = A * B; C.transpose(); // Print out matrix C for (row_iterator it_C = C.begin(); it_C != C.end(); ++it_C) { std::cout << *it_C << " "; } return 0; } //! [example] stxxl-1.4.1/examples/containers/vector1.cpp000644 001411 000144 00000001603 12405152404 020544 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/vector1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include int main() { typedef stxxl::VECTOR_GENERATOR::result vector; vector my_vector; for (int i = 0; i < 1024 * 1024; i++) { my_vector.push_back(i + 2); } std::cout << my_vector[99] << std::endl; my_vector[100] = 0; while (!my_vector.empty()) { my_vector.pop_back(); } return 0; } //! [example] stxxl-1.4.1/examples/containers/unordered_map1.cpp000644 001411 000144 00000005115 12423673670 022105 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/unordered_map1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include //! [hash] struct HashFunctor { size_t operator () (int key) const { // a simple integer hash function return (size_t)(key * 2654435761u); } }; //! [hash] //! [comparator] struct CompareLess { bool operator () (const int& a, const int& b) const { return a < b; } static int min_value() { return std::numeric_limits::min(); } static int max_value() { return std::numeric_limits::max(); } }; //! [comparator] int main() { //! [construction] #define SUB_BLOCK_SIZE 8192 #define SUB_BLOCKS_PER_BLOCK 256 // template parameter typedef stxxl::unordered_map< int, char, HashFunctor, CompareLess, SUB_BLOCK_SIZE, SUB_BLOCKS_PER_BLOCK > unordered_map_type; // constructor: use defaults for all parameters unordered_map_type my_map; //! [construction] // insert some items and delete one my_map.insert(std::make_pair(1, 'a')); my_map.insert(std::make_pair(2, 'b')); my_map.insert(std::make_pair(3, 'c')); my_map.insert(std::make_pair(4, 'd')); my_map.erase(3); // iterate over all items in the unordered_map unordered_map_type::iterator iter; std::cout << "my_map contains:\n"; for (iter = my_map.begin(); iter != my_map.end(); ++iter) { std::cout << iter->first << " => " << iter->second << std::endl; } // direct operator[] access to items std::cout << "my_map[2] = " << my_map[2] << std::endl; // efficient bulk-insert into hash map by sorting by hash keys std::vector value_array; for (int i = 0; i < 128; ++i) value_array.push_back(std::make_pair(i, (char)i)); my_map.insert(value_array.begin(), value_array.end(), 8 * 1024 * 1024); // check results of insertion std::cout << "my_map[42] = " << my_map[42] << std::endl; std::cout << "my_map.size() = " << my_map.size() << std::endl; return 0; } //! [example] stxxl-1.4.1/examples/containers/vector2.cpp000644 001411 000144 00000003157 12405152404 020553 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/vector2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include int main() { // template parameter typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type my_vector; unsigned int counter = 0; unsigned int tmp; stxxl::random_number<> rand; stxxl::uint64 number_of_elements = 32 * 1024 * 1024; // fill vector with random integers for (stxxl::uint64 i = 0; i < number_of_elements; ++i) { tmp = rand(123456789); // generate random number from the interval [0,123456789) my_vector.push_back(tmp); } // construct iterator vector_type::const_iterator iter = my_vector.begin(); // use iterator to advance my_vector and calculate number of even elements for (stxxl::uint64 j = 0; j < my_vector.size(); j++) { //std::cout << *iter << " "; if (*iter % 2 == 0) // is my_vector's current element even? { ++counter; } iter++; } STXXL_MSG("found " << counter << " even numbers in V"); return 0; } stxxl-1.4.1/examples/containers/queue2.cpp000644 001411 000144 00000003160 12405152404 020367 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/queue2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include int main() { // template parameter typedef stxxl::queue a_queue; // construct queue with default parameters a_queue my_queue; unsigned int random; stxxl::random_number32 rand32; // define random number generator stxxl::uint64 number_of_elements = 64 * 1024 * 1024; // push random values in the queue for (stxxl::uint64 i = 0; i < number_of_elements; i++) { random = rand32(); // generate random integers from interval [0,2^32) my_queue.push(random); } unsigned int last_inserted = my_queue.back(); STXXL_MSG("last element inserted: " << last_inserted); // identify smaller element than first_inserted, search in growth-direction (front->back) while (!my_queue.empty()) { if (last_inserted > my_queue.front()) { STXXL_MSG("found smaller element: " << my_queue.front() << " than last inserted element"); break; } std::cout << my_queue.front() << " " << std::endl; my_queue.pop(); } return 0; } stxxl-1.4.1/examples/containers/sequence1.cpp000644 001411 000144 00000001717 12405152404 021060 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/sequence1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include int main() { typedef stxxl::sequence sequence_type; sequence_type my_sequence; for (int i = 0; i < 100; ++i) { my_sequence.push_back(i * i); } sequence_type::stream forward_stream = my_sequence.get_stream(); while (!forward_stream.empty()) { std::cout << *forward_stream << " "; my_sequence.pop_back(); ++forward_stream; } return 0; } //! [example] stxxl-1.4.1/examples/containers/pqueue2.cpp000644 001411 000144 00000003336 12405152404 020554 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/pqueue2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include // comparison struct for priority queue where top() returns the biggest contained value: struct Cmp { bool operator () (const int& a, const int& b) const { return a < b; } int min_value() const { return std::numeric_limits::min(); } }; int main() { // use 64 GiB on main memory and 1 billion items at most typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pq_type; typedef pq_type::block_type block_type; // block_type::raw_size = 262144 bytes // use 64 block read and write pools each to enable overlapping between I/O and computation const unsigned int mem_for_pools = 32 * 1024 * 1024; stxxl::read_write_pool pool((mem_for_pools / 2) / block_type::raw_size, (mem_for_pools / 2) / block_type::raw_size); pq_type Q(pool); Q.push(1); Q.push(4); Q.push(2); Q.push(8); Q.push(5); Q.push(7); assert(Q.size() == 6); assert(Q.top() == 8); Q.pop(); assert(Q.top() == 7); Q.pop(); assert(Q.top() == 5); Q.pop(); assert(Q.top() == 4); Q.pop(); assert(Q.top() == 2); Q.pop(); assert(Q.top() == 1); Q.pop(); assert(Q.empty()); return 0; } stxxl-1.4.1/examples/containers/sorter2.cpp000644 001411 000144 00000004176 12405152404 020571 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/sorter2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include struct TwoInteger { int i, j; TwoInteger() { } TwoInteger(int _i, int _j) : i(_i), j(_j) { } }; struct TwoIntegerComparator { bool operator () (const TwoInteger& a, const TwoInteger& b) const { return a.i < b.i; } TwoInteger min_value() const { return TwoInteger(std::numeric_limits::min(), std::numeric_limits::min()); } TwoInteger max_value() const { return TwoInteger(std::numeric_limits::max(), std::numeric_limits::max()); } }; int main() { // template parameter typedef stxxl::sorter sorter_type; // create sorter object (CompareType(), MainMemoryLimit) sorter_type int_sorter(TwoIntegerComparator(), 64 * 1024 * 1024); stxxl::random_number32 rand32; stxxl::timer Timer1; Timer1.start(); // insert random numbers from [0,100000) for (size_t i = 0; i < 1000; ++i) { int_sorter.push(TwoInteger(rand32() % 100000, (int)i)); // fill sorter container } Timer1.stop(); STXXL_MSG("push time: " << (Timer1.mseconds() / 1000)); stxxl::timer Timer2; Timer2.start(); int_sorter.sort(); // switch to output state and sort Timer2.stop(); STXXL_MSG("sort time: " << (Timer2.mseconds() / 1000)); // echo sorted elements while (!int_sorter.empty()) { std::cout << int_sorter->i << " "; // access value ++int_sorter; } return 0; } stxxl-1.4.1/examples/containers/deque1.cpp000644 001411 000144 00000002541 12405152404 020347 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/deque1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include int main() { typedef stxxl::deque deque; deque my_deque; my_deque.push_front(2); my_deque.push_front(11); my_deque.push_back(5); my_deque.push_back(8); // deque now stores: |11|2|5|8| std::cout << "return 'first' element: " << my_deque.front() << std::endl; // prints 11 std::cout << "return 'last' element: " << my_deque.back() << std::endl; // prints 8 std::cout << "random access: " << my_deque[2] << std::endl; // prints 5 // generate forward iterator stxxl::deque_iterator deque_iterator = my_deque.begin(); // iterate over my_deque, access values and delete them afterwards while (!my_deque.empty()) { std::cout << *deque_iterator << " "; ++deque_iterator; my_deque.pop_front(); } return 0; } //! [example] stxxl-1.4.1/examples/containers/stack1.cpp000644 001411 000144 00000001656 12405152404 020357 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/stack1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include int main() { typedef stxxl::STACK_GENERATOR::result stack_type; stack_type my_stack; my_stack.push(8); my_stack.push(7); my_stack.push(4); assert(my_stack.size() == 3); assert(my_stack.top() == 4); my_stack.pop(); assert(my_stack.top() == 7); my_stack.pop(); assert(my_stack.top() == 8); my_stack.pop(); assert(my_stack.empty()); return 0; } //! [example] stxxl-1.4.1/examples/containers/copy_file.cpp000644 001411 000144 00000010104 12405152404 021126 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/copy_file.cpp * * This example shows three methods to copy a file to another file using * stxxl:vectors. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004-2006 Roman Dementiev * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include void copy_file(const char* input_path, const char* output_path, unsigned int method) { using stxxl::file; file::unlink(output_path); // delete output file stxxl::timer tm(true); // start a timer // input file object stxxl::syscall_file InputFile(input_path, file::RDONLY | file::DIRECT); // output file object stxxl::syscall_file OutputFile(output_path, file::RDWR | file::CREAT | file::DIRECT); typedef stxxl::vector vector_type; std::cout << "Copying file " << input_path << " to " << output_path << std::endl; // InputVector is mapped to InputFile vector_type InputVector(&InputFile); vector_type OutputVector(&OutputFile); // OutputVector is mapped to OutputFile std::cout << "File " << input_path << " has size " << InputVector.size() << " bytes." << std::endl; if (method == 1) { // First method: copy vector elements. This is rather slow, because no // prefetching is used and the vector's pager does lots of internal // work! std::cout << "Using first method: copying vector elements." << std::endl; for (vector_type::const_iterator it = InputVector.begin(); // iterate through InputVector it != InputVector.end(); ++it) { OutputVector.push_back(*it); // add the value pointed by 'it' to OutputVector } } else if (method == 2) { // Second method: use a vector_iterator2stream to prefetch blocks in // the input vector and a vector_bufwriter for buffered writing to the // output. std::cout << "Using second method: vector_iterator2stream and vector_bufwriter." << std::endl; // prepare output vector's size OutputVector.resize(InputVector.size()); // construct prefetching input stream stxxl::stream::vector_iterator2stream input(InputVector.begin(), InputVector.end()); // construct buffered output writer vector_type::bufwriter_type writer(OutputVector.begin()); while (!input.empty()) // iterate through InputVector { writer << *input; ++input; } writer.finish(); // flush buffers } else if (method == 3) { // Third method: use a vector_iterator2stream to prefetch blocks in the // input vector and materialize to write the stream to the output. std::cout << "Using third method: vector_iterator2stream and materialize." << std::endl; // prepare output vector's size OutputVector.resize(InputVector.size()); // construct prefetching input stream stxxl::stream::vector_iterator2stream input(InputVector.begin(), InputVector.end()); // materilize intput directly into output vector stxxl::stream::materialize(input, OutputVector.begin(), OutputVector.end()); } std::cout << "Copied in " << tm.seconds() << " at " << (double)InputVector.size() / tm.seconds() / 1024 / 1024 << " MiB/s" << std::endl; } int main(int argc, char* argv[]) { if (argc < 3) { std::cout << "Usage: " << argv[0] << " input_file output_file [method 1-3]" << std::endl; return -1; } int method = (argc >= 4) ? atoi(argv[3]) : 3; copy_file(argv[1], argv[2], method); return 0; } stxxl-1.4.1/examples/containers/deque2.cpp000644 001411 000144 00000003477 12411366426 020371 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/deque2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include int main() { typedef stxxl::deque deque; deque my_deque; unsigned int random, p, x; unsigned int smaller_left = 0; unsigned int smaller_right = 0; stxxl::random_number32 rand32; stxxl::uint64 number_of_elements = 8 * 1024 * 1024; // fill deque with random integer values for (stxxl::uint64 i = 0; i < number_of_elements; i++) { random = rand32(); // produce random integer from intervall [0,2^32) my_deque.push_front(random); } stxxl::deque_iterator deque_iterator = my_deque.begin(); // Access random element x at position p(x) in the deque p = (unsigned int)(rand32() % number_of_elements); x = my_deque[p]; // Count number of smaller elements from the front to p(x) - 1 for (stxxl::uint64 j = 0; j < p; j++) { if (*deque_iterator < x) { smaller_left += 1; } ++deque_iterator; } ++deque_iterator; // Count number of smaller elements from p(x) + 1 to the end for (stxxl::uint64 k = p + 1; k < number_of_elements - 1; k++) { if (*deque_iterator < x) { smaller_right += 1; } ++deque_iterator; } STXXL_MSG("smaller left: " << smaller_left << ", smaller right: " << smaller_right); return 0; } stxxl-1.4.1/examples/containers/CMakeLists.txt000644 001411 000144 00000002474 12411366426 021234 0ustar00tbusers000000 000000 ############################################################################ # examples/containers/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013-2014 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_example(copy_file) stxxl_build_example(deque1) stxxl_build_example(deque2) stxxl_build_example(map1) stxxl_build_example(matrix1) stxxl_build_example(pqueue1) stxxl_build_example(pqueue2) stxxl_build_example(queue1) stxxl_build_example(queue2) stxxl_build_example(sequence1) stxxl_build_example(sorter1) stxxl_build_example(sorter2) stxxl_build_example(stack1) stxxl_build_example(stack2) stxxl_build_example(unordered_map1) stxxl_build_example(vector1) stxxl_build_example(vector2) stxxl_build_example(vector_buf) stxxl_test(deque1) stxxl_test(deque2) stxxl_test(map1) stxxl_test(matrix1) stxxl_test(pqueue1) stxxl_test(pqueue2) stxxl_test(queue1) stxxl_test(queue2) stxxl_test(sequence1) stxxl_test(sorter1) stxxl_test(sorter2) stxxl_test(stack1) stxxl_test(stack2) stxxl_test(unordered_map1) stxxl_test(vector1) stxxl_test(vector2) stxxl_test(vector_buf) stxxl-1.4.1/examples/containers/sorter1.cpp000644 001411 000144 00000003011 12405152404 020553 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/sorter1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include #include #include struct my_comparator { bool operator () (const int& a, const int& b) const { return a < b; } int min_value() const { return std::numeric_limits::min(); } int max_value() const { return std::numeric_limits::max(); } }; int main() { // template parameter typedef stxxl::sorter sorter_type; // create sorter object (CompareType(), MainMemoryLimit) sorter_type int_sorter(my_comparator(), 64 * 1024 * 1024); // fill sorter with elements order in descending order for (int i = 1000; i > 0; i--) { int_sorter.push(i); } int_sorter.sort(); // sort elements (in ascending order) // walk through sorted values and print them out while (!int_sorter.empty()) { std::cout << *int_sorter << " "; ++int_sorter; } return 0; } //! [example] stxxl-1.4.1/examples/containers/queue1.cpp000644 001411 000144 00000002473 12405152404 020374 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/queue1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include int main() { typedef stxxl::queue queue; queue my_queue; my_queue.push(5); my_queue.push(11); my_queue.push(3); my_queue.push(7); // my_queue now stores: |7|3|11|5| assert(my_queue.size() == 4); std::cout << "back element " << my_queue.back() << std::endl; // prints out 7 (last inserted element) assert(my_queue.back() == 7); std::cout << "front element " << my_queue.front() << std::endl; // prints out 5 (first inserted element) assert(my_queue.front() == 5); my_queue.pop(); // deletes element 5, queue now stores: |7|3|11| std::cout << "front element " << my_queue.front() << std::endl; // prints out 11 assert(my_queue.front() == 11); return 0; } //! [example] stxxl-1.4.1/examples/containers/pqueue1.cpp000644 001411 000144 00000003477 12405152404 020561 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/pqueue1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Daniel Feist * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! [example] #include #include #include // comparison struct for priority queue where top() returns the smallest contained value: struct ComparatorGreater { bool operator () (const int& a, const int& b) const { return (a > b); } int min_value() const { return std::numeric_limits::max(); } }; int main() { typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pqueue_type; typedef pqueue_type::block_type block_type; // block_type::raw_size = 262144 bytes // use 64 block read and write pools each to enable overlapping between I/O and computation const unsigned int mem_for_pools = 32 * 1024 * 1024; stxxl::read_write_pool pool((mem_for_pools / 2) / block_type::raw_size, (mem_for_pools / 2) / block_type::raw_size); pqueue_type my_pqueue(pool); // creates stxxl priority queue instance with read-write-pool my_pqueue.push(5); my_pqueue.push(4); my_pqueue.push(19); my_pqueue.push(1); assert(my_pqueue.size() == 4); assert(my_pqueue.top() == 1); STXXL_MSG("Smallest inserted value in my: " << my_pqueue.top()); my_pqueue.pop(); // pop the 1 on top assert(my_pqueue.top() == 4); STXXL_MSG("Smallest value after 1 pop(): " << my_pqueue.top()); return 0; } //! [example] stxxl-1.4.1/examples/containers/vector_buf.cpp000644 001411 000144 00000006752 12405152404 021331 0ustar00tbusers000000 000000 /*************************************************************************** * examples/containers/vector_buf.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include using stxxl::uint64; void test_vector_element(uint64 size) { stxxl::scoped_print_timer tm("vector element access", 2 * size * sizeof(uint64)); //! [element] typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type vec(size); for (uint64 i = 0; i < vec.size(); ++i) vec[i] = (i % 1024); uint64 sum = 0; for (uint64 i = 0; i < vec.size(); ++i) sum += vec[i]; //! [element] std::cout << "sum: " << sum << std::endl; STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); } void test_vector_iterator(uint64 size) { stxxl::scoped_print_timer tm("vector iterator access", 2 * size * sizeof(uint64)); //! [iterator] typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type vec(size); uint64 i = 0; for (vector_type::iterator it = vec.begin(); it != vec.end(); ++it, ++i) *it = (i % 1024); uint64 sum = 0; for (vector_type::const_iterator it = vec.begin(); it != vec.end(); ++it) sum += *it; //! [iterator] std::cout << "sum: " << sum << std::endl; STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); } void test_vector_buffered(uint64 size) { stxxl::scoped_print_timer tm("vector buffered access", 2 * size * sizeof(uint64)); //! [buffered] typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type vec(size); // write using vector_bufwriter vector_type::bufwriter_type writer(vec); for (uint64 i = 0; i < vec.size(); ++i) writer << (i % 1024); // required to flush out the last block (or destruct the bufwriter) writer.finish(); // now read using vector_bufreader uint64 sum = 0; for (vector_type::bufreader_type reader(vec); !reader.empty(); ++reader) { sum += *reader; } //! [buffered] std::cout << "sum: " << sum << std::endl; STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); } #if STXXL_HAVE_CXX11_RANGE_FOR_LOOP void test_vector_cxx11(uint64 size) { stxxl::scoped_print_timer tm("vector C++11 loop access", 2 * size * sizeof(uint64)); typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type vec(size); { vector_type::bufwriter_type writer(vec); for (uint64 i = 0; i < vec.size(); ++i) writer << (i % 1024); } //! [cxx11] // now using vector_bufreader adaptor to C++11 for loop uint64 sum = 0; for (auto it : vector_type::bufreader_type(vec)) { sum += it; } //! [cxx11] std::cout << "sum: " << sum << std::endl; STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); } #endif int main(int argc, char* argv[]) { int multi = (argc >= 2 ? atoi(argv[1]) : 64); const uint64 size = multi * 1024 * uint64(1024) / sizeof(uint64); stxxl::block_manager::get_instance(); test_vector_element(size); test_vector_iterator(size); test_vector_buffered(size); #if STXXL_HAVE_CXX11_RANGE_FOR_LOOP test_vector_cxx11(size); #endif return 0; } stxxl-1.4.1/examples/algo/phonebills.cpp000644 001411 000144 00000012172 12411366426 020110 0ustar00tbusers000000 000000 /*************************************************************************** * examples/algo/phonebills.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #define USE_STXXL //! [prolog] #include // STXXL header #include // for STL std::sort #include // for STL std::vector #include // for std::fstream #include // for time_t type #define CT_PER_MIN 2 // subscribers pay 2 cent per minute struct LogEntry // the event log data structure { long long int from; // callers number (64 bit integer) long long int to; // destination number (64 bit int) time_t timestamp; // time of event int event; // event type 1 - call started // 2 - call ended }; // input operator used for reading from the file std::istream& operator >> (std::istream& i, LogEntry& entry) { i >> entry.from; i >> entry.to; i >> entry.timestamp; i >> entry.event; return i; } // output operator used for writing to file std::ostream& operator << (std::ostream& i, const LogEntry& entry) { i << entry.from << " "; i << entry.to << " "; i << entry.timestamp << " "; i << entry.event; return i; } // unary function used for producing the bills struct ProduceBill { std::ostream& out; // stream for outputting // the bills unsigned sum; // current subscribers debit LogEntry last; // the last record ProduceBill(std::ostream& o_) : out(o_), sum(0) { last.from = -1; } void operator () (const LogEntry& e) { if (last.from == e.from) { // either the last event was 'call started' and current event // is 'call ended' or the last event was 'call ended' and // current event is 'call started' assert((last.event == 1 && e.event == 2) || (last.event == 2 && e.event == 1)); if (e.event == 2) // call ended sum += CT_PER_MIN * (unsigned int)(e.timestamp - last.timestamp) / 60; } else if (last.from != -1) { assert(last.event == 2); // must be 'call ended' assert(e.event == 1); // must be 'call started' // output the total sum out << last.from << "; " << (sum / 100) << " EUR " << (sum % 100) << " ct" << std::endl; sum = 0; // reset the sum } last = e; } }; struct SortByCaller { // comparison function bool operator () (const LogEntry& a, const LogEntry& b) const { return a.from < b.from || (a.from == b.from && a.timestamp < b.timestamp) || (a.from == b.from && a.timestamp == b.timestamp && a.event < b.event); } // min sentinel = value which is strictly smaller that any input element static LogEntry min_value() { LogEntry dummy; dummy.from = std::numeric_limits::min(); dummy.timestamp = std::numeric_limits::min(); dummy.event = std::numeric_limits::min(); return dummy; } // max sentinel = value which is strictly larger that any input element static LogEntry max_value() { LogEntry dummy; dummy.from = std::numeric_limits::max(); dummy.timestamp = std::numeric_limits::max(); dummy.event = std::numeric_limits::max(); return dummy; } }; void print_usage(const char* program) { std::cout << "Usage: " << program << " logfile main billfile" << std::endl; std::cout << " logfile - file name of the input" << std::endl; std::cout << " main - memory to use (in MiB)" << std::endl; std::cout << " billfile - file name of the output" << std::endl; } //! [prolog] #ifdef USE_STXXL typedef stxxl::vector vector_type; #else typedef std::vector vector_type; #endif int main(int argc, char* argv[]) { if (argc < 4) { print_usage(argv[0]); return 0; } // read from the file std::fstream in(argv[1], std::ios::in); vector_type v; std::copy(std::istream_iterator(in), std::istream_iterator(), std::back_inserter(v)); // sort by callers #ifndef USE_STXXL std::sort(v.begin(), v.end(), SortByCaller()); std::fstream out(argv[3], std::ios::out); // output bills std::for_each(v.begin(), v.end(), ProduceBill(out)); #else const stxxl::internal_size_type M = atol(argv[2]) * 1024 * 1024; stxxl::sort(v.begin(), v.end(), SortByCaller(), M); std::fstream out(argv[3], std::ios::out); // output bills stxxl::for_each(v.begin(), v.end(), ProduceBill(out), M / vector_type::block_type::raw_size); #endif return 0; } //! [example] stxxl-1.4.1/examples/algo/copy_and_sort_file.cpp000644 001411 000144 00000006450 12411366426 021615 0ustar00tbusers000000 000000 /*************************************************************************** * examples/algo/copy_and_sort_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/copy_and_sort_file.cpp //! This example imports a file into an \c stxxl::vector without copying its //! content and then sorts it using \c stxxl::stream::sort while writing the //! sorted output to a different file. #include #include #include #include #include #include #include struct my_type { typedef unsigned key_type; key_type m_key; char m_data[128 - sizeof(key_type)]; my_type() { } my_type(key_type k) : m_key(k) { } static my_type min_value() { return my_type(std::numeric_limits::min()); } static my_type max_value() { return my_type(std::numeric_limits::max()); } }; inline bool operator < (const my_type& a, const my_type& b) { return a.m_key < b.m_key; } inline bool operator == (const my_type& a, const my_type& b) { return a.m_key == b.m_key; } struct Cmp { typedef my_type first_argument_type; typedef my_type second_argument_type; typedef bool result_type; bool operator () (const my_type& a, const my_type& b) const { return a < b; } static my_type min_value() { return my_type::min_value(); } static my_type max_value() { return my_type::max_value(); } }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.m_key; return o; } int main(int argc, char** argv) { if (argc < 3) { std::cout << "Usage: " << argv[0] << " infile outfile" << std::endl; return -1; } const stxxl::internal_size_type memory_to_use = 512 * 1024 * 1024; const stxxl::internal_size_type block_size = sizeof(my_type) * 4096; typedef stxxl::vector, block_size> vector_type; stxxl::syscall_file in_file(argv[1], stxxl::file::DIRECT | stxxl::file::RDONLY); stxxl::syscall_file out_file(argv[2], stxxl::file::DIRECT | stxxl::file::RDWR | stxxl::file::CREAT); vector_type input(&in_file); vector_type output(&out_file); output.resize(input.size()); typedef stxxl::stream::streamify_traits::stream_type input_stream_type; input_stream_type input_stream = stxxl::stream::streamify(input.begin(), input.end()); typedef Cmp comparator_type; typedef stxxl::stream::sort sort_stream_type; sort_stream_type sort_stream(input_stream, comparator_type(), memory_to_use); vector_type::iterator o = stxxl::stream::materialize(sort_stream, output.begin(), output.end()); STXXL_ASSERT(o == output.end()); if (1) { STXXL_MSG("Checking order..."); STXXL_MSG((stxxl::is_sorted(output.begin(), output.end(), comparator_type()) ? "OK" : "WRONG")); } return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/examples/algo/sort_file.cpp000644 001411 000144 00000010763 12411366426 017743 0ustar00tbusers000000 000000 /*************************************************************************** * examples/algo/sort_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/sort_file.cpp //! This example imports a file into an \c stxxl::vector without copying its //! content and then sorts it using stxxl::sort / stxxl::ksort / ... #include #include #include #include #include #include struct my_type { typedef unsigned key_type; key_type m_key; char m_data[128 - sizeof(key_type)]; key_type key() const { return m_key; } my_type() { } my_type(key_type k) : m_key(k) { } static my_type min_value() { return my_type(std::numeric_limits::min()); } static my_type max_value() { return my_type(std::numeric_limits::max()); } }; inline bool operator < (const my_type& a, const my_type& b) { return a.key() < b.key(); } inline bool operator == (const my_type& a, const my_type& b) { return a.key() == b.key(); } struct Cmp { typedef my_type first_argument_type; typedef my_type second_argument_type; typedef bool result_type; bool operator () (const my_type& a, const my_type& b) const { return a < b; } static my_type min_value() { return my_type::min_value(); } static my_type max_value() { return my_type::max_value(); } }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.key(); return o; } int main(int argc, char** argv) { if (argc < 3) { std::cout << "Usage: " << argv[0] << " action file" << std::endl; std::cout << " where action is one of generate, sort, ksort, stable_sort, stable_ksort" << std::endl; return -1; } const stxxl::unsigned_type block_size = sizeof(my_type) * 4096; if (strcmp(argv[1], "generate") == 0) { const my_type::key_type num_elements = 1 * 1024 * 1024; const stxxl::unsigned_type records_in_block = block_size / sizeof(my_type); stxxl::syscall_file f(argv[2], stxxl::file::CREAT | stxxl::file::RDWR); my_type* array = (my_type*)stxxl::aligned_alloc(block_size); memset(array, 0, block_size); my_type::key_type cur_key = num_elements; for (unsigned i = 0; i < num_elements / records_in_block; i++) { for (unsigned j = 0; j < records_in_block; j++) array[j].m_key = cur_key--; stxxl::request_ptr req = f.awrite((void*)array, stxxl::int64(i) * block_size, block_size); req->wait(); } stxxl::aligned_dealloc(array); } else { #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif stxxl::syscall_file f(argv[2], stxxl::file::DIRECT | stxxl::file::RDWR); unsigned memory_to_use = 50 * 1024 * 1024; typedef stxxl::vector, block_size> vector_type; vector_type v(&f); /* STXXL_MSG("Printing..."); for(stxxl::int64 i=0; i < v.size(); i++) STXXL_MSG(v[i].key()); */ STXXL_MSG("Checking order..."); STXXL_MSG((stxxl::is_sorted(v.begin(), v.end()) ? "OK" : "WRONG")); STXXL_MSG("Sorting..."); if (strcmp(argv[1], "sort") == 0) { stxxl::sort(v.begin(), v.end(), Cmp(), memory_to_use); #if 0 // stable_sort is not yet implemented } else if (strcmp(argv[1], "stable_sort") == 0) { stxxl::stable_sort(v.begin(), v.end(), memory_to_use); #endif } else if (strcmp(argv[1], "ksort") == 0) { stxxl::ksort(v.begin(), v.end(), memory_to_use); } else if (strcmp(argv[1], "stable_ksort") == 0) { stxxl::stable_ksort(v.begin(), v.end(), memory_to_use); } else { STXXL_MSG("Not implemented: " << argv[1]); } STXXL_MSG("Checking order..."); STXXL_MSG((stxxl::is_sorted(v.begin(), v.end()) ? "OK" : "WRONG")); } return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/examples/algo/CMakeLists.txt000644 001411 000144 00000001130 12350112610 017757 0ustar00tbusers000000 000000 ############################################################################ # examples/algo/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_example(copy_and_sort_file) stxxl_build_example(phonebills) stxxl_build_example(phonebills_genlog) stxxl_build_example(sort_file) stxxl-1.4.1/examples/algo/phonebills_genlog.cpp000644 001411 000144 00000006332 12411366426 021444 0ustar00tbusers000000 000000 /*************************************************************************** * examples/algo/phonebills_genlog.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include struct LogEntry { long long unsigned from; // callers number long long unsigned to; // destination number time_t timestamp; // time of event int event; // event type 1 - call started // 2 - call ended }; struct cmp { bool operator () (const LogEntry& a, const LogEntry& b) const { return a.timestamp < b.timestamp; } static LogEntry min_value() { LogEntry e; e.timestamp = std::numeric_limits::min(); return e; } static LogEntry max_value() { LogEntry e; e.timestamp = std::numeric_limits::max(); return e; } }; typedef stxxl::VECTOR_GENERATOR::result vector_type; std::ostream& operator << (std::ostream& i, const LogEntry& entry) { i << entry.from << " "; i << entry.to << " "; i << entry.timestamp << " "; i << entry.event << "\n"; return i; } int main(int argc, char* argv[]) { if (argc < 5) { STXXL_MSG("Usage: " << argv[0] << " ncalls avcalls main logfile"); STXXL_MSG(" ncalls - number of calls"); STXXL_MSG(" avcalls - average number of calls per client"); STXXL_MSG(" main - memory to use (in MiB)"); STXXL_MSG(" logfile - file name of the output"); return 0; } stxxl::internal_size_type M = atol(argv[3]) * 1024 * 1024; const stxxl::uint64 ncalls = stxxl::atouint64(argv[1]); const long av_calls = atol(argv[2]); const stxxl::uint64 nclients = ncalls / av_calls; stxxl::uint64 calls_made = 0; time_t now = time(NULL); vector_type log; log.reserve(2 * ncalls); stxxl::random_number64 rnd; for (stxxl::uint64 number = 0; number < nclients && calls_made < ncalls; ++number) { long long int serv = std::min(rnd(av_calls * 2), (ncalls - calls_made)); LogEntry e; e.from = number; time_t cur = now; while (serv-- > 0) { cur += (time_t)(1 + rnd(3600 * 24)); e.to = rnd(nclients); e.timestamp = cur; ++calls_made; e.event = 1; log.push_back(e); cur += (time_t)(1 + rnd(1800)); e.timestamp = cur; e.event = 2; log.push_back(e); } } // sort events in order of their appereance stxxl::sort(log.begin(), log.end(), cmp(), M); std::fstream out(argv[4], std::ios::out); std::copy(log.begin(), log.end(), std::ostream_iterator(out)); STXXL_MSG("\n" << calls_made << " calls made."); STXXL_MSG("The log is written to '" << argv[4] << "'."); return 0; } stxxl-1.4.1/examples/CMakeLists.txt000644 001411 000144 00000001113 12350112610 017036 0ustar00tbusers000000 000000 ############################################################################ # examples/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ add_subdirectory(algo) add_subdirectory(applications) add_subdirectory(common) add_subdirectory(containers) add_subdirectory(stream) stxxl-1.4.1/README000644 001411 000144 00000001471 12350112610 013347 0ustar00tbusers000000 000000 DESCRIPTION STXXL is an implementation of the C++ standard template library STL for external memory (out-of-core) computations, i. e. STXXL implements containers and algorithms that can process huge volumes of data that only fit on disks. While the closeness to the STL supports ease of use and compatibility with existing applications, another design priority is high performance. DOCUMENTATION See the Doxygen documentation for installation manual and programmer documentation: http://stxxl.sourceforge.net LICENSE TERMS STXXL is distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) BUGS AND QUESTIONS If you find any bugs or have any questions, please visit the forums at http://sourceforge.net/projects/stxxl/forums stxxl-1.4.1/tests/common/test_binary_buffer.cpp000644 001411 000144 00000004403 12405152404 021505 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_binary_buffer.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include void test1() { //! [serialize] // construct a binary blob stxxl::binary_buffer bb; { bb.put(1); bb.put_string("test"); bb.put_varint(42); bb.put_varint(12345678); // add a sub block stxxl::binary_buffer sub; sub.put_string("sub block"); sub.put_varint(6 * 9); bb.put_string(sub); } //! [serialize] // read binary block and verify content stxxl::binary_buffer_ref bbr = bb; const unsigned char bb_data[] = { // bb.put(1) 0x01, 0x00, 0x00, 0x00, // bb.put_string("test") 0x04, 0x74, 0x65, 0x73, 0x74, // bb.put_varint(42); 0x2a, // bb.put_varint(12345678); 0xce, 0xc2, 0xf1, 0x05, // begin sub block (length) 0x0b, // sub.put_string("sub block"); 0x09, 0x73, 0x75, 0x62, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, // sub.put_varint(6 * 9); 0x36, }; stxxl::binary_buffer_ref bb_verify(bb_data, sizeof(bb_data)); if (bbr != bb_verify) std::cout << bbr.str(); STXXL_CHECK(bbr == bb_verify); //! [deserialize] // read binary block using binary_reader stxxl::binary_reader br(bb); STXXL_CHECK(br.get() == 1); STXXL_CHECK(br.get_string() == "test"); STXXL_CHECK(br.get_varint() == 42); STXXL_CHECK(br.get_varint() == 12345678); { stxxl::binary_reader sub_br = br.get_binary_buffer_ref(); STXXL_CHECK(sub_br.get_string() == "sub block"); STXXL_CHECK(sub_br.get_varint() == 6 * 9); STXXL_CHECK(sub_br.empty()); } STXXL_CHECK(br.empty()); //! [deserialize] } int main(int, char**) { test1(); return 0; } stxxl-1.4.1/tests/common/test_globals.cpp000644 001411 000144 00000001075 12405152404 020315 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_globals.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl::vector global_vector; int main() { return 0; } stxxl-1.4.1/tests/common/test_counting_ptr.cpp000644 001411 000144 00000004160 12405152404 021403 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_counting_ptr.cpp * * Small test case for reference counting in stxxl::counting_ptr. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include static unsigned int count_deletes = 0; // derive from counted_object to include reference counter struct my_int : public stxxl::counted_object { int i; my_int(int _i) : i(_i) { } // count number of destructor calls ~my_int() { ++count_deletes; } }; typedef stxxl::counting_ptr int_ptr; typedef stxxl::const_counting_ptr int_cptr; int_cptr run_test() { // create object and pointer to it int_ptr i1 = new my_int(42); STXXL_CHECK(i1->i == 42); STXXL_CHECK((*i1).i == 42); STXXL_CHECK(i1.get()->i == 42); STXXL_CHECK(i1->unique()); // make pointer sharing same object int_ptr i2 = i1; STXXL_CHECK(i2->i == 42); STXXL_CHECK(!i1->unique()); STXXL_CHECK(i1 == i2); STXXL_CHECK(i1->get_reference_count() == 2); // make another pointer sharing the same object int_ptr i3 = i2; STXXL_CHECK(i3->i == 42); STXXL_CHECK(i3->get_reference_count() == 3); // replace object in i3 with new integer i3 = new my_int(5); STXXL_CHECK(i1 != i3); STXXL_CHECK(i1->get_reference_count() == 2); // create a const pointer int_cptr i4 = i3; // quitting the function will release the first object return i4; } int main() { { // run tests and return a reference counted object int_cptr t1 = run_test(); // check number of objects destructed STXXL_CHECK(count_deletes == 1); // quitting the block will release the ptr } STXXL_CHECK(count_deletes == 2); return 0; } stxxl-1.4.1/tests/common/test_manyunits2.cpp000644 001411 000144 00000001022 12405152404 020773 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_manyunits2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include int main() { return 0; } stxxl-1.4.1/tests/common/test_cmdline.cpp000644 001411 000144 00000002526 12405152404 020307 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_cmdline.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include void test1() { int a_int = 0; std::string a_str; stxxl::cmdline_parser cp; cp.add_int('i', "int", "", "an integer", a_int); cp.add_string('f', "filename", "", "a filename", a_str); cp.set_description("Command Line Parser Test"); cp.set_author("Timo Bingmann "); // good command line const char* cmdline1[] = { "test", "-i", "42", "-f", "somefile", NULL }; std::ostringstream os1; STXXL_CHECK(cp.process(5, cmdline1, os1)); STXXL_CHECK(a_int == 42); STXXL_CHECK(a_str == "somefile"); // bad command line const char* cmdline2[] = { "test", "-i", "dd", "-f", "somefile", NULL }; std::ostringstream os2; STXXL_CHECK(!cp.process(5, cmdline2, os2)); } int main(int, char**) { test1(); return 0; } stxxl-1.4.1/tests/common/test_tuple.cpp000644 001411 000144 00000004114 12405152404 020020 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_tuple.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include using stxxl::uint40; using stxxl::uint64; // force instantiation of some tuple types template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; // force instantiation of more tuple types template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; // force instantiation of even more tuple types template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; template struct stxxl::tuple; void test1() { stxxl::tuple pair; pair.first = 5; pair.second = 42; std::ostringstream oss1; oss1 << pair; STXXL_CHECK(oss1.str() == "(5,42)"); pair = stxxl::tuple(1, 2); std::ostringstream oss2; oss2 << pair; STXXL_CHECK(oss2.str() == "(1,2)"); } int main(int, char**) { test1(); return 0; } stxxl-1.4.1/tests/common/test_log2.cpp000644 001411 000144 00000006510 12405152404 017534 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_log2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include using stxxl::LOG2; using stxxl::unsigned_type; template void log_i(unsigned_type floorv, unsigned_type ceilv) { std::cout << i << "\t" << (i < 1000000 ? "\t" : "") << stxxl::LOG2_floor::value << "\t" << stxxl::LOG2::floor << "\t" << stxxl::LOG2::ceil << std::endl; std::cout << "\t\t" << stxxl::ilog2_floor(i) << "\t" << stxxl::ilog2_floor(i) << "\t" << stxxl::ilog2_ceil(i) << std::endl; STXXL_CHECK(stxxl::LOG2_floor::value == stxxl::ilog2_floor(i)); STXXL_CHECK(stxxl::LOG2::floor == stxxl::ilog2_floor(i)); STXXL_CHECK(stxxl::LOG2::ceil == stxxl::ilog2_ceil(i)); if (i <= 1) { STXXL_CHECK(stxxl::LOG2_floor::value == 0); STXXL_CHECK(stxxl::LOG2::floor == 0); STXXL_CHECK(stxxl::LOG2::ceil == 0); } else if (i == 2) { STXXL_CHECK(stxxl::LOG2_floor::value == 1); STXXL_CHECK(stxxl::LOG2::floor == 1); STXXL_CHECK(stxxl::LOG2::ceil == 1); } else { STXXL_CHECK(stxxl::LOG2_floor::value == floorv); STXXL_CHECK(stxxl::LOG2::floor == floorv); STXXL_CHECK(stxxl::LOG2::ceil == ceilv); #if 0 // not many compiler have log2l() if (i <= ((stxxl::uint64)(1) << 59)) // does not work for higher powers { STXXL_CHECK(stxxl::LOG2_floor::value == (unsigned_type)floorl(log2l(i))); STXXL_CHECK(stxxl::LOG2::floor == (unsigned_type)floorl(log2l(i))); STXXL_CHECK(stxxl::LOG2::ceil == (unsigned_type)ceill(log2l(i))); } #endif } std::cout << "\n"; } template void log_ipm1(unsigned_type p) { log_i(p - 1, p); log_i(p, p); log_i(p, p + 1); std::cout << std::endl; } int main() { std::cout << "i\t\tLOG2\tLOG2\tLOG2" << std::endl; std::cout << "\t\t\tfloor\tceil" << std::endl; log_ipm1<1 << 0>(0); log_ipm1<1 << 1>(1); log_ipm1<1 << 2>(2); log_ipm1<1 << 3>(3); log_ipm1<1 << 4>(4); log_ipm1<1 << 5>(5); log_ipm1<1 << 6>(6); log_ipm1<1 << 7>(7); log_ipm1<1 << 8>(8); log_ipm1<1 << 9>(9); log_ipm1<1 << 10>(10); log_ipm1<1 << 11>(11); log_ipm1<1 << 12>(12); log_ipm1<1 << 16>(16); log_ipm1<1 << 24>(24); log_ipm1<1 << 30>(30); log_ipm1<1UL << 31>(31); #if __WORDSIZE == 64 log_ipm1<1UL << 32>(32); log_ipm1<1UL << 33>(33); log_ipm1<1UL << 48>(48); log_ipm1<1UL << 50>(50); log_ipm1<1UL << 55>(55); log_ipm1<1UL << 63>(63); #endif } stxxl-1.4.1/tests/common/CMakeLists.txt000644 001411 000144 00000002175 12411366426 017701 0ustar00tbusers000000 000000 ############################################################################ # tests/common/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013-2014 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(test_binary_buffer) stxxl_build_test(test_cmdline) stxxl_build_test(test_counting_ptr) if(USE_BOOST) stxxl_build_test(test_external_shared_ptr) endif(USE_BOOST) stxxl_build_test(test_globals) stxxl_build_test(test_log2) stxxl_build_test(test_manyunits test_manyunits2) stxxl_build_test(test_random) stxxl_build_test(test_tuple) stxxl_build_test(test_uint_types) stxxl_test(test_binary_buffer) stxxl_test(test_cmdline) stxxl_test(test_counting_ptr) if(USE_BOOST) stxxl_test(test_external_shared_ptr) endif(USE_BOOST) stxxl_test(test_globals) stxxl_test(test_log2) stxxl_test(test_manyunits) stxxl_test(test_random) stxxl_test(test_tuple) stxxl_test(test_uint_types) stxxl-1.4.1/tests/common/test_random.cpp000644 001411 000144 00000003320 12405152404 020145 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_random.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007, 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include int main() { //stxxl::set_seed(42); std::cout << "seed = " << stxxl::get_next_seed() << std::endl; stxxl::srandom_number32(stxxl::get_next_seed()); stxxl::random_number32 random_number32; stxxl::random_number32_r random_number32_r; stxxl::random_uniform_fast random_uniform_fast; stxxl::random_uniform_slow random_uniform_slow; stxxl::random_number<> random_number_fast; stxxl::random_number random_number_slow; stxxl::random_number64 random_number64; for (int i = 0; i < 3; ++i) { std::cout << "r32 " << random_number32() << std::endl; std::cout << "r3r " << random_number32_r() << std::endl; std::cout << "ruf " << random_uniform_fast() << std::endl; std::cout << "rus " << random_uniform_slow() << std::endl; std::cout << "rnf " << random_number_fast(42) << std::endl; std::cout << "rns " << random_number_slow(42) << std::endl; std::cout << "r64 " << random_number64() << std::endl; std::cout << std::endl; } stxxl::set_seed(42); STXXL_CHECK(stxxl::get_next_seed() == 42); STXXL_CHECK(stxxl::get_next_seed() != 42); } stxxl-1.4.1/tests/common/test_manyunits.cpp000644 001411 000144 00000000763 12405152404 020724 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_manyunits.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include stxxl-1.4.1/tests/common/test_uint_types.cpp000644 001411 000144 00000003641 12405152404 021076 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_uint_types.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include // forced instantiation template class stxxl::uint_pair; template class stxxl::uint_pair; template void dotest(unsigned int nbytes) { // simple initialize uint a = 42; // check sizeof (again) STXXL_CHECK(sizeof(a) == nbytes); // count up 1024 and down again uint b = 0xFFFFFF00; uint b_save = b; stxxl::uint64 b64 = b; for (stxxl::uint32 i = 0; i < 1024; ++i) { STXXL_CHECK(b.u64() == b64); STXXL_CHECK(b.ull() == b64); STXXL_CHECK(b != a); ++b, ++b64; } STXXL_CHECK(b != b_save); for (stxxl::uint32 i = 0; i < 1024; ++i) { STXXL_CHECK(b.u64() == b64); STXXL_CHECK(b.ull() == b64); STXXL_CHECK(b != a); --b, --b64; } STXXL_CHECK(b.u64() == b64); STXXL_CHECK(b.ull() == b64); STXXL_CHECK(b == b_save); // check min and max value STXXL_CHECK(uint::min() <= a); STXXL_CHECK(uint::max() >= a); STXXL_CHECK(std::numeric_limits::min() < a); STXXL_CHECK(std::numeric_limits::max() > a); // check simple math a = 42; a = a + a; STXXL_CHECK(a == uint(84)); STXXL_CHECK(a.ull() == 84); a += uint(0xFFFFFF00); STXXL_CHECK(a.ull() == 84 + (unsigned long long)(0xFFFFFF00)); } int main() { dotest(5); dotest(6); return 0; } stxxl-1.4.1/tests/common/test_external_shared_ptr.cpp000644 001411 000144 00000017351 12411366426 022743 0ustar00tbusers000000 000000 /*************************************************************************** * tests/common/test_external_shared_ptr.cpp * * This file has been derived from the following tests written * by Roman Dementiev: * - test_vector.cpp * - test_map.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Daniel Godas-Lopez * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include #include struct actual_element // 24 bytes, not a power of 2 intentionally { stxxl::int64 key; stxxl::int64 load0; stxxl::int64 load1; actual_element& operator = (stxxl::int64 i) { key = i; load0 = i + 42; load1 = i ^ 42; return *this; } bool operator == (const actual_element& e2) const { return key == e2.key && load0 == e2.load0 && load1 == e2.load1; } }; typedef boost::shared_ptr actual_element_ptr; typedef stxxl::external_shared_ptr element; struct counter { int value; counter(int v) : value(v) { } int operator () () { int old_val = value; value++; return old_val; } }; template void test_const_iterator(const my_vec_type& x) { typename my_vec_type::const_iterator i = x.begin(); i = x.end() - 1; i.block_externally_updated(); i.flush(); i++; ++i; --i; i--; *i; } void test_vector() { // use non-randomized striping to avoid side effects on random generator typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type v(64 * 1024 * 1024 / sizeof(element)); // test assignment const_iterator = iterator vector_type::const_iterator c_it = v.begin(); STXXL_UNUSED(c_it); test_const_iterator(v); stxxl::random_number32 rnd; int offset = rnd(); STXXL_MSG("write " << v.size() << " elements"); stxxl::ran32State = 0xdeadbeef; vector_type::size_type i; // fill the vector with increasing sequence of integer numbers for (i = 0; i < v.size(); ++i) { actual_element_ptr aep(boost::make_shared()); aep->key = i + offset; element e(aep); v[i] = e; STXXL_CHECK(v[i].get()->key == stxxl::int64(i + offset)); } // fill the vector with random numbers for (i = 0; i < v.size(); ++i) { actual_element_ptr aep(boost::make_shared()); aep->key = rnd(); element e(aep); v[i].unwrap(); v[i] = e; STXXL_CHECK(v[i].get()->key == aep->key); } v.flush(); STXXL_MSG("seq read of " << v.size() << " elements"); stxxl::ran32State = 0xdeadbeef; // testing swap vector_type a; std::swap(v, a); std::swap(v, a); for (i = 0; i < v.size(); i++) STXXL_CHECK(v[i].get()->key == rnd()); // check again STXXL_MSG("clear"); for (vector_type::iterator it = v.begin(); it != v.end(); ++it) it->unwrap(); v.clear(); stxxl::ran32State = 0xdeadbeef + 10; v.resize(64 * 1024 * 1024 / sizeof(element)); STXXL_MSG("write " << v.size() << " elements"); for (i = 0; i < v.size(); ++i) { actual_element_ptr aep(boost::make_shared()); aep->key = rnd(); element e(aep); v[i] = e; STXXL_CHECK(v[i].get()->key == aep->key); } stxxl::ran32State = 0xdeadbeef + 10; STXXL_MSG("seq read of " << v.size() << " elements"); for (i = 0; i < v.size(); i++) STXXL_CHECK(v[i].get()->key == rnd()); STXXL_MSG("copy vector of " << v.size() << " elements"); vector_type v_copy0(v); STXXL_CHECK(v == v_copy0); vector_type v_copy1; v_copy1 = v; STXXL_CHECK(v == v_copy1); while (v.size() != 0) { element e = v.back(); v.pop_back(); e.unwrap(); } } typedef size_t key_type; struct test_data { unsigned char a; unsigned long b[3]; unsigned int c; }; typedef boost::shared_ptr test_data_ptr; typedef stxxl::external_shared_ptr data_type; struct cmp : public std::less { static key_type min_value() { return (std::numeric_limits::min)(); } static key_type max_value() { return (std::numeric_limits::max)(); } }; #define BLOCK_SIZE (32 * 1024) #define CACHE_SIZE (2 * 1024 * 1024 / BLOCK_SIZE) #define CACHE_ELEMENTS (BLOCK_SIZE * CACHE_SIZE / (sizeof(key_type) + sizeof(data_type))) typedef stxxl::map map_type; void test_map() { const unsigned max_mult = 8; stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); stxxl::stats_data stats_elapsed; STXXL_MSG(stats_begin); STXXL_MSG("Block size " << BLOCK_SIZE / 1024 << " KiB"); STXXL_MSG("Cache size " << (CACHE_SIZE * BLOCK_SIZE) / 1024 << " KiB"); for (unsigned mult = 1; mult < max_mult; mult *= 2) { stats_begin = *stxxl::stats::get_instance(); const size_t el = mult * (CACHE_ELEMENTS / 8); STXXL_MSG("Elements to insert " << el << " volume =" << (el * (sizeof(key_type) + sizeof(data_type))) / 1024 << " KiB"); map_type* DMap = new map_type(CACHE_SIZE * BLOCK_SIZE / 2, CACHE_SIZE * BLOCK_SIZE / 2); map_type& Map = *DMap; for (size_t i = 0; i < el; ++i) { test_data_ptr test = boost::make_shared(); test->a = (unsigned char)(i + 1); for (unsigned j = 0; j < 3; j++) test->b[j] = (unsigned long)(i + 2); test->c = (unsigned int)(i + 3); data_type data(test); Map[i] = data; } stats_elapsed = stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; double writes = double(stats_elapsed.get_writes()) / double(el); double logel = log(double(el)) / log(double(BLOCK_SIZE)); STXXL_MSG("Logs: writes " << writes << " logel " << logel << " writes/logel " << (writes / logel)); STXXL_MSG(stats_elapsed); stats_begin = *stxxl::stats::get_instance(); STXXL_MSG("Doing search"); size_t queries = el; stxxl::random_number32 myrandom; for (unsigned i = 0; i < queries; ++i) { key_type key = myrandom() % el; map_type::iterator result = Map.find(key); data_type data = (*result).second; test_data_ptr tmp = data.get(); STXXL_CHECK(tmp->a == (unsigned char)(key + 1)); for (unsigned j = 0; j < 3; ++j) STXXL_CHECK(tmp->b[j] == (unsigned long)(key + 2)); STXXL_CHECK(tmp->c == (unsigned int)(key + 3)); } stats_elapsed = stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; double reads = double(stats_elapsed.get_reads()) / logel; double readsperq = double(stats_elapsed.get_reads()) / (double)queries; STXXL_MSG("reads/logel " << reads << " readsperq " << readsperq); STXXL_MSG(stats_elapsed); while (Map.size() != 0) { map_type::iterator it = Map.begin(); data_type data = (*it).second; Map.erase(it); data.unwrap(); } delete DMap; } } int main() { test_vector(); test_map(); return 0; } stxxl-1.4.1/tests/mng/test_aligned.cpp000644 001411 000144 00000003405 12405152404 017565 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_aligned.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #define STXXL_VERBOSE_LEVEL 0 #define STXXL_VERBOSE_ALIGNED_ALLOC STXXL_VERBOSE0 #define STXXL_VERBOSE_TYPED_BLOCK STXXL_VERBOSE0 #include #include #include #define BLOCK_SIZE (512 * 1024) struct type { int i; ~type() { } }; typedef stxxl::typed_block block_type; template class stxxl::typed_block; // forced instantiation void test_typed_block() { block_type* a = new block_type; block_type* b = new block_type; block_type* A = new block_type[4]; block_type* B = new block_type[1]; block_type* C = NULL; C = new block_type[0]; delete a; a = b; b = 0; //-tb delete of NULL is a noop //delete b; delete a; delete[] A; delete[] B; delete[] C; } void test_aligned_alloc() { void* p = stxxl::aligned_alloc<1024>(4096); void* q = NULL; void* r = stxxl::aligned_alloc<1024>(4096, 42); stxxl::aligned_dealloc<1024>(p); stxxl::aligned_dealloc<1024>(q); stxxl::aligned_dealloc<1024>(r); } void test_typed_block_vector() { std::vector v1(2); std::vector > v2(2); } int main() { test_typed_block(); test_aligned_alloc(); test_typed_block_vector(); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/mng/test_buf_streams.cpp000644 001411 000144 00000005430 12405375303 020501 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_buf_streams.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example mng/test_buf_streams.cpp //! This is an example of use of \c stxxl::buf_istream and \c stxxl::buf_ostream #include #include #include #include #include #define BLOCK_SIZE (1024 * 512) typedef stxxl::typed_block block_type; typedef stxxl::BIDArray::iterator bid_iterator_type; typedef stxxl::buf_ostream buf_ostream_type; typedef stxxl::buf_istream buf_istream_type; typedef stxxl::buf_istream_reverse buf_istream_reverse_type; // forced instantiations template class stxxl::buf_ostream::iterator>; template class stxxl::buf_istream::iterator>; template class stxxl::buf_istream_reverse::iterator>; int main() { const unsigned nblocks = 128; const unsigned nelements = nblocks * block_type::size; stxxl::BIDArray bids(nblocks); stxxl::block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(stxxl::striping(), bids.begin(), bids.end()); { buf_ostream_type out(bids.begin(), 2); for (unsigned i = 0; i < nelements; i++) out << i; } { buf_istream_type in(bids.begin(), bids.end(), 2); for (unsigned i = 0; i < nelements; i++) { unsigned prevalue = *in; unsigned value; in >> value; STXXL_CHECK2(value == i, "Error at position " << std::hex << i << " (" << value << ") block " << (i / block_type::size)); STXXL_CHECK(prevalue == value); } } { buf_istream_reverse_type in(bids.begin(), bids.end(), 2); for (unsigned i = 0; i < nelements; i++) { unsigned prevalue = *in; unsigned value; in >> value; STXXL_CHECK2(value == nelements - i - 1, "Error at position " << std::hex << i << " (" << value << ") block " << (i / block_type::size)); STXXL_CHECK(prevalue == value); } } bm->delete_blocks(bids.begin(), bids.end()); return 0; } stxxl-1.4.1/tests/mng/test_pool_pair.cpp000644 001411 000144 00000007461 12405152404 020154 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_pool_pair.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example mng/test_pool_pair.cpp #include #include #include #include #define BLOCK_SIZE (1024 * 512) struct MyType { int integer; char chars[5]; }; typedef stxxl::typed_block block_type; // forced instantiation template class stxxl::typed_block; template class stxxl::prefetch_pool; template class stxxl::write_pool; int main() { stxxl::block_manager* bm = stxxl::block_manager::get_instance(); STXXL_DEFAULT_ALLOC_STRATEGY alloc; { STXXL_MSG("Write-After-Write coherence test"); stxxl::prefetch_pool p_pool(2); stxxl::write_pool w_pool(10); block_type* blk; block_type::bid_type bid; bm->new_block(alloc, bid); // write the block for the first time blk = w_pool.steal(); (*blk)[0].integer = 42; w_pool.write(blk, bid); // read the block blk = w_pool.steal(); p_pool.read(blk, bid)->wait(); delete blk; // write the block for the second time blk = w_pool.steal(); (*blk)[0].integer = 23; w_pool.write(blk, bid); // hint the block p_pool.hint(bid, w_pool); // flush w_pool // get the hinted block blk = w_pool.steal(); p_pool.read(blk, bid)->wait(); STXXL_CHECK2((*blk)[0].integer == 23, "WRITE-AFTER-WRITE COHERENCE FAILURE"); w_pool.add(blk); bm->delete_block(bid); } { STXXL_MSG("Write-After-Hint coherence test #1"); stxxl::prefetch_pool p_pool(1); stxxl::write_pool w_pool(1); block_type* blk; block_type::bid_type bid; bm->new_block(alloc, bid); blk = w_pool.steal(); (*blk)[0].integer = 42; w_pool.write(blk, bid); blk = w_pool.steal(); // flush w_pool // hint the block p_pool.hint(bid); // update the hinted block (*blk)[0].integer = 23; w_pool.write(blk, bid); p_pool.invalidate(bid); blk = w_pool.steal(); // flush w_pool // get the hinted block p_pool.read(blk, bid)->wait(); STXXL_CHECK2((*blk)[0].integer == 23, "WRITE-AFTER-HINT COHERENCE FAILURE"); w_pool.add(blk); bm->delete_block(bid); } { STXXL_MSG("Write-After-Hint coherence test #2"); stxxl::prefetch_pool p_pool(1); stxxl::write_pool w_pool(1); block_type* blk; block_type::bid_type bid; bm->new_block(alloc, bid); blk = w_pool.steal(); (*blk)[0].integer = 42; w_pool.write(blk, bid); // hint the block p_pool.hint(bid, w_pool); // flush w_pool // update the hinted block blk = w_pool.steal(); (*blk)[0].integer = 23; w_pool.write(blk, bid); p_pool.invalidate(bid); blk = w_pool.steal(); // flush w_pool // get the hinted block p_pool.read(blk, bid)->wait(); STXXL_CHECK2((*blk)[0].integer == 23, "WRITE-AFTER-HINT COHERENCE FAILURE"); w_pool.add(blk); bm->delete_block(bid); } return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/mng/test_block_alloc_strategy.cpp000644 001411 000144 00000004701 12405152404 022350 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_block_alloc_strategy.cpp * * instantiate all block allocation strategies * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include template void test_strategy() { STXXL_MSG(STXXL_PRETTY_FUNCTION_NAME); strategy s0; strategy s2(1, 3); stxxl::offset_allocator o(1, s0); typedef typename stxxl::interleaved_alloc_traits::strategy interleaved; interleaved itl(23, strategy(1, 3)); for (int i = 0; i < 16; ++i) STXXL_MSG(s0(i) << " " << s2(i) << " " << o(i) << " " << itl(i)); int firstdisk = 0; int ndisks = 4; int nruns = 10; int runsize = 15; std::cout << "firstdisk=" << firstdisk << " ndisks=" << ndisks << " nruns=" << nruns << " runsize=" << runsize; interleaved itl2(nruns, strategy(firstdisk, firstdisk + ndisks)); for (int i = 0; i < nruns * runsize; ++i) { if (i % nruns == 0) std::cout << std::endl; std::cout << itl2(i) << " "; } std::cout << std::endl; } int main() { stxxl::config* cfg = stxxl::config::get_instance(); // instantiate the allocation strategies STXXL_MSG("Number of disks: " << cfg->disks_number()); for (unsigned i = 0; i < cfg->disks_number(); ++i) STXXL_MSG(cfg->disk_path(i) << ", " << cfg->disk_size(i) << ", " << cfg->disk_io_impl(i)); test_strategy(); test_strategy(); test_strategy(); test_strategy(); STXXL_MSG("Regular disks: [" << cfg->regular_disk_range().first << "," << cfg->regular_disk_range().second << ")"); if (cfg->regular_disk_range().first != cfg->regular_disk_range().second) test_strategy(); STXXL_MSG("Flash devices: [" << cfg->flash_range().first << "," << cfg->flash_range().second << ")"); if (cfg->flash_range().first != cfg->flash_range().second) test_strategy(); test_strategy(); } stxxl-1.4.1/tests/mng/test_block_manager2.cpp000644 001411 000144 00000004336 12411366426 021044 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_block_manager2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #define BLOCK_SIZE (1024 * 1024 * 32) typedef stxxl::typed_block block_type; template class stxxl::typed_block; // forced instantiation int main() { stxxl::int64 totalsize = 0; stxxl::config* config = stxxl::config::get_instance(); for (size_t i = 0; i < config->disks_number(); ++i) totalsize += config->disk_size(i); stxxl::unsigned_type totalblocks = (stxxl::unsigned_type)(totalsize / block_type::raw_size); STXXL_MSG("external memory: " << totalsize << " bytes == " << totalblocks << " blocks"); stxxl::BIDArray b5a(totalblocks / 5); stxxl::BIDArray b5b(totalblocks / 5); stxxl::BIDArray b5c(totalblocks / 5); stxxl::BIDArray b5d(totalblocks / 5); stxxl::BIDArray b2(totalblocks / 2); stxxl::block_manager* bm = stxxl::block_manager::get_instance(); STXXL_MSG("get 4 x " << totalblocks / 5); bm->new_blocks(stxxl::striping(), b5a.begin(), b5a.end()); bm->new_blocks(stxxl::striping(), b5b.begin(), b5b.end()); bm->new_blocks(stxxl::striping(), b5c.begin(), b5c.end()); bm->new_blocks(stxxl::striping(), b5d.begin(), b5d.end()); STXXL_MSG("free 2 x " << totalblocks / 5); bm->delete_blocks(b5a.begin(), b5a.end()); bm->delete_blocks(b5c.begin(), b5c.end()); // the external memory should now be fragmented enough, // s.t. the following request needs to be split into smaller ones STXXL_MSG("get 1 x " << totalblocks / 2); bm->new_blocks(stxxl::striping(), b2.begin(), b2.end()); bm->delete_blocks(b5b.begin(), b5b.end()); bm->delete_blocks(b5d.begin(), b5d.end()); bm->delete_blocks(b2.begin(), b2.end()); } stxxl-1.4.1/tests/mng/test_read_write_pool.cpp000644 001411 000144 00000006716 12405152404 021350 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_read_write_pool.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example mng/test_read_write_pool.cpp #include #include #include #define BLOCK_SIZE (1024 * 512) struct MyType { int integer; char chars[5]; }; typedef stxxl::typed_block block_type; // forced instantiation template class stxxl::typed_block; template class stxxl::read_write_pool; int main() { stxxl::block_manager* bm = stxxl::block_manager::get_instance(); STXXL_DEFAULT_ALLOC_STRATEGY alloc; { STXXL_MSG("Write-After-Write coherence test"); stxxl::read_write_pool pool(2, 10); block_type* blk; block_type::bid_type bid; bm->new_block(alloc, bid); // write the block for the first time blk = pool.steal(); (*blk)[0].integer = 42; pool.write(blk, bid); // read the block blk = pool.steal(); pool.read(blk, bid)->wait(); delete blk; // write the block for the second time blk = pool.steal(); (*blk)[0].integer = 23; pool.write(blk, bid); // hint the block pool.hint(bid); // flush w_pool // get the hinted block blk = pool.steal(); pool.read(blk, bid)->wait(); STXXL_CHECK2((*blk)[0].integer == 23, "WRITE-AFTER-WRITE COHERENCE FAILURE"); pool.add(blk); bm->delete_block(bid); } { STXXL_MSG("Write-After-Hint coherence test #1"); stxxl::read_write_pool pool(1, 1); block_type* blk; block_type::bid_type bid; bm->new_block(alloc, bid); blk = pool.steal(); (*blk)[0].integer = 42; pool.write(blk, bid); blk = pool.steal(); // flush w_pool // hint the block pool.hint(bid); // update the hinted block (*blk)[0].integer = 23; pool.write(blk, bid); blk = pool.steal(); // flush w_pool // get the hinted block pool.read(blk, bid)->wait(); STXXL_CHECK2((*blk)[0].integer == 23, "WRITE-AFTER-HINT COHERENCE FAILURE"); pool.add(blk); bm->delete_block(bid); } { STXXL_MSG("Write-After-Hint coherence test #2"); stxxl::read_write_pool pool(1, 1); block_type* blk; block_type::bid_type bid; bm->new_block(alloc, bid); blk = pool.steal(); (*blk)[0].integer = 42; pool.write(blk, bid); // hint the block pool.hint(bid); // update the hinted block blk = pool.steal(); (*blk)[0].integer = 23; pool.write(blk, bid); blk = pool.steal(); // flush w_pool // get the hinted block pool.read(blk, bid)->wait(); STXXL_CHECK2((*blk)[0].integer == 23, "WRITE-AFTER-HINT COHERENCE FAILURE"); pool.add(blk); bm->delete_block(bid); } return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/mng/test_block_manager1.cpp000644 001411 000144 00000002503 12405375303 021032 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_block_manager1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include int main() { typedef stxxl::typed_block<128* 1024, double> block_type; std::vector bids; std::vector requests; stxxl::block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(32, stxxl::striping(), std::back_inserter(bids)); std::vector > blocks(32); int vIndex; for (vIndex = 0; vIndex < 32; ++vIndex) { for (int vIndex2 = 0; vIndex2 < block_type::size; ++vIndex2) { blocks[vIndex][vIndex2] = vIndex2; } } for (vIndex = 0; vIndex < 32; ++vIndex) { requests.push_back(blocks[vIndex].write(bids[vIndex])); } stxxl::wait_all(requests.begin(), requests.end()); bm->delete_blocks(bids.begin(), bids.end()); return 0; } stxxl-1.4.1/tests/mng/test_block_manager.cpp000644 001411 000144 00000005667 12405375303 020767 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_block_manager.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example mng/test_mng.cpp //! This is an example of use of completion handlers, \c stxxl::block_manager, and //! \c stxxl::typed_block #include #include #include #include #define BLOCK_SIZE (1024 * 512) struct MyType { int integer; //char chars[4]; ~MyType() { } }; struct my_handler { void operator () (stxxl::request* req) { STXXL_MSG(req << " done, type=" << req->io_type()); } }; typedef stxxl::typed_block block_type; int main() { STXXL_MSG(sizeof(MyType) << " " << (BLOCK_SIZE % sizeof(MyType))); STXXL_MSG(sizeof(block_type) << " " << BLOCK_SIZE); const unsigned nblocks = 2; stxxl::BIDArray bids(nblocks); std::vector disks(nblocks, 2); stxxl::request_ptr* reqs = new stxxl::request_ptr[nblocks]; stxxl::block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(stxxl::striping(), bids.begin(), bids.end()); block_type* block = new block_type[2]; STXXL_MSG(std::hex); STXXL_MSG("Allocated block address : " << (stxxl::unsigned_type)(block)); STXXL_MSG("Allocated block address + 1: " << (stxxl::unsigned_type)(block + 1)); STXXL_MSG(std::dec); unsigned i = 0; for (i = 0; i < block_type::size; ++i) { block->elem[i].integer = i; //memcpy (block->elem[i].chars, "STXXL", 4); } for (i = 0; i < nblocks; ++i) reqs[i] = block->write(bids[i], my_handler()); std::cout << "Waiting " << std::endl; stxxl::wait_all(reqs, nblocks); for (i = 0; i < nblocks; ++i) { reqs[i] = block->read(bids[i], my_handler()); reqs[i]->wait(); for (int j = 0; j < block_type::size; ++j) { STXXL_CHECK2(j == block->elem[j].integer, "Error in block " << std::hex << i << " pos: " << j << " value read: " << block->elem[j].integer); } } bm->delete_blocks(bids.begin(), bids.end()); delete[] reqs; delete[] block; #if 0 // variable-size blocks, not supported currently BIDArray<0> vbids(nblocks); for (i = 0; i < nblocks; i++) vbids[i].size = 1024 + i; bm->new_blocks(striping(), vbids.begin(), vbids.end()); for (i = 0; i < nblocks; i++) STXXL_MSG("Allocated block: offset=" << vbids[i].offset << ", size=" << vbids[i].size); bm->delete_blocks(vbids.begin(), vbids.end()); #endif } stxxl-1.4.1/tests/mng/test_config.cpp000644 001411 000144 00000005712 12405152404 017432 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_config.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include void test1() { // test disk_config parser: stxxl::disk_config cfg; cfg.parse_line("disk=/var/tmp/stxxl.tmp, 100 GiB , syscall unlink direct=on"); STXXL_CHECK(cfg.path == "/var/tmp/stxxl.tmp"); STXXL_CHECK(cfg.size == 100 * 1024 * 1024 * stxxl::uint64(1024)); STXXL_CHECK(cfg.fileio_string() == "syscall direct=on unlink_on_open"); // test disk_config parser: cfg.parse_line("disk=/var/tmp/stxxl.tmp, 100 , wincall queue=5 delete_on_exit direct=on"); STXXL_CHECK(cfg.path == "/var/tmp/stxxl.tmp"); STXXL_CHECK(cfg.size == 100 * 1024 * stxxl::uint64(1024)); STXXL_CHECK(cfg.fileio_string() == "wincall delete_on_exit direct=on queue=5"); STXXL_CHECK(cfg.queue == 5); STXXL_CHECK(cfg.direct == stxxl::disk_config::DIRECT_ON); // bad configurations STXXL_CHECK_THROW( cfg.parse_line("disk=/var/tmp/stxxl.tmp, 100 GiB, wincall_fileperblock unlink direct=on"), std::runtime_error ); STXXL_CHECK_THROW( cfg.parse_line("disk=/var/tmp/stxxl.tmp,0x,syscall"), std::runtime_error ); } void test2() { #if !STXXL_WINDOWS // test user-supplied configuration stxxl::config* config = stxxl::config::get_instance(); { stxxl::disk_config disk1("/tmp/stxxl-1.tmp", 100 * 1024 * 1024, "syscall"); disk1.unlink_on_open = true; disk1.direct = stxxl::disk_config::DIRECT_OFF; STXXL_CHECK(disk1.path == "/tmp/stxxl-1.tmp"); STXXL_CHECK(disk1.size == 100 * 1024 * stxxl::uint64(1024)); STXXL_CHECK(disk1.fileio_string() == "syscall direct=off unlink_on_open"); config->add_disk(disk1); stxxl::disk_config disk2("/tmp/stxxl-2.tmp", 200 * 1024 * 1024, "syscall direct=off"); disk2.unlink_on_open = true; STXXL_CHECK(disk2.path == "/tmp/stxxl-2.tmp"); STXXL_CHECK(disk2.size == 200 * 1024 * stxxl::uint64(1024)); STXXL_CHECK(disk2.fileio_string() == "syscall direct=off unlink_on_open"); STXXL_CHECK(disk2.direct == 0); config->add_disk(disk2); } STXXL_CHECK(config->disks_number() == 2); STXXL_CHECK(config->total_size() == 300 * 1024 * 1024); // construct block_manager with user-supplied config stxxl::block_manager* bm = stxxl::block_manager::get_instance(); STXXL_CHECK(bm->get_total_bytes() == 300 * 1024 * 1024); STXXL_CHECK(bm->get_free_bytes() == 300 * 1024 * 1024); #endif } int main() { test1(); test2(); return 0; } stxxl-1.4.1/tests/mng/test_bmlayer.cpp000644 001411 000144 00000011715 12405375303 017625 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_bmlayer.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #define BLOCK_SIZE (1024 * 512) struct MyType { int integer; char chars[5]; }; struct my_handler { void operator () (stxxl::request* req) { STXXL_MSG(req << " done, type=" << req->io_type()); } }; typedef stxxl::typed_block block_type; void testIO() { const unsigned nblocks = 2; stxxl::BIDArray bids(nblocks); std::vector disks(nblocks, 2); stxxl::request_ptr* reqs = new stxxl::request_ptr[nblocks]; stxxl::block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(stxxl::striping(), bids.begin(), bids.end()); block_type* block = new block_type; STXXL_MSG(std::hex); STXXL_MSG("Allocated block address : " << (stxxl::unsigned_type)(block)); STXXL_MSG("Allocated block address + 1: " << (stxxl::unsigned_type)(block + 1)); STXXL_MSG(std::dec); unsigned i = 0; for (i = 0; i < block_type::size; ++i) { block->elem[i].integer = i; //memcpy (block->elem[i].chars, "STXXL", 4); } for (i = 0; i < nblocks; ++i) reqs[i] = block->write(bids[i], my_handler()); std::cout << "Waiting " << std::endl; stxxl::wait_all(reqs, nblocks); for (i = 0; i < nblocks; ++i) { reqs[i] = block->read(bids[i], my_handler()); reqs[i]->wait(); for (int j = 0; j < block_type::size; ++j) { STXXL_CHECK(j == block->elem[j].integer); } } bm->delete_blocks(bids.begin(), bids.end()); delete[] reqs; delete block; } void testIO2() { typedef stxxl::typed_block<128* 1024, double> block_type; std::vector bids; std::vector requests; stxxl::block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(32, stxxl::striping(), std::back_inserter(bids)); block_type* blocks = new block_type[32]; int vIndex; for (vIndex = 0; vIndex < 32; ++vIndex) { for (int vIndex2 = 0; vIndex2 < block_type::size; ++vIndex2) { blocks[vIndex][vIndex2] = vIndex2; } } for (vIndex = 0; vIndex < 32; ++vIndex) { requests.push_back(blocks[vIndex].write(bids[vIndex])); } stxxl::wait_all(requests.begin(), requests.end()); bm->delete_blocks(bids.begin(), bids.end()); delete[] blocks; } void testPrefetchPool() { stxxl::prefetch_pool pool(2); pool.resize(10); pool.resize(5); block_type* blk = new block_type; block_type::bid_type bid; stxxl::block_manager::get_instance()->new_block(stxxl::single_disk(), bid); pool.hint(bid); pool.read(blk, bid)->wait(); delete blk; } void testWritePool() { stxxl::write_pool pool(100); pool.resize(10); pool.resize(5); block_type* blk = new block_type; block_type::bid_type bid; stxxl::block_manager::get_instance()->new_block(stxxl::single_disk(), bid); pool.write(blk, bid); } typedef stxxl::typed_block block_type1; typedef stxxl::buf_ostream::iterator> buf_ostream_type; typedef stxxl::buf_istream::iterator> buf_istream_type; void testStreams() { const unsigned nblocks = 64; const unsigned nelements = nblocks * block_type1::size; stxxl::BIDArray bids(nblocks); stxxl::block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(stxxl::striping(), bids.begin(), bids.end()); { buf_ostream_type out(bids.begin(), 2); for (unsigned i = 0; i < nelements; i++) out << i; } { buf_istream_type in(bids.begin(), bids.end(), 2); for (unsigned i = 0; i < nelements; i++) { int value; in >> value; STXXL_CHECK(value == int(i)); } } bm->delete_blocks(bids.begin(), bids.end()); } int main() { testIO(); testIO2(); testPrefetchPool(); testWritePool(); testStreams(); #if STXXL_MNG_COUNT_ALLOCATION stxxl::block_manager* bm = stxxl::block_manager::get_instance(); STXXL_MSG("block_manager total allocation: " << bm->get_total_allocation()); STXXL_MSG("block_manager current allocation: " << bm->get_current_allocation()); STXXL_MSG("block_manager maximum allocation: " << bm->get_maximum_allocation()); #endif // STXXL_MNG_COUNT_ALLOCATION } stxxl-1.4.1/tests/mng/CMakeLists.txt000644 001411 000144 00000003060 12350112610 017146 0ustar00tbusers000000 000000 ############################################################################ # tests/mng/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(test_aligned) stxxl_build_test(test_block_alloc_strategy) stxxl_build_test(test_block_manager) stxxl_build_test(test_block_manager1) stxxl_build_test(test_block_manager2) stxxl_build_test(test_block_scheduler) stxxl_build_test(test_bmlayer) stxxl_build_test(test_buf_streams) stxxl_build_test(test_config) stxxl_build_test(test_pool_pair) stxxl_build_test(test_prefetch_pool) stxxl_build_test(test_read_write_pool) stxxl_build_test(test_write_pool) stxxl_test(test_aligned) stxxl_test(test_block_alloc_strategy) stxxl_test(test_block_manager) stxxl_test(test_block_manager1) stxxl_test(test_block_manager2) stxxl_test(test_block_scheduler) stxxl_test(test_bmlayer) stxxl_test(test_buf_streams) stxxl_test(test_config) stxxl_test(test_pool_pair) stxxl_test(test_prefetch_pool) stxxl_test(test_read_write_pool) stxxl_test(test_write_pool) add_define(test_block_manager "STXXL_VERBOSE_LEVEL=2") add_define(test_block_manager1 "STXXL_VERBOSE_LEVEL=2") add_define(test_block_manager2 "STXXL_VERBOSE_LEVEL=3") add_define(test_prefetch_pool "STXXL_VERBOSE_LEVEL=2") add_define(test_write_pool "STXXL_VERBOSE_LEVEL=1") stxxl-1.4.1/tests/mng/test_prefetch_pool.cpp000644 001411 000144 00000002541 12405152404 021013 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_prefetch_pool.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example mng/test_prefetch_pool.cpp #include #include #include #define BLOCK_SIZE (1024 * 512) struct MyType { int integer; char chars[5]; }; typedef stxxl::typed_block block_type; // forced instantiation template class stxxl::prefetch_pool; int main() { stxxl::prefetch_pool pool(2); pool.resize(10); pool.resize(5); block_type* blk = new block_type; (*blk)[0].integer = 42; block_type::bid_type bids[2]; stxxl::block_manager::get_instance()->new_blocks(stxxl::single_disk(), bids, bids + 2); blk->write(bids[0])->wait(); blk->write(bids[1])->wait(); pool.hint(bids[0]); pool.read(blk, bids[0])->wait(); pool.read(blk, bids[1])->wait(); delete blk; } stxxl-1.4.1/tests/mng/test_write_pool.cpp000644 001411 000144 00000002332 12405152404 020343 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_write_pool.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example mng/test_write_pool.cpp #include #include #include #define BLOCK_SIZE (1024 * 512) struct MyType { int integer; char chars[5]; }; typedef stxxl::typed_block block_type; // forced instantiation template class stxxl::typed_block; template class stxxl::write_pool; int main() { stxxl::write_pool pool(100); pool.resize(10); pool.resize(5); block_type* blk = new block_type; block_type::bid_type bid; stxxl::block_manager::get_instance()->new_block(stxxl::single_disk(), bid); pool.write(blk, bid)->wait(); delete blk; } stxxl-1.4.1/tests/mng/test_block_scheduler.cpp000644 001411 000144 00000027566 12405152404 021330 0ustar00tbusers000000 000000 /*************************************************************************** * tests/mng/test_block_scheduler.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include using stxxl::int_type; using stxxl::unsigned_type; // forced instantiation template class stxxl::block_scheduler >; template void set_pattern_A(IBT& ib) { for (int_type i = 0; i < ib.size; ++i) ib[i] = i; } template int_type test_pattern_A(IBT& ib) { int_type num_err = 0; for (int_type i = 0; i < ib.size; ++i) num_err += (ib[i] != i); return num_err; } template void set_pattern_B(IBT& ib) { for (int_type i = 0; i < ib.size; ++i) ib[i] = ib.size - i; } template int_type test_pattern_B(IBT& ib) { int_type num_err = 0; for (int_type i = 0; i < ib.size; ++i) num_err += (ib[i] != ib.size - i); return num_err; } const int block_size = 1024; typedef int_type value_type; unsigned_type internal_memory = 0; typedef stxxl::block_scheduler > block_scheduler_type; typedef stxxl::swappable_block swappable_block_type; typedef block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; typedef block_scheduler_type::internal_block_type internal_block_type; typedef block_scheduler_type::external_block_type external_block_type; void test1() { // ------------------- call all functions ----------------------- STXXL_MSG("first test: call all functions"); // prepare an external_block with pattern A external_block_type ext_bl; stxxl::block_manager::get_instance()->new_block(stxxl::striping(), ext_bl); internal_block_type* int_bl = new internal_block_type; set_pattern_A(*int_bl); int_bl->write(ext_bl)->wait(); // the block_scheduler may use internal_memory byte for caching block_scheduler_type* bs_ptr = new block_scheduler_type(internal_memory); block_scheduler_type& bs = *bs_ptr; // make sure is not just recording a prediction sequence STXXL_CHECK(! bs.is_simulating()); // allocate a swappable_block and store its identifier swappable_block_identifier_type sbi1 = bs.allocate_swappable_block(); STXXL_CHECK2(!bs.is_initialized(sbi1), "new block should not be initialized?"); // initialize the swappable_block with the prepared external_block bs.initialize(sbi1, ext_bl); { // acquire the swappable_block to gain access internal_block_type& ib = bs.acquire(sbi1); // read from the swappable_block. it should contain pattern A int_type num_err = 0; for (int_type i = 0; i < block_size; ++i) num_err += (ib[i] != i); STXXL_CHECK2(num_err == 0, "previously initialized block had " << num_err << " errors."); } { // get a new reference to the already allocated block (because we forgot the old one) internal_block_type& ib = bs.get_internal_block(sbi1); // write pattern B for (int_type i = 0; i < block_size; ++i) ib[i] = block_size - i; // release the swappable_block. changes have to be stored. it may now be swapped out. bs.release(sbi1, true); } // allocate a second swappable_block and store its identifier swappable_block_identifier_type sbi2 = bs.allocate_swappable_block(); STXXL_CHECK2(!bs.is_initialized(sbi2), "new block should not be initialized?"); { // acquire the swappable_block to gain access internal_block_type& ib1 = bs.acquire(sbi1); // acquire the swappable_block to gain access because it was uninitialized, it now becomes initialized internal_block_type& ib2 = bs.acquire(sbi2); // copy pattern B for (int_type i = 0; i < block_size; ++i) ib2[i] = ib1[i]; // release the swappable_block. no changes happened. bs.release(sbi1, false); bs.release(sbi2, true); } // both blocks should now be initialized STXXL_CHECK2(bs.is_initialized(sbi1), "block should not be initialized!"); STXXL_CHECK2(bs.is_initialized(sbi2), "block should not be initialized!"); // get the external_block ext_bl = bs.extract_external_block(sbi2); STXXL_CHECK2(!bs.is_initialized(sbi2), "block should not be initialized after extraction!"); // free external block 1 bs.deinitialize(sbi1); STXXL_CHECK2(!bs.is_initialized(sbi1), "block should not be initialized after deinitialize!"); // free the swappable_blocks bs.free_swappable_block(sbi1); bs.free_swappable_block(sbi2); bs.explicit_timestep(); // switch to simulation mode stxxl::block_scheduler_algorithm_simulation* asim = new stxxl::block_scheduler_algorithm_simulation(bs); delete bs.switch_algorithm_to(asim); // allocate swappable block swappable_block_identifier_type sbi = bs.allocate_swappable_block(); // perform acquire and release sequence bs.acquire(sbi); bs.acquire(sbi); bs.release(sbi, true); bs.explicit_timestep(); bs.release(sbi, false); bs.deinitialize(sbi); bs.initialize(sbi, external_block_type()); if (bs.is_simulating()) bs.extract_external_block(sbi); else bs.extract_external_block(sbi); bs.free_swappable_block(sbi); // output prediction sequence if (true) { block_scheduler_type::prediction_sequence_type ps = bs.get_prediction_sequence(); for (block_scheduler_type::prediction_sequence_type::iterator it = ps.begin(); it != ps.end(); ++it) STXXL_MSG("id: " << it->id << " op: " << it->op << " t: " << it->time); } // switch to LFD processing delete bs.switch_algorithm_to( new stxxl::block_scheduler_algorithm_offline_lfd(asim)); sbi = bs.allocate_swappable_block(); bs.acquire(sbi); bs.acquire(sbi); bs.release(sbi, true); bs.explicit_timestep(); bs.release(sbi, false); bs.deinitialize(sbi); bs.initialize(sbi, external_block_type()); if (bs.is_simulating()) bs.extract_external_block(sbi); else bs.extract_external_block(sbi); bs.free_swappable_block(sbi); #if 0 // 2013-tb: segfaults due to missing prediction sequence? TODO delete bs.switch_algorithm_to( new stxxl::block_scheduler_algorithm_offline_lru_prefetching(asim)); sbi = bs.allocate_swappable_block(); bs.acquire(sbi); bs.acquire(sbi); bs.release(sbi, true); bs.explicit_timestep(); bs.release(sbi, false); bs.deinitialize(sbi); bs.initialize(sbi, external_block_type()); if (bs.is_simulating()) bs.extract_external_block(sbi); else bs.extract_external_block(sbi); bs.free_swappable_block(sbi); #endif delete bs_ptr; int_bl->read(ext_bl)->wait(); STXXL_CHECK2(test_pattern_B(*int_bl) == 0, "after extraction changed block should contain pattern B."); delete int_bl; } void test2() { // ---------- force swapping --------------------- STXXL_MSG("next test: force swapping"); const int_type num_sb = 5; // only 3 internal_blocks allowed block_scheduler_type* bs_ptr = new block_scheduler_type(block_size * sizeof(value_type) * 3); block_scheduler_type& bs = *bs_ptr; // allocate blocks swappable_block_identifier_type sbi[num_sb]; for (int_type i = 0; i < num_sb; ++i) sbi[i] = bs.allocate_swappable_block(); // fill 3 blocks internal_block_type* ib[num_sb]; ib[0] = &bs.acquire(sbi[0]); ib[1] = &bs.acquire(sbi[1]); ib[2] = &bs.acquire(sbi[2]); set_pattern_A(*ib[0]); set_pattern_A(*ib[1]); set_pattern_A(*ib[2]); bs.release(sbi[0], true); bs.release(sbi[1], true); bs.release(sbi[2], true); // fill 2 blocks, now some others have to be swapped out ib[3] = &bs.acquire(sbi[3]); ib[4] = &bs.acquire(sbi[4]); set_pattern_A(*ib[3]); set_pattern_A(*ib[4]); bs.release(sbi[3], true); bs.release(sbi[4], true); ib[2] = &bs.acquire(sbi[2]); // this block can still be cached ib[3] = &bs.acquire(sbi[3]); // as can this ib[1] = &bs.acquire(sbi[1]); // but not this STXXL_CHECK2(test_pattern_A(*ib[1]) == 0, "Block 1 had errors."); STXXL_CHECK2(test_pattern_A(*ib[2]) == 0, "Block 2 had errors."); STXXL_CHECK2(test_pattern_A(*ib[3]) == 0, "Block 3 had errors."); bs.release(sbi[1], false); bs.release(sbi[2], false); bs.release(sbi[3], false); // free blocks for (int_type i = 0; i < num_sb; ++i) bs.free_swappable_block(sbi[i]); delete bs_ptr; } void test3() { { // ---------- do not free uninitialized block --------------------- STXXL_MSG("next test: do not free uninitialized block"); block_scheduler_type bs(block_size * sizeof(value_type)); swappable_block_identifier_type sbi; sbi = bs.allocate_swappable_block(); bs.acquire(sbi); bs.release(sbi, false); // do not free uninitialized block } { // ---------- do not free initialized block --------------------- STXXL_MSG("next test: do not free initialized block"); block_scheduler_type bs(block_size * sizeof(value_type)); swappable_block_identifier_type sbi; sbi = bs.allocate_swappable_block(); bs.acquire(sbi); bs.release(sbi, true); // do not free initialized block } if (0) //-tb: causes assertion (which is the expected behaviour)! { // ---------- do not release but free block --------------------- STXXL_MSG("next test: do not release but free block"); block_scheduler_type bs(block_size * sizeof(value_type)); swappable_block_identifier_type sbi; sbi = bs.allocate_swappable_block(); bs.acquire(sbi); // do not release block bs.free_swappable_block(sbi); } { // ---------- do neither release nor free block --------------------- STXXL_MSG("next test: do neither release nor free block"); block_scheduler_type bs(block_size * sizeof(value_type)); swappable_block_identifier_type sbi; sbi = bs.allocate_swappable_block(); bs.acquire(sbi); // do not release block // do not free block } if (0) //-tb: causes assertion (which is the expected behaviour)! { // ---------- release block to often --------------------- STXXL_MSG("next test: release block to often"); block_scheduler_type bs(block_size * sizeof(value_type)); swappable_block_identifier_type sbi; sbi = bs.allocate_swappable_block(); bs.acquire(sbi); bs.release(sbi, false); bs.release(sbi, false); // release once to often } } int main(int argc, char** argv) { int test_case = -1; int internal_memory_megabytes = 256; stxxl::cmdline_parser cp; cp.add_int('t', "test-case", "I", "number of the test case to run", test_case); cp.add_int('m', "memory", "N", "internal memory to use (in megabytes)", internal_memory_megabytes); cp.set_description("stxxl block_scheduler test"); cp.set_author("Raoul Steffen, R-Steffen@gmx.de"); if (!cp.process(argc, argv)) return -1; internal_memory = unsigned_type(internal_memory_megabytes) * 1048576; // run individual tests test1(); test2(); test3(); STXXL_MSG("end of test"); return 0; } stxxl-1.4.1/tests/stream/test_sorted_runs.cpp000644 001411 000144 00000007054 12405375303 021254 0ustar00tbusers000000 000000 /*************************************************************************** * tests/stream/test_sorted_runs.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009, 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example stream/test_sorted_runs.cpp //! This is an example of how to use some basic algorithms from //! stream package. This example shows how to create //! \c sorted_runs data structure from sorted sequences //! using \c stream::from_sorted_sequences specialization of \c stream::runs_creator class #include #include const unsigned long long megabyte = 1024 * 1024; const int block_size = 1 * megabyte; typedef unsigned value_type; struct Cmp : public std::binary_function { typedef unsigned value_type; bool operator () (const value_type& a, const value_type& b) const { return a < b; } value_type min_value() { return std::numeric_limits::min(); } value_type max_value() { return std::numeric_limits::max(); } }; int main() { #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif // special parameter type typedef stxxl::stream::from_sorted_sequences InputType; typedef stxxl::stream::runs_creator CreateRunsAlg; typedef CreateRunsAlg::sorted_runs_type SortedRunsType; unsigned input_size = (50 * megabyte / sizeof(value_type)); Cmp c; CreateRunsAlg SortedRuns(c, 10 * megabyte); value_type checksum_before(0); stxxl::random_number32 rnd; stxxl::random_number<> rnd_max; for (unsigned cnt = input_size; cnt > 0; ) { unsigned run_size = rnd_max(cnt) + 1; // random run length cnt -= run_size; STXXL_MSG("current run size: " << run_size); std::vector tmp(run_size); // create temp storage for current run // fill with random numbers std::generate(tmp.begin(), tmp.end(), rnd _STXXL_FORCE_SEQUENTIAL); std::sort(tmp.begin(), tmp.end(), c); // sort for (unsigned j = 0; j < run_size; ++j) { checksum_before += tmp[j]; SortedRuns.push(tmp[j]); // push sorted values to the current run } SortedRuns.finish(); // finish current run } SortedRunsType Runs = SortedRuns.result(); // get sorted_runs data structure STXXL_CHECK(check_sorted_runs(Runs, Cmp())); // merge the runs stxxl::stream::runs_merger merger(Runs, Cmp(), 10 * megabyte); stxxl::vector, block_size, STXXL_DEFAULT_ALLOC_STRATEGY> array; STXXL_MSG(input_size << " " << Runs->elements); STXXL_MSG("checksum before: " << checksum_before); value_type checksum_after(0); for (unsigned i = 0; i < input_size; ++i) { checksum_after += *merger; array.push_back(*merger); ++merger; } STXXL_MSG("checksum after: " << checksum_after); STXXL_CHECK(stxxl::is_sorted(array.begin(), array.end(), Cmp())); STXXL_CHECK(checksum_before == checksum_after); STXXL_CHECK(merger.empty()); return 0; } stxxl-1.4.1/tests/stream/test_materialize.cpp000644 001411 000144 00000010771 12405152404 021206 0ustar00tbusers000000 000000 /*************************************************************************** * tests/stream/test_materialize.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include struct forty_two { unsigned counter, length; forty_two(unsigned l) : counter(0), length(l) { } bool empty() const { return !(counter < length); } unsigned len() const { return length; } int operator * () { STXXL_CHECK(!empty()); return counter; } forty_two& operator ++ () { STXXL_CHECK(!empty()); ++counter; return *this; } forty_two & reset() { counter = 0; return *this; } }; /* template OutputIterator_ materialize(StreamAlgorithm_ & in, OutputIterator_ out); template OutputIterator_ materialize(StreamAlgorithm_ & in, OutputIterator_ outbegin, OutputIterator_ outend); template stxxl::vector_iterator materialize(StreamAlgorithm_ & in, stxxl::vector_iterator outbegin, stxxl::vector_iterator outend, unsigned_type nbuffers = 0); template stxxl::vector_iterator materialize(StreamAlgorithm_ & in, stxxl::vector_iterator out, unsigned_type nbuffers = 0); */ int generate_0() { return 0; } template void check_42_fill(VectorType& v, unsigned length) { typename VectorType::const_iterator ci = v.begin(); for (unsigned i = 0; i < length; ++i) { STXXL_CHECK(*ci == (int)i); ++ci; } for (unsigned i = length; i < v.size(); ++i) { STXXL_CHECK(*ci == 0); ++ci; } std::fill(v.begin(), v.end(), 0); } int main() { stxxl::config::get_instance(); { forty_two _42(42); // materialize into std vector std::vector v(1000); std::generate(v.begin(), v.end(), generate_0); stxxl::stream::materialize(_42.reset(), v.begin()); check_42_fill(v, _42.len()); stxxl::stream::materialize(_42.reset(), v.begin(), v.end()); check_42_fill(v, _42.len()); } { forty_two _42(42); // materialize into stxxl vector stxxl::VECTOR_GENERATOR::result v(1000); stxxl::generate(v.begin(), v.end(), generate_0, 42); stxxl::stream::materialize(_42.reset(), v.begin()); check_42_fill(v, _42.len()); stxxl::stream::materialize(_42.reset(), v.begin(), 42); check_42_fill(v, _42.len()); stxxl::stream::materialize(_42.reset(), v.begin(), v.end()); check_42_fill(v, _42.len()); stxxl::stream::materialize(_42.reset(), v.begin(), v.end(), 42); check_42_fill(v, _42.len()); } { forty_two _42mill(42 * 1000000); // materialize into larger stxxl vector (to cross block boundaries) stxxl::VECTOR_GENERATOR::result v(60 * 1000000); stxxl::generate(v.begin(), v.end(), generate_0, 42); stxxl::stream::materialize(_42mill.reset(), v.begin()); check_42_fill(v, _42mill.len()); stxxl::stream::materialize(_42mill.reset(), v.begin(), 42); check_42_fill(v, _42mill.len()); stxxl::stream::materialize(_42mill.reset(), v.begin(), v.end()); check_42_fill(v, _42mill.len()); stxxl::stream::materialize(_42mill.reset(), v.begin(), v.end(), 42); check_42_fill(v, _42mill.len()); } } stxxl-1.4.1/tests/stream/test_loop.cpp000644 001411 000144 00000016043 12411366426 017657 0ustar00tbusers000000 000000 /*************************************************************************** * tests/stream/test_loop.cpp * * example for building a loop of stream operations * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Jaroslaw Fedorowicz * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example stream/test_loop.cpp //! This is an example of how to use some basic algorithms from the //! stream package to form a loop iterating over the data. //! Some input is generated, sorted, some elements are filtered out. //! The remaining elements are transformed, sorted and processed in the //! next pass. The loop will terminate if at most one element remains. //! A split sorter is used to cut the data flow (and type dependency) cycle. #include #include #include #include #include using std::cout; using std::endl; const stxxl::uint64 memory_to_use = 128ul * 1024 * 1024; bool verbose; struct random_generator { typedef stxxl::random_number32::value_type value_type; typedef stxxl::uint64 size_type; value_type current; size_type count; stxxl::random_number32 rnd; random_generator(size_type _count) : count(_count) { if (verbose) cout << "Random Stream: "; current = rnd(); } value_type operator * () const { return current; } random_generator& operator ++ () { count--; if (verbose) { cout << current << ", "; if (empty()) cout << endl; } current = rnd(); return *this; } bool empty() const { return (count == 0); } }; template struct Cmp : std::binary_function { bool operator () (const value_type& a, const value_type& b) const { return a < b; } static value_type min_value() { return value_type(std::numeric_limits::min()); } static value_type max_value() { return value_type(std::numeric_limits::max()); } }; template struct filter { typedef typename Input::value_type value_type; typedef stxxl::uint64 size_type; Input& input; value_type filter_value; size_type& counter; void apply_filter() { while (!input.empty() && *input == filter_value) { ++input; counter++; } } filter(Input& _input, value_type _filter_value, size_type& _counter) : input(_input), filter_value(_filter_value), counter(_counter) { apply_filter(); } const value_type operator * () const { return *input; } filter& operator ++ () { ++input; apply_filter(); return *this; } bool empty() const { return input.empty(); } }; template struct output { typedef typename Input::value_type value_type; Input& input; output(Input& _input) : input(_input) { } const value_type operator * () const { return *input; } output& operator ++ () { if (verbose) cout << *input << ", "; ++input; if (empty() && verbose) cout << endl; return *this; } bool empty() const { return input.empty(); } }; template struct shuffle { typedef typename Input::value_type value_type; Input& input; value_type current, next; bool even, is_empty; // from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan int count_bits(stxxl::uint64 v) { int c; for (c = 0; v; c++) { v &= v - 1; } return c; } void apply_shuffle() { is_empty = input.empty(); if (!is_empty) { current = *input; ++input; if (!input.empty()) { STXXL_STATIC_ASSERT(sizeof(value_type) == 4); stxxl::uint64 combined = current; combined = combined << 32 | *input; combined = (1ul << count_bits(combined)) - 1; current = (value_type)(combined >> 32); next = (value_type)combined; } } } shuffle(Input& _input) : input(_input), current(0), next(0), even(true), is_empty(false) { apply_shuffle(); } value_type operator * () const { return current; } shuffle& operator ++ () { even = !even; is_empty = input.empty(); if (even) { ++input; apply_shuffle(); } else { current = next; } return *this; } bool empty() const { return is_empty; } }; typedef random_generator input_generator_type; typedef Cmp cmp; typedef stxxl::stream::runs_creator runs_creator_type0; typedef runs_creator_type0::sorted_runs_type sorted_runs_type; typedef stxxl::stream::runs_merger runs_merger_type; typedef output output_type; typedef filter filter_type0; typedef filter filter_type1; typedef shuffle shuffle_type; typedef stxxl::stream::runs_creator runs_creator_type1; // force instantiation of whole chain template class stxxl::stream::runs_creator; int main(int argc, char** argv) { if (argc < 2) { cout << "Usage: " << argv[0] << " count [Options]\nOptions: -v \t prints elements of each iteration\n"; return EXIT_FAILURE; } stxxl::block_manager::get_instance(); verbose = (argc == 3) && !strcmp(argv[2], "-v"); stxxl::uint64 total = stxxl::atouint64(argv[1]); input_generator_type input_stream(total); runs_creator_type0 runs_creator(input_stream, cmp(), memory_to_use); sorted_runs_type sorted_runs = runs_creator.result(); stxxl::uint64 counter = 0; int i; for (i = 0; counter < total - 1; ++i) { if (verbose) cout << "Iteration " << i << ": "; runs_merger_type runs_merger(sorted_runs, cmp(), memory_to_use); output_type output_stream(runs_merger); filter_type0 filter0(output_stream, 0, counter); filter_type1 filter1(filter0, filter_type1::value_type(-1), counter); shuffle_type shuffled_stream(filter1); runs_creator_type1 runs_creator(shuffled_stream, cmp(), memory_to_use); sorted_runs = runs_creator.result(); } runs_merger_type runs_merger(sorted_runs, cmp(), memory_to_use); while (!runs_merger.empty()) { if (verbose) cout << "Iteration " << i << ": " << *runs_merger << endl; ++runs_merger; } cout << "\nIteration needed: " << i << endl; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/stream/test_stream.cpp000644 001411 000144 00000016242 12411366426 020202 0ustar00tbusers000000 000000 /*************************************************************************** * tests/stream/test_stream.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2007 Andreas Beckmann * Copyright (C) 2009, 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example stream/test_stream.cpp //! This is an example of how to use some basic algorithms from the //! stream package. The example sorts characters of a string producing an //! array of sorted tuples (character, index position). #include #include #include #include #define USE_FORMRUNS_N_MERGE // comment if you want to use one 'sort' algorithm // without producing intermediate sorted runs. #define USE_EXTERNAL_ARRAY // comment if you want to use internal vectors as // input/output of the algorithm #define block_size (8 * 1024) typedef stxxl::tuple tuple_type; namespace std { std::ostream& operator << (std::ostream& os, const tuple_type& t) { os << "<" << t.first << "," << t.second << ">"; return os; } } // namespace std #ifdef USE_EXTERNAL_ARRAY typedef stxxl::VECTOR_GENERATOR::result input_array_type; typedef stxxl::VECTOR_GENERATOR::result output_array_type; #else typedef std::vector input_array_type; typedef std::vector output_array_type; #endif using stxxl::stream::streamify; using stxxl::stream::streamify_traits; using stxxl::stream::make_tuple; using stxxl::tuple; const char* phrase = "Hasta la vista, baby"; template void fill_input_array(Container_& container, It_ p) { while (*p) { container.push_back(*p); ++p; } } template struct counter { typedef ValTp value_type; value_type cnt; counter() : cnt(0) { } value_type operator () () { value_type ret = cnt; ++cnt; return ret; } }; typedef counter counter_type; struct cmp_type : std::binary_function { typedef tuple_type value_type; bool operator () (const value_type& a, const value_type& b) const { if (a.first == b.first) return a.second < b.second; return a.first < b.first; } value_type min_value() const { return tuple_type::min_value(); } value_type max_value() const { return tuple_type::max_value(); } }; struct cmp_int : std::binary_function { typedef int value_type; bool operator () (const value_type& a, const value_type& b) const { return a > b; } value_type max_value() const { return std::numeric_limits::min(); } value_type min_value() const { return std::numeric_limits::max(); } }; template struct identity : std::unary_function { typedef T value_type; const T& operator () (const T& t) { return t; } }; int main() { input_array_type input; output_array_type output; stxxl::stats* s = stxxl::stats::get_instance(); std::cout << *s; fill_input_array(input, phrase); output.resize(input.size()); // HERE streaming part begins (streamifying) // create input stream typedef streamify_traits::stream_type input_stream_type; input_stream_type input_stream = streamify(input.begin(), input.end()); // create counter stream typedef stxxl::stream::generator2stream counter_stream_type; counter_stream_type counter_stream = streamify(counter_type()); // create tuple stream typedef make_tuple tuple_stream_type; tuple_stream_type tuple_stream(input_stream, counter_stream); const stxxl::unsigned_type sorter_memory = 128 * 1024; #ifdef USE_FORMRUNS_N_MERGE // sort tuples by character // 1. form runs typedef stxxl::stream::runs_creator runs_creator_stream_type; runs_creator_stream_type runs_creator_stream(tuple_stream, cmp_type(), sorter_memory); // 2. merge runs typedef stxxl::stream::runs_merger sorted_stream_type; sorted_stream_type sorted_stream(runs_creator_stream.result(), cmp_type(), sorter_memory); #else // sort tuples by character // (combination of the previous two steps in one algorithm: form runs and merge) typedef stxxl::stream::sort sorted_stream_type; sorted_stream_type sorted_stream(tuple_stream, cmp_type(), sorter_memory); #endif typedef stxxl::stream::transform >, sorted_stream_type> transformed_stream_type; identity > id; transformed_stream_type transformed_stream(id, sorted_stream); // HERE streaming part ends (materializing) output_array_type::iterator o = stxxl::stream::materialize(transformed_stream, output.begin(), output.end()); // or materialize(sorted_stream,output.begin()); STXXL_CHECK(o == output.end()); STXXL_MSG("input string (character,position) :"); for (unsigned i = 0; i < input.size(); ++i) { STXXL_MSG("('" << input[i] << "'," << i << ")"); } STXXL_MSG("sorted tuples (character,position):"); for (unsigned i = 0; i < input.size(); ++i) { STXXL_MSG("('" << output[i].first << "'," << output[i].second << ")"); } STXXL_CHECK(output[0] == tuple_type(' ', 5)); STXXL_CHECK(output[1] == tuple_type(' ', 8)); STXXL_CHECK(output[2] == tuple_type(' ', 15)); STXXL_CHECK(output[3] == tuple_type(',', 14)); STXXL_CHECK(output[4] == tuple_type('H', 0)); STXXL_CHECK(output[5] == tuple_type('a', 1)); STXXL_CHECK(output[6] == tuple_type('a', 4)); STXXL_CHECK(output[7] == tuple_type('a', 7)); STXXL_CHECK(output[8] == tuple_type('a', 13)); STXXL_CHECK(output[9] == tuple_type('a', 17)); STXXL_CHECK(output[10] == tuple_type('b', 16)); STXXL_CHECK(output[11] == tuple_type('b', 18)); STXXL_CHECK(output[12] == tuple_type('i', 10)); STXXL_CHECK(output[13] == tuple_type('l', 6)); STXXL_CHECK(output[14] == tuple_type('s', 2)); STXXL_CHECK(output[15] == tuple_type('s', 11)); STXXL_CHECK(output[16] == tuple_type('t', 3)); STXXL_CHECK(output[17] == tuple_type('t', 12)); STXXL_CHECK(output[18] == tuple_type('v', 9)); STXXL_CHECK(output[19] == tuple_type('y', 19)); STXXL_CHECK(output.size() == 20); std::cout << *s; std::vector InternalArray(1024 * 1024); std::sort(InternalArray.begin(), InternalArray.end(), cmp_int()); //convenience function based on streaming stxxl::sort<1024* 1024>(InternalArray.begin(), InternalArray.end(), cmp_int(), 1024 * 1024 * 31, stxxl::RC()); return 0; } stxxl-1.4.1/tests/stream/test_naive_transpose.cpp000644 001411 000144 00000012473 12411366426 022111 0ustar00tbusers000000 000000 /*************************************************************************** * tests/stream/test_naive_transpose.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example stream/test_naive_transpose.cpp //! This is an example of how to use some basic algorithms from the //! stream package. The example transposes a 2D-matrix which is serialized //! as an 1D-vector. //! //! This transpose implementation is trivial, not neccessarily fast or //! efficient. There are more sophisticated and faster algorithms for external //! memory matrix transpostion than sorting, see e.g. J.S. Vitter: Algorithms //! and Data Structures for External Memory, Chapter 7.2. #include #include #include #include class streamop_matrix_transpose { unsigned cut, repeat; unsigned pos; public: typedef unsigned value_type; streamop_matrix_transpose(unsigned cut, unsigned repeat) : cut(cut), repeat(repeat), pos(0) { } value_type operator * () const { return (pos % cut) * repeat + pos / cut; } streamop_matrix_transpose& operator ++ () { ++pos; return *this; } bool empty() const { return pos == (cut * repeat); } }; template struct cmp_tuple_first : std::binary_function { typedef T value_type; typedef typename value_type::first_type first_value_type; bool operator () (const value_type& a, const value_type& b) const { return a.first < b.first; } value_type min_value() const { return value_type(std::numeric_limits::min(), 0); } value_type max_value() const { return value_type(std::numeric_limits::max(), 0); } }; template void dump_upper_left(const Vector& v, unsigned rows, unsigned cols, unsigned nx, unsigned ny) { int w = 5; // assumes row-major layout in the vector serialization of the matrix for (unsigned y = 0; y < ny && y < rows; ++y) { std::cout << std::setw(w) << y << ":"; for (unsigned x = 0; x < nx && x < cols; ++x) std::cout << " " << std::setw(w) << v[y * cols + x]; if (nx < cols) std::cout << " ..."; std::cout << std::endl; } if (ny < rows) std::cout << std::setw(w) << "..." << std::endl; std::cout << std::endl; } int main() { unsigned num_cols = 10000; unsigned num_rows = 5000; // buffers for streamify and materialize, // block size matches the block size of the input/output vector size_t numbuffers = 2 * stxxl::config::get_instance()->disks_number(); // RAM to be used for sorting (in bytes) size_t memory_for_sorting = 1 << 28; /////////////////////////////////////////////////////////////////////// typedef stxxl::VECTOR_GENERATOR::result array_type; array_type input(num_rows * num_cols); array_type output(num_cols * num_rows); // fill the input array with some values for (unsigned i = 0; i < num_rows * num_cols; ++i) input[i] = i; std::cout << "Before transpose:" << std::endl; dump_upper_left(input, num_rows, num_cols, 10, 10); stxxl::stats_data stats_before(*stxxl::stats::get_instance()); // HERE streaming part begins (streamifying) // create input stream typedef stxxl::stream::streamify_traits::stream_type input_stream_type; input_stream_type input_stream = stxxl::stream::streamify(input.begin(), input.end(), numbuffers); // create stream of destination indices typedef streamop_matrix_transpose destination_index_stream_type; destination_index_stream_type destination_index_stream(num_cols, num_rows); // create tuple stream: (key, value) typedef stxxl::stream::make_tuple tuple_stream_type; tuple_stream_type tuple_stream(destination_index_stream, input_stream); // sort tuples by first entry (key) typedef cmp_tuple_first cmp_type; typedef stxxl::stream::sort sorted_tuple_stream_type; sorted_tuple_stream_type sorted_tuple_stream(tuple_stream, cmp_type(), memory_for_sorting); // discard the key we used for sorting, keep second entry of the tuple only (value) typedef stxxl::stream::choose sorted_element_stream_type; sorted_element_stream_type sorted_element_stream(sorted_tuple_stream); // HERE streaming part ends (materializing) array_type::iterator o = stxxl::stream::materialize(sorted_element_stream, output.begin(), output.end(), numbuffers); STXXL_CHECK(o == output.end()); STXXL_CHECK(sorted_element_stream.empty()); stxxl::stats_data stats_after(*stxxl::stats::get_instance()); std::cout << "After transpose:" << std::endl; dump_upper_left(output, num_cols, num_rows, 10, 10); std::cout << "I/O stats (streaming part only!)" << std::endl << (stats_after - stats_before); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/stream/test_stream1.cpp000644 001411 000144 00000005146 12411366426 020264 0ustar00tbusers000000 000000 /*************************************************************************** * tests/stream/test_stream1.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include struct Input { typedef unsigned value_type; value_type value; value_type rnd_value; stxxl::random_number32 rnd; value_type crc; Input(value_type init) : value(init) { rnd_value = rnd(); crc = rnd_value; } bool empty() const { return value == 1; } Input& operator ++ () { --value; rnd_value = rnd(); if (!empty()) crc += rnd_value; return *this; } const value_type& operator * () const { return rnd_value; } }; struct Cmp : std::binary_function { typedef unsigned value_type; bool operator () (const value_type& a, const value_type& b) const { return a < b; } value_type min_value() const { return std::numeric_limits::min(); } value_type max_value() const { return std::numeric_limits::max(); } }; #define MULT (1000) int main() { typedef stxxl::stream::runs_creator CreateRunsAlg; typedef CreateRunsAlg::sorted_runs_type SortedRunsType; stxxl::stats* s = stxxl::stats::get_instance(); std::cout << *s; STXXL_MSG("Size of block type " << sizeof(CreateRunsAlg::block_type)); unsigned size = MULT * 1024 * 128 / (unsigned)(sizeof(Input::value_type) * 2); Input in(size + 1); CreateRunsAlg SortedRuns(in, Cmp(), 1024 * 128 * MULT); SortedRunsType Runs = SortedRuns.result(); STXXL_CHECK(stxxl::stream::check_sorted_runs(Runs, Cmp())); // merge the runs stxxl::stream::runs_merger merger(Runs, Cmp(), MULT * 1024 * 128); stxxl::vector array; STXXL_MSG(size << " " << Runs->elements); STXXL_MSG("CRC: " << in.crc); Input::value_type crc(0); for (unsigned i = 0; i < size; ++i) { crc += *merger; array.push_back(*merger); ++merger; } STXXL_MSG("CRC: " << crc); STXXL_CHECK(stxxl::is_sorted(array.begin(), array.end(), Cmp())); STXXL_CHECK(merger.empty()); std::cout << *s; return 0; } stxxl-1.4.1/tests/stream/test_push_sort.cpp000644 001411 000144 00000006006 12405152404 020722 0ustar00tbusers000000 000000 /*************************************************************************** * tests/stream/test_push_sort.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004 Roman Dementiev * Copyright (C) 2009, 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example stream/test_push_sort.cpp //! This is an example of how to use some basic algorithms from //! stream package. This example shows how to create //! \c sorted_runs data structure //! using \c stream::use_push specialization of \c stream::runs_creator class #include #include const unsigned long long megabyte = 1024 * 1024; const unsigned int block_size = 1 * megabyte; typedef unsigned value_type; struct Cmp : public std::binary_function { typedef unsigned value_type; bool operator () (const value_type& a, const value_type& b) const { return a < b; } value_type min_value() { return std::numeric_limits::min(); } value_type max_value() { return std::numeric_limits::max(); } }; // special parameter type typedef stxxl::stream::use_push InputType; typedef stxxl::stream::runs_creator CreateRunsAlg; typedef CreateRunsAlg::sorted_runs_type SortedRunsType; // forced instantiation template class stxxl::stream::runs_merger; int main() { #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif unsigned input_size = (50 * megabyte / sizeof(value_type)); Cmp c; CreateRunsAlg SortedRuns(c, 10 * megabyte); value_type checksum_before(0); stxxl::random_number32 rnd; for (unsigned cnt = input_size; cnt > 0; --cnt) { const value_type element = rnd(); checksum_before += element; SortedRuns.push(element); // push into the sorter } SortedRunsType Runs = SortedRuns.result(); // get sorted_runs data structure STXXL_CHECK(stxxl::stream::check_sorted_runs(Runs, Cmp())); // merge the runs stxxl::stream::runs_merger merger(Runs, Cmp(), 10 * megabyte); stxxl::vector, block_size, STXXL_DEFAULT_ALLOC_STRATEGY> array; STXXL_MSG(input_size << " " << Runs->elements); STXXL_MSG("checksum before: " << checksum_before); value_type checksum_after(0); for (unsigned i = 0; i < input_size; ++i) { checksum_after += *merger; array.push_back(*merger); ++merger; } STXXL_MSG("checksum after: " << checksum_after); STXXL_CHECK(stxxl::is_sorted(array.begin(), array.end(), Cmp())); STXXL_CHECK(checksum_before == checksum_after); STXXL_CHECK(merger.empty()); return 0; } stxxl-1.4.1/tests/stream/CMakeLists.txt000644 001411 000144 00000002275 12422212055 017673 0ustar00tbusers000000 000000 ############################################################################ # tests/stream/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(test_loop) stxxl_build_test(test_materialize) stxxl_build_test(test_naive_transpose) stxxl_build_test(test_push_sort) stxxl_build_test(test_sorted_runs) stxxl_build_test(test_stream) stxxl_build_test(test_stream1) add_define(test_stream1 "STXXL_VERBOSE_LEVEL=1") add_define(test_push_sort "STXXL_VERBOSE_LEVEL=0") add_define(test_stream "STXXL_VERBOSE_LEVEL=1") add_define(test_sorted_runs "STXXL_VERBOSE_LEVEL=0") add_define(test_materialize "STXXL_VERBOSE_LEVEL=0" "STXXL_VERBOSE_MATERIALIZE=STXXL_VERBOSE0") stxxl_test(test_loop 100 -v) stxxl_test(test_loop 1000000) stxxl_test(test_materialize) stxxl_test(test_naive_transpose) stxxl_test(test_push_sort) stxxl_test(test_sorted_runs) stxxl_test(test_stream) stxxl_test(test_stream1) stxxl-1.4.1/tests/io/test_io.cpp000644 001411 000144 00000006032 12405375303 016423 0ustar00tbusers000000 000000 /*************************************************************************** * tests/io/test_io.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include //! \example io/test_io.cpp //! This is an example of use of \c \ files, requests, and //! completion tracking mechanisms, i.e. \c stxxl::file , \c stxxl::request using stxxl::file; struct my_handler { void operator () (stxxl::request* ptr) { STXXL_MSG("Request completed: " << ptr); } }; int main(int argc, char** argv) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " tempdir" << std::endl; return -1; } std::string tempfilename[2]; tempfilename[0] = std::string(argv[1]) + "/test_io_1.dat"; tempfilename[1] = std::string(argv[1]) + "/test_io_2.dat"; std::cout << sizeof(void*) << std::endl; const int size = 1024 * 384; char* buffer = (char*)stxxl::aligned_alloc<4096>(size); memset(buffer, 0, size); #if STXXL_HAVE_MMAP_FILE stxxl::mmap_file file1(tempfilename[0], file::CREAT | file::RDWR | file::DIRECT, 0); file1.set_size(size * 1024); #endif stxxl::syscall_file file2(tempfilename[1], file::CREAT | file::RDWR | file::DIRECT, 1); stxxl::request_ptr req[16]; unsigned i; for (i = 0; i < 16; i++) req[i] = file2.awrite(buffer, i * size, size, my_handler()); wait_all(req, 16); // check behaviour of having requests to the same location at the same time for (i = 2; i < 16; i++) req[i] = file2.awrite(buffer, 0, size, my_handler()); req[0] = file2.aread(buffer, 0, size, my_handler()); req[1] = file2.awrite(buffer, 0, size, my_handler()); wait_all(req, 16); stxxl::aligned_dealloc<4096>(buffer); std::cout << *(stxxl::stats::get_instance()); stxxl::uint64 sz; for (sz = 123, i = 0; i < 20; ++i, sz *= 10) STXXL_MSG(">>>" << stxxl::add_SI_multiplier(sz) << "<<<"); for (sz = 123, i = 0; i < 20; ++i, sz *= 10) STXXL_MSG(">>>" << stxxl::add_SI_multiplier(sz, "B") << "<<<"); STXXL_MSG(">>>" << stxxl::add_SI_multiplier(std::numeric_limits::max(), "B") << "<<<"); for (sz = 123, i = 0; i < 20; ++i, sz *= 10) STXXL_MSG(">>>" << stxxl::add_IEC_binary_multiplier(sz) << "<<<"); for (sz = 123, i = 0; i < 20; ++i, sz *= 10) STXXL_MSG(">>>" << stxxl::add_IEC_binary_multiplier(sz, "B") << "<<<"); STXXL_MSG(">>>" << stxxl::add_IEC_binary_multiplier(std::numeric_limits::max(), "B") << "<<<"); #if STXXL_HAVE_MMAP_FILE file1.close_remove(); #endif file2.close_remove(); return 0; } stxxl-1.4.1/tests/io/test_cancel.cpp000644 001411 000144 00000005610 12414452316 017242 0ustar00tbusers000000 000000 /*************************************************************************** * tests/io/test_cancel.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009-2011 Johannes Singler * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include //! \example io/test_cancel.cpp //! This tests the request cancelation mechanisms. using stxxl::file; struct print_completion { void operator () (stxxl::request* ptr) { std::cout << "Request completed: " << ptr << std::endl; } }; int main(int argc, char** argv) { if (argc < 3) { std::cout << "Usage: " << argv[0] << " filetype tempfile" << std::endl; return -1; } const stxxl::uint64 size = 16 * 1024 * 1024, num_blocks = 16; char* buffer = (char*)stxxl::aligned_alloc<4096>(size); memset(buffer, 0, size); stxxl::compat_unique_ptr::result file( stxxl::create_file( argv[1], argv[2], stxxl::file::CREAT | stxxl::file::RDWR | stxxl::file::DIRECT) ); file->set_size(num_blocks * size); stxxl::request_ptr req[num_blocks]; //without cancelation std::cout << "Posting " << num_blocks << " requests." << std::endl; stxxl::stats_data stats1(*stxxl::stats::get_instance()); unsigned i = 0; for ( ; i < num_blocks; i++) req[i] = file->awrite(buffer, i * size, size, print_completion()); wait_all(req, num_blocks); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats1; //with cancelation std::cout << "Posting " << num_blocks << " requests." << std::endl; stxxl::stats_data stats2(*stxxl::stats::get_instance()); for (unsigned i = 0; i < num_blocks; i++) req[i] = file->awrite(buffer, i * size, size, print_completion()); //cancel first half std::cout << "Canceling first " << num_blocks / 2 << " requests." << std::endl; size_t num_canceled = cancel_all(req, req + num_blocks / 2); std::cout << "Successfully canceled " << num_canceled << " requests." << std::endl; //cancel every second in second half for (unsigned i = num_blocks / 2; i < num_blocks; i += 2) { std::cout << "Canceling request " << &(*(req[i])) << std::endl; if (req[i]->cancel()) std::cout << "Request canceled: " << &(*(req[i])) << std::endl; else std::cout << "Request not canceled: " << &(*(req[i])) << std::endl; } wait_all(req, num_blocks); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats2; stxxl::aligned_dealloc<4096>(buffer); return 0; } stxxl-1.4.1/tests/io/test_io_sizes.cpp000644 001411 000144 00000005435 12414452316 017646 0ustar00tbusers000000 000000 /*************************************************************************** * tests/io/test_io_sizes.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include //! \example io/test_io_sizes.cpp //! This tests the maximum chunk size that a file type can handle with a single request. int main(int argc, char** argv) { if (argc < 4) { std::cout << "Usage: " << argv[0] << " filetype tempfile maxsize" << std::endl; return -1; } using stxxl::unsigned_type; using stxxl::uint64; unsigned_type max_size = atoi(argv[3]); uint64* buffer = (uint64*)stxxl::aligned_alloc<4096>(max_size); try { stxxl::compat_unique_ptr::result file( stxxl::create_file( argv[1], argv[2], stxxl::file::CREAT | stxxl::file::RDWR | stxxl::file::DIRECT) ); file->set_size(max_size); stxxl::request_ptr req; stxxl::stats_data stats1(*stxxl::stats::get_instance()); for (unsigned_type size = 4096; size < max_size; size *= 2) { //generate data for (uint64 i = 0; i < size / sizeof(uint64); ++i) buffer[i] = i; //write STXXL_MSG(stxxl::add_IEC_binary_multiplier(size, "B") << "are being written at once"); req = file->awrite(buffer, 0, size); wait_all(&req, 1); //fill with wrong data for (uint64 i = 0; i < size / sizeof(uint64); ++i) buffer[i] = 0xFFFFFFFFFFFFFFFFull; //read again STXXL_MSG(stxxl::add_IEC_binary_multiplier(size, "B") << "are being read at once"); req = file->aread(buffer, 0, size); wait_all(&req, 1); //check bool wrong = false; for (uint64 i = 0; i < size / sizeof(uint64); ++i) if (buffer[i] != i) { STXXL_ERRMSG("Read inconsistent data at position " << i * sizeof(uint64)); wrong = true; break; } if (wrong) break; } std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats1; file->close_remove(); } catch (stxxl::io_error e) { std::cerr << e.what() << std::endl; throw; } stxxl::aligned_dealloc<4096>(buffer); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/io/test_mmap.cpp000644 001411 000144 00000003473 12405152404 016747 0ustar00tbusers000000 000000 /*************************************************************************** * tests/io/test_mmap.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Roman Dementiev * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include struct my_handler { void operator () (stxxl::request* ptr) { STXXL_MSG("Request completed: " << ptr); } }; void testIO() { const int size = 1024 * 384; char* buffer = static_cast(stxxl::aligned_alloc(size)); memset(buffer, 0, size); #if STXXL_WINDOWS const char* paths[2] = { "data1", "data2" }; #else const char* paths[2] = { "/var/tmp/data1", "/var/tmp/data2" }; stxxl::mmap_file file1(paths[0], stxxl::file::CREAT | stxxl::file::RDWR, 0); file1.set_size(size * 1024); #endif stxxl::syscall_file file2(paths[1], stxxl::file::CREAT | stxxl::file::RDWR, 1); stxxl::request_ptr req[16]; unsigned i = 0; for ( ; i < 16; i++) req[i] = file2.awrite(buffer, i * size, size, my_handler()); stxxl::wait_all(req, 16); stxxl::aligned_dealloc(buffer); #if !STXXL_WINDOWS file1.close_remove(); #endif file2.close_remove(); } void testIOException() { stxxl::file::unlink("TestFile"); // try to open non-existing files STXXL_CHECK_THROW(stxxl::mmap_file file1("TestFile", stxxl::file::RDWR, 0), stxxl::io_error); STXXL_CHECK_THROW(stxxl::syscall_file file1("TestFile", stxxl::file::RDWR, 0), stxxl::io_error); } int main() { testIO(); testIOException(); } stxxl-1.4.1/tests/io/CMakeLists.txt000644 001411 000144 00000004134 12414452316 017012 0ustar00tbusers000000 000000 ############################################################################ # tests/io/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(test_cancel) stxxl_build_test(test_io) stxxl_build_test(test_io_sizes) stxxl_test(test_io "${STXXL_TMPDIR}") stxxl_test(test_cancel syscall "${STXXL_TMPDIR}/testdisk1") # TODO: clean up after fileperblock_syscall stxxl_test(test_cancel fileperblock_syscall "${STXXL_TMPDIR}/testdisk1") if(STXXL_HAVE_MMAP_FILE) stxxl_test(test_cancel mmap "${STXXL_TMPDIR}/testdisk1") stxxl_test(test_cancel fileperblock_mmap "${STXXL_TMPDIR}/testdisk1") #-tb: fails randomly (due to I/O cancelation order) #stxxl_test(test_cancel simdisk "${STXXL_TMPDIR}/testdisk1") endif(STXXL_HAVE_MMAP_FILE) if(STXXL_HAVE_LINUXAIO_FILE) stxxl_test(test_cancel linuxaio "${STXXL_TMPDIR}/testdisk1") endif(STXXL_HAVE_LINUXAIO_FILE) if(USE_BOOST) stxxl_test(test_cancel boostfd "${STXXL_TMPDIR}/testdisk1") stxxl_test(test_cancel fileperblock_boostfd "${STXXL_TMPDIR}/testdisk1") endif(USE_BOOST) stxxl_test(test_cancel memory "${STXXL_TMPDIR}/testdisk1") # TODO: asserts! #stxxl_test(test_cancel wbtl "${STXXL_TMPDIR}/testdisk1") stxxl_test(test_io_sizes syscall "${STXXL_TMPDIR}/testdisk1" 1073741824) if(STXXL_HAVE_MMAP_FILE) stxxl_test(test_io_sizes mmap "${STXXL_TMPDIR}/testdisk1" 1073741824) endif(STXXL_HAVE_MMAP_FILE) if(STXXL_HAVE_LINUXAIO_FILE) stxxl_test(test_io_sizes linuxaio "${STXXL_TMPDIR}/testdisk1" 1073741824) endif(STXXL_HAVE_LINUXAIO_FILE) if(USE_BOOST) stxxl_test(test_io_sizes boostfd "${STXXL_TMPDIR}/testdisk1" 1073741824) endif(USE_BOOST) if(STXXL_HAVE_MMAP_FILE) stxxl_build_test(test_mmap) stxxl_test(test_mmap) stxxl_build_test(test_sim_disk) # TODO: sometimes asserts due to timing #stxxl_test(test_sim_disk) endif(STXXL_HAVE_MMAP_FILE) stxxl-1.4.1/tests/io/test_sim_disk.cpp000644 001411 000144 00000005245 12411366426 017626 0ustar00tbusers000000 000000 /*************************************************************************** * tests/io/test_sim_disk.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ // Tests sim_disk file implementation // must be run on in-memory swap partition ! #include #include #include #include using stxxl::file; using stxxl::timestamp; int main() { const stxxl::int64 disk_size = stxxl::int64(1024 * 1024) * 1024 * 40; std::cout << sizeof(void*) << std::endl; const int block_size = 4 * 1024 * 1024; char* buffer = static_cast(stxxl::aligned_alloc(block_size)); memset(buffer, 0, block_size); const char* paths[2] = { "/tmp/data1", "/tmp/data2" }; stxxl::sim_disk_file file1(paths[0], file::CREAT | file::RDWR | file::DIRECT, 0); file1.set_size(disk_size); stxxl::sim_disk_file file2(paths[1], file::CREAT | file::RDWR | file::DIRECT, 1); file2.set_size(disk_size); unsigned i = 0; stxxl::int64 pos = 0; stxxl::request_ptr req; STXXL_MSG("Estimated time:" << block_size / stxxl::simdisk_geometry::s_average_speed); STXXL_MSG("Sequential write"); for (i = 0; i < 40; i++) { double begin = timestamp(); req = file1.awrite(buffer, pos, block_size); req->wait(); double end = timestamp(); STXXL_MSG("Pos: " << pos << " block_size:" << block_size << " time:" << (end - begin)); pos += 1024 * 1024 * 1024; } double sum = 0.; double sum2 = 0.; STXXL_MSG("Random write"); const unsigned int times = 80; stxxl::random_number<> rnd; for (i = 0; i < times; i++) { pos = (stxxl::int64)rnd(disk_size / block_size) * block_size; double begin = timestamp(); req = file1.awrite(buffer, pos, block_size); req->wait(); double diff = timestamp() - begin; sum += diff; sum2 += diff * diff; STXXL_MSG("Pos: " << pos << " block_size:" << block_size << " time:" << (diff)); } sum = sum / double(times); sum2 = sum2 / double(times); STXXL_CHECK(sum2 - sum * sum >= 0.0); double err = sqrt(sum2 - sum * sum); STXXL_MSG("Standard Deviation: " << err << " s, " << 100. * (err / sum) << " %"); stxxl::aligned_dealloc(buffer); file1.close_remove(); file2.close_remove(); return 0; } stxxl-1.4.1/tests/containers/test_map_random.cpp000644 001411 000144 00000027572 12405375303 021703 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_map_random.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004, 2005 Thomas Nowak * Copyright (C) 2005, 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \file containers/test_map_random.cpp //! File for testing functionality of stxxl::map. //! \example containers/test_map_random.cpp //! This is an example of use of \c stxxl::map container. #include #include "map_test_handlers.h" typedef int key_type; typedef int data_type; struct cmp2 : public std::less { static int max_value() { return std::numeric_limits::max(); } }; #define DATA_NODE_BLOCK_SIZE (4096) #define DATA_LEAF_BLOCK_SIZE (4096) typedef std::map std_map_type; typedef stxxl::map xxl_map_type; #define PERCENT_CLEAR 1 #define PERCENT_ERASE_BULK 9 #define PERCENT_ERASE_KEY 90 #define PERCENT_ERASE_ITERATOR 100 #define PERCENT_INSERT_PAIR 100 #define PERCENT_INSERT_BULK 100 #define PERCENT_SIZING 100 #define PERCENT_LOWER 100 #define PERCENT_UPPER 200 #define PERCENT_FIND 100 #define PERCENT_ITERATOR 100 //#define MAX_KEY 1000 #define MAX_KEY 10000 //#define MAX_STEP 0x0001000 #define NODE_BLOCK_SIZE xxl_map_type::node_block_type::raw_size #define LEAF_BLOCK_SIZE xxl_map_type::leaf_block_type::raw_size #define NODE_MELEMENTS xxl_map_type::node_block_type::size #define LEAF_MELEMENTS xxl_map_type::leaf_block_type::size int main(int argc, char* argv[]) { typedef std::vector > vector_type; STXXL_MSG("Node block size: " << NODE_BLOCK_SIZE << " bytes"); STXXL_MSG("Leaf block size: " << LEAF_BLOCK_SIZE << " bytes"); STXXL_MSG("Node max elements: " << NODE_MELEMENTS); STXXL_MSG("Leaf max elements: " << LEAF_MELEMENTS); stxxl::random_number32 rnd; //stxxl::ran32State = 1141225706; STXXL_MSG("Init random seed: " << stxxl::ran32State); int a = (PERCENT_CLEAR + PERCENT_SIZING + PERCENT_ERASE_BULK + PERCENT_ERASE_KEY + PERCENT_ERASE_ITERATOR + PERCENT_INSERT_PAIR + PERCENT_INSERT_BULK + PERCENT_LOWER + PERCENT_UPPER + PERCENT_FIND + PERCENT_ITERATOR); STXXL_CHECK(a == 1000); if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " STEP "); STXXL_MSG("Note, that STEP must be > 1000"); return -1; } stxxl::uint64 MAX_STEP = atoi(argv[1]); STXXL_CHECK(MAX_STEP > 1000); std_map_type stdmap; xxl_map_type xxlmap(NODE_BLOCK_SIZE * 4, LEAF_BLOCK_SIZE * 3); for (stxxl::uint64 i = 0; i < MAX_STEP; i++) { // *************************************************** // A random number is created to determine which kind // of operation we will be called. // *************************************************** long step = rnd() % 1000; int percent = 0; if (i % (MAX_STEP / 100) == 0) { STXXL_MSG("Step=" << i << " (" << (unsigned)stdmap.size() << ")"); } // ********************************************************* // The clear function will be called // ********************************************************* if (step < (percent += PERCENT_CLEAR)) { if ((unsigned)rand() % 1000 < stdmap.size()) { stdmap.clear(); xxlmap.clear(); STXXL_CHECK(stdmap.empty()); STXXL_CHECK(xxlmap.empty()); } } // ********************************************************* // The size function will be called // ********************************************************* else if (step < (percent += PERCENT_SIZING)) { std_map_type::size_type size1 = stdmap.size(); xxl_map_type::size_type size2 = xxlmap.size(); STXXL_CHECK(size1 == size2); } // ********************************************************* // The erase range function will be called // ********************************************************* else if (step < (percent += PERCENT_ERASE_BULK)) { key_type key1 = rand() % MAX_KEY; key_type key2 = rand() % MAX_KEY; if (key1 > key2) { std::swap(key1, key2); } stdmap.erase(stdmap.lower_bound(key1), stdmap.upper_bound(key2)); xxlmap.erase(xxlmap.lower_bound(key1), xxlmap.upper_bound(key2)); STXXL_CHECK(stdmap.size() == xxlmap.size()); STXXL_CHECK(stdmap.lower_bound(key1) == stdmap.end() || stdmap.lower_bound(key1) == stdmap.upper_bound(key2)); STXXL_CHECK(xxlmap.lower_bound(key1) == xxlmap.end() || xxlmap.lower_bound(key1) == xxlmap.upper_bound(key2)); } // ********************************************************* // The erase a key function will be called // ********************************************************* else if (step < (percent += PERCENT_ERASE_KEY)) { key_type key = rnd() % MAX_KEY; stdmap.erase(key); xxlmap.erase(key); STXXL_CHECK(stxxl::not_there(stdmap, key)); STXXL_CHECK(stxxl::not_there(xxlmap, key)); } // ********************************************************* // The erase function will be called // ********************************************************* else if (step < (percent += PERCENT_ERASE_ITERATOR)) { key_type key = rnd() % MAX_KEY; std_map_type::iterator stditer = stdmap.find(key); xxl_map_type::iterator xxliter = xxlmap.find(key); STXXL_CHECK(stxxl::is_end(stdmap, stditer) == is_end(xxlmap, xxliter)); if (stditer != stdmap.end()) stdmap.erase(stditer); if (xxliter != xxlmap.end()) xxlmap.erase(xxliter); STXXL_CHECK(stxxl::not_there(stdmap, key)); STXXL_CHECK(stxxl::not_there(xxlmap, key)); } // ********************************************************* // The insert function will be called // ********************************************************* else if (step < (percent += PERCENT_INSERT_PAIR)) { key_type key = rnd() % MAX_KEY; stdmap.insert(std::pair(key, 2 * key)); xxlmap.insert(std::pair(key, 2 * key)); STXXL_CHECK(stxxl::there(stdmap, key, 2 * key)); STXXL_CHECK(stxxl::there(xxlmap, key, 2 * key)); } // ********************************************************* // The bulk insert function will be called // ********************************************************* else if (step < (percent += PERCENT_INSERT_BULK)) { unsigned lower = rnd() % MAX_KEY; unsigned upper = rnd() % MAX_KEY; if (lower > upper) std::swap(lower, upper); vector_type v2(upper - lower); for (unsigned j = 0; j < (unsigned)(upper - lower); j++) { v2[j].first = lower + j; v2[j].second = 2 * v2[j].first; } stdmap.insert(v2.begin(), v2.end()); xxlmap.insert(v2.begin(), v2.end()); for (unsigned i = lower; i < upper; i++) STXXL_CHECK(stxxl::there(stdmap, i, 2 * i)); for (unsigned i = lower; i < upper; i++) STXXL_CHECK(stxxl::there(xxlmap, i, 2 * i)); } // ********************************************************* // The lower_bound function will be called // ********************************************************* else if (step < (percent += PERCENT_LOWER)) { key_type key1 = rand() % MAX_KEY; key_type key2 = rand() % MAX_KEY; if (key1 > key2) { std::swap(key1, key2); } while (key1 < key2) { std_map_type::iterator stditer = stdmap.lower_bound(key1); xxl_map_type::iterator xxliter = xxlmap.lower_bound(key1); STXXL_CHECK(stxxl::is_end(stdmap, stditer) == is_end(xxlmap, xxliter)); if (!stxxl::is_end(stdmap, stditer)) { STXXL_CHECK(stxxl::is_same(*(stditer), *(xxliter))); } key1++; } } // ********************************************************* // The upper_bound function will be called // ********************************************************* else if (step < (percent += PERCENT_UPPER)) { key_type key1 = rand() % MAX_KEY; key_type key2 = rand() % MAX_KEY; if (key1 > key2) { std::swap(key1, key2); } while (key1 < key2) { std_map_type::iterator stditer = stdmap.upper_bound(key1); xxl_map_type::iterator xxliter = xxlmap.upper_bound(key1); STXXL_CHECK(stxxl::is_end(stdmap, stditer) == is_end(xxlmap, xxliter)); if (!stxxl::is_end(stdmap, stditer)) { STXXL_CHECK(stxxl::is_same(*(stditer), *(xxliter))); } key1++; } } // ********************************************************* // The find function will be called // ********************************************************* else if (step < (percent += PERCENT_FIND)) { key_type key1 = rand() % MAX_KEY; key_type key2 = rand() % MAX_KEY; if (key1 > key2) { std::swap(key1, key2); } while (key1 < key2) { std_map_type::iterator stditer = stdmap.find(key1); xxl_map_type::iterator xxliter = xxlmap.find(key1); STXXL_CHECK(stxxl::is_end(stdmap, stditer) == stxxl::is_end(xxlmap, xxliter)); if (!stxxl::is_end(stdmap, stditer)) { STXXL_CHECK(stxxl::is_same(*(stditer), *(xxliter))); } key1++; } } // ********************************************************* // The iterate functions will be called // ********************************************************* else if (step < (percent += PERCENT_ITERATOR)) { std_map_type::const_iterator siter1 = stdmap.begin(); xxl_map_type::const_iterator xiter1 = xxlmap.begin(); std_map_type::const_iterator siter2 = siter1; xxl_map_type::const_iterator xiter2 = xiter1; while (siter1 != stdmap.end()) { STXXL_CHECK(xiter1 != xxlmap.end()); STXXL_CHECK(stxxl::is_same(*(siter1++), *(xiter1++))); if (siter1 != stdmap.end()) { STXXL_CHECK(!stxxl::is_same(*siter1, *siter2)); } if (xiter1 != xxlmap.end()) { STXXL_CHECK(!stxxl::is_same(*xiter1, *xiter2)); } } STXXL_CHECK(xiter1 == xxlmap.end()); STXXL_CHECK(siter2 == stdmap.begin()); STXXL_CHECK(xiter2 == xxlmap.begin()); } } return 0; } stxxl-1.4.1/tests/containers/test_stack.cpp000644 001411 000144 00000013315 12411366426 020664 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_stack.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_stack.cpp //! This is an example of how to use \c stxxl::STACK_GENERATOR class //! to generate an \b external stack type //! with \c stxxl::grow_shrink_stack implementation, \b four blocks per page, //! block size \b 4096 bytes #include // forced instantiation template class stxxl::STACK_GENERATOR; template class stxxl::STACK_GENERATOR; template class stxxl::STACK_GENERATOR; template class stxxl::STACK_GENERATOR; template void test_lvalue_correctness(stack_type& stack, int a, int b) { int i; STXXL_CHECK(stack.empty()); for (i = 0; i < a; ++i) stack.push(i); for (i = 0; i < b; ++i) stack.push(i); for (i = 0; i < b; ++i) stack.pop(); stack.top() = 0xbeeff00d; for (i = 0; i < b; ++i) stack.push(i); for (i = 0; i < b; ++i) stack.pop(); if ((stack.top() != (size_t)(0xbeeff00d))) { STXXL_ERRMSG("STACK MISMATCH AFTER top() LVALUE MODIFICATION (0x" << std::hex << stack.top() << " != 0xbeeff00d)"); STXXL_CHECK(stack.top() == (size_t)(0xbeeff00d)); } for (i = 0; i < a; ++i) stack.pop(); } template void simple_test(stack_type& my_stack, size_t test_size) { for (size_t i = 0; i < test_size; i++) { my_stack.push(i); STXXL_CHECK(my_stack.top() == i); STXXL_CHECK(my_stack.size() == i + 1); } for (size_t i = test_size; i > 0; ) { --i; STXXL_CHECK(my_stack.top() == i); my_stack.pop(); STXXL_CHECK(my_stack.size() == i); } for (size_t i = 0; i < test_size; i++) { my_stack.push(i); STXXL_CHECK(my_stack.top() == i); STXXL_CHECK(my_stack.size() == i + 1); } // test swap stack_type s2; std::swap(s2, my_stack); std::swap(s2, my_stack); for (size_t i = test_size; i > 0; ) { --i; STXXL_CHECK(my_stack.top() == i); my_stack.pop(); STXXL_CHECK(my_stack.size() == i); } std::stack int_stack; for (size_t i = 0; i < test_size; i++) { int_stack.push(i); STXXL_CHECK(int_stack.top() == i); STXXL_CHECK(int_stack.size() == i + 1); } stack_type my_stack1(int_stack); for (size_t i = test_size; i > 0; ) { --i; STXXL_CHECK(my_stack1.top() == i); my_stack1.pop(); STXXL_CHECK(my_stack1.size() == i); } STXXL_MSG("Test 1 passed."); test_lvalue_correctness(my_stack, 4 * 4096 / 4 * 2, 4 * 4096 / 4 * 2 * 20); } int main(int argc, char* argv[]) { typedef stxxl::STACK_GENERATOR::result ext_normal_stack_type; typedef stxxl::STACK_GENERATOR::result ext_migrating_stack_type; typedef stxxl::STACK_GENERATOR::result ext_stack_type; typedef stxxl::STACK_GENERATOR::result ext_stack_type2; if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " test_size_in_pages"); return -1; } { ext_normal_stack_type my_stack; simple_test(my_stack, atoi(argv[1]) * 4 * 4096 / sizeof(int)); } { ext_migrating_stack_type my_stack; //simple_test(my_stack, atoi(argv[1]) * 4 * 4096 / sizeof(int)); } { ext_stack_type my_stack; simple_test(my_stack, atoi(argv[1]) * 4 * 4096 / sizeof(int)); } { // prefetch/write pool with 10 blocks prefetching and 10 block write cache (> D is recommended) stxxl::read_write_pool pool(10, 10); // create a stack that does not prefetch (level of prefetch aggressiveness 0) ext_stack_type2 my_stack(pool, 0); size_t test_size = atoi(argv[1]) * 4 * 4096 / sizeof(int); for (size_t i = 0; i < test_size; i++) { my_stack.push(i); STXXL_CHECK(my_stack.top() == i); STXXL_CHECK(my_stack.size() == i + 1); } my_stack.set_prefetch_aggr(10); for (size_t i = test_size; i > 0; ) { --i; STXXL_CHECK(my_stack.top() == i); my_stack.pop(); STXXL_CHECK(my_stack.size() == i); } for (size_t i = 0; i < test_size; i++) { my_stack.push(i); STXXL_CHECK(my_stack.top() == i); STXXL_CHECK(my_stack.size() == i + 1); } // test swap ext_stack_type2 s2(pool, 0); std::swap(s2, my_stack); std::swap(s2, my_stack); for (size_t i = test_size; i > 0; ) { --i; STXXL_CHECK(my_stack.top() == i); my_stack.pop(); STXXL_CHECK(my_stack.size() == i); } STXXL_MSG("Test 2 passed."); test_lvalue_correctness(my_stack, 4 * 4096 / 4 * 2, 4 * 4096 / 4 * 2 * 20); } return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/containers/map_test_handlers.h000644 001411 000144 00000006310 12405152404 021646 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/map_test_handlers.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004, 2005 Thomas Nowak * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \file map_test_handlers.h //! This file contains help functions for testing of stxxl::map. #ifndef STXXL_TEST_HEADER__MAP_TEST_HANDLERS_H_ #define STXXL_TEST_HEADER__MAP_TEST_HANDLERS_H_ #include STXXL_BEGIN_NAMESPACE // *********************************************** // THERE // *********************************************** template bool there(const MAPTYPE& map_, const typename MAPTYPE::key_type& key, const typename MAPTYPE::mapped_type& data) { typename MAPTYPE::const_iterator iter = map_.find(key); if (!(iter->second == data)) { STXXL_VERBOSE2("iter=(" << (*iter).first << ":" << (*iter).second << ")"); STXXL_VERBOSE2("key=" << key); STXXL_VERBOSE2("data=" << data); return false; } return true; } // *********************************************** // IS EQUAL END // *********************************************** template bool is_equal_end(const MAPTYPE& map_, typename MAPTYPE::const_iterator& iter) { return iter == map_.end(); } // *********************************************** // IS SAME // *********************************************** template bool is_same(value_type& v1, value_type& v2) { return v1.first == v2.first && v1.second == v2.second; } template bool is_same(const value_type& v1, const value_type& v2) { return v1.first == v2.first && v1.second == v2.second; } // *********************************************** // NOT THERE // *********************************************** template bool not_there(const MAPTYPE& map_, const typename MAPTYPE::key_type& key) { return map_.find(key) == map_.end(); } // *********************************************** // IS EMPTY // *********************************************** template bool is_empty(const MAPTYPE& map_) { return map_.empty(); } // *********************************************** // IS END // *********************************************** template bool is_end(MAPTYPE& map_, typename MAPTYPE::iterator& iter) { return iter == map_.end(); } template bool is_end(const MAPTYPE& map_, typename MAPTYPE::const_iterator& iter) { return iter == map_.end(); } // *********************************************** // IS SIZE // *********************************************** template bool is_size(const MAPTYPE& map_, const typename MAPTYPE::size_type size) { return map_.size() == size; } STXXL_END_NAMESPACE #endif // !STXXL_TEST_HEADER__MAP_TEST_HANDLERS_H_ stxxl-1.4.1/tests/containers/test_map.cpp000644 001411 000144 00000006735 12411366426 020344 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_map.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005, 2006 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include typedef unsigned int key_type; typedef unsigned int data_type; struct cmp : public std::less { static key_type min_value() { return std::numeric_limits::min(); } static key_type max_value() { return std::numeric_limits::max(); } }; #define BLOCK_SIZE (32 * 1024) #define CACHE_SIZE (2 * 1024 * 1024 / BLOCK_SIZE) #define CACHE_ELEMENTS (BLOCK_SIZE * CACHE_SIZE / (sizeof(key_type) + sizeof(data_type))) typedef stxxl::map map_type; // forced instantiation template class stxxl::map; int main(int argc, char** argv) { stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); stxxl::stats_data stats_elapsed; STXXL_MSG(stats_begin); STXXL_MSG("Block size " << BLOCK_SIZE / 1024 << " KiB"); STXXL_MSG("Cache size " << (CACHE_SIZE * BLOCK_SIZE) / 1024 << " KiB"); int max_mult = (argc > 1) ? atoi(argv[1]) : 256; for (int mult = 1; mult < max_mult; mult *= 2) { stats_begin = *stxxl::stats::get_instance(); const size_t el = mult * (CACHE_ELEMENTS / 8); STXXL_MSG("Elements to insert " << el << " volume =" << (el * (sizeof(key_type) + sizeof(data_type))) / 1024 << " KiB"); // allocate map and insert elements map_type* DMap = new map_type(CACHE_SIZE * BLOCK_SIZE / 2, CACHE_SIZE * BLOCK_SIZE / 2); map_type& Map = *DMap; for (unsigned i = 0; i < el; ++i) { Map[i] = i + 1; } STXXL_CHECK(Map.size() == el); stats_elapsed = stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; double writes = double(stats_elapsed.get_writes()) / double(el); double logel = log(double(el)) / log(double(BLOCK_SIZE)); STXXL_MSG("Logs: writes " << writes << " logel " << logel << " writes/logel " << (writes / logel)); STXXL_MSG(stats_elapsed); // search for keys stats_begin = *stxxl::stats::get_instance(); STXXL_MSG("Doing search"); size_t queries = el / 16; const map_type& ConstMap = Map; stxxl::random_number32 myrandom; for (unsigned i = 0; i < queries; ++i) { key_type key = (key_type)(myrandom() % el); map_type::const_iterator result = ConstMap.find(key); STXXL_CHECK((*result).second == key + 1); STXXL_CHECK(result->second == key + 1); } stats_elapsed = stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; double reads = double(stats_elapsed.get_reads()) / logel; double readsperq = double(stats_elapsed.get_reads()) / (double)queries; STXXL_MSG("reads/logel " << reads << " readsperq " << readsperq); STXXL_MSG(stats_elapsed); delete DMap; } return 0; } stxxl-1.4.1/tests/containers/test_vector_export.cpp000644 001411 000144 00000002630 12405375303 022455 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_vector_export.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_vector_export.cpp //! This is an example of use of \c stxxl::vector::export_files #include #include #include #include typedef stxxl::int64 int64; int main() { // use non-randomized striping to avoid side effects on random generator typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type v(int64(64 * 1024 * 1024) / sizeof(int64)); stxxl::random_number32 rnd; int offset = rnd(); STXXL_MSG("write " << v.size() << " elements"); stxxl::ran32State = 0xdeadbeef; vector_type::size_type i; // fill the vector with increasing sequence of integer numbers for (i = 0; i < v.size(); ++i) { v[i] = i + offset; STXXL_CHECK(v[i] == int64(i + offset)); } v.flush(); STXXL_MSG("export files"); v.export_files("exported_"); return 0; } stxxl-1.4.1/tests/containers/test_queue2.cpp000644 001411 000144 00000003444 12405152404 020757 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_queue2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #define STXXL_VERBOSE_LEVEL 0 // stxxl::queue contains deprecated funtions #define STXXL_NO_DEPRECATED 1 #include typedef stxxl::uint64 my_type; // forced instantiation template class stxxl::queue; int main(int argc, char** argv) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " [n in MiB]" << std::endl; return -1; } stxxl::int64 mega = 1 << 20; stxxl::int64 megabytes = atoi(argv[1]); stxxl::int64 nelements = megabytes * mega / sizeof(my_type); stxxl::int64 i; my_type in = 0, out = 0; stxxl::queue q; STXXL_MSG("op-sequence: ( push, pop, push ) * n"); for (i = 0; i < nelements; ++i) { if ((i % mega) == 0) STXXL_MSG("Insert " << i); q.push(in++); STXXL_CHECK(q.front() == out); q.pop(); ++out; q.push(in++); } STXXL_MSG("op-sequence: ( pop, push, pop ) * n"); for ( ; i > 0; --i) { if ((i % mega) == 0) STXXL_MSG("Remove " << i); STXXL_CHECK(q.front() == out); q.pop(); ++out; q.push(in++); STXXL_CHECK(q.front() == out); q.pop(); ++out; } STXXL_CHECK(q.empty()); STXXL_CHECK(in == out); std::cout << *stxxl::stats::get_instance(); return 0; } stxxl-1.4.1/tests/containers/test_migr_stack.cpp000644 001411 000144 00000004300 12411366426 021674 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_migr_stack.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_migr_stack.cpp //! This is an example of how to use \c stxxl::STACK_GENERATOR class //! to generate an \b migrating stack with critical size \c critical_size , //! external implementation \c normal_stack , \b four blocks per page, //! block size \b 4096 bytes, and internal implementation //! \c std::stack #include // forced instantiation const unsigned critical_size = 8 * 4096; template class stxxl::STACK_GENERATOR, critical_size>; int main() { typedef stxxl::STACK_GENERATOR, critical_size>::result migrating_stack_type; STXXL_MSG("Starting test."); migrating_stack_type my_stack; size_t test_size = 1 * 1024 * 1024 / sizeof(int); STXXL_MSG("Filling stack."); for (size_t i = 0; i < test_size; i++) { my_stack.push(i); STXXL_CHECK(my_stack.top() == i); STXXL_CHECK(my_stack.size() == i + 1); STXXL_CHECK((my_stack.size() >= critical_size) == my_stack.external()); } STXXL_MSG("Testing swap."); // test swap migrating_stack_type my_stack2; std::swap(my_stack2, my_stack); std::swap(my_stack2, my_stack); STXXL_MSG("Removing elements from " << (my_stack.external() ? "external" : "internal") << " stack"); for (size_t i = test_size; i > 0; ) { --i; STXXL_CHECK(my_stack.top() == i); STXXL_CHECK(my_stack.size() == i + 1); my_stack.pop(); STXXL_CHECK(my_stack.size() == i); STXXL_CHECK(my_stack.external() == (test_size >= int(critical_size))); } STXXL_MSG("Test passed."); return 0; } stxxl-1.4.1/tests/containers/test_queue.cpp000644 001411 000144 00000011465 12405152404 020677 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_queue.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ // stxxl::queue contains deprecated funtions #define STXXL_NO_DEPRECATED 1 #include #include typedef unsigned my_type; template void check(const q1type& q1, const q2type& q2) { STXXL_CHECK(q1.empty() == q2.empty()); STXXL_CHECK(q1.size() == q2.size()); if (!q1.empty()) { if (q1.front() != q2.front() || q1.back() != q2.back()) STXXL_MSG(q1.size() << ": (" << q1.front() << ", " << q1.back() << ") (" << q2.front() << ", " << q2.back() << ")" << (q1.front() == q2.front() ? "" : " FRONT")); STXXL_CHECK(q1.front() == q2.front()); STXXL_CHECK(q1.back() == q2.back()); } } // forced instantiation template class stxxl::queue; int main() { #if 1 //stxxl::set_seed(424648671); // works fine with a single disk, fails with two disks STXXL_MSG("SEED=" << stxxl::get_next_seed()); stxxl::srandom_number32(stxxl::get_next_seed()); stxxl::random_number32_r rnd; #else //stxxl::ran32State = 1028675152; // fails with two disks STXXL_MSG("ran32State=" << stxxl::ran32State); stxxl::random_number32 rnd; #endif unsigned cnt; STXXL_MSG("Elements in a block: " << stxxl::queue::block_type::size); // FIXME: can this be raised to recommended (3, 2) without breaking "Testing special case 4" or any other tests? stxxl::queue xqueue(2, 2, -1); std::queue squeue; check(xqueue, squeue); STXXL_MSG("Testing special case 4"); cnt = stxxl::queue::block_type::size; while (cnt--) { my_type val = rnd(); xqueue.push(val); squeue.push(val); check(xqueue, squeue); } cnt = stxxl::queue::block_type::size; while (cnt--) { xqueue.pop(); squeue.pop(); check(xqueue, squeue); } STXXL_MSG("Testing other cases "); cnt = 100 * stxxl::queue::block_type::size; while (cnt--) { if ((cnt % 1000000) == 0) STXXL_MSG("Operations left: " << cnt << " queue size " << squeue.size()); int rndtmp = rnd() % 3; if (rndtmp > 0 || squeue.empty()) { my_type val = rnd(); xqueue.push(val); squeue.push(val); check(xqueue, squeue); } else { xqueue.pop(); squeue.pop(); check(xqueue, squeue); } } while (!squeue.empty()) { if ((cnt++ % 1000000) == 0) STXXL_MSG("Operations: " << cnt << " queue size " << squeue.size()); int rndtmp = rnd() % 4; if (rndtmp >= 3) { my_type val = rnd(); xqueue.push(val); squeue.push(val); check(xqueue, squeue); } else { xqueue.pop(); squeue.pop(); check(xqueue, squeue); } } cnt = 10 * stxxl::queue::block_type::size; while (cnt--) { if ((cnt % 1000000) == 0) STXXL_MSG("Operations left: " << cnt << " queue size " << squeue.size()); int rndtmp = rnd() % 3; if (rndtmp > 0 || squeue.empty()) { my_type val = rnd(); xqueue.push(val); squeue.push(val); check(xqueue, squeue); } else { xqueue.pop(); squeue.pop(); check(xqueue, squeue); } } typedef stxxl::queue::block_type block_type; stxxl::read_write_pool pool(5, 5); stxxl::queue xqueue1(pool, -1); std::queue squeue1; cnt = 10 * stxxl::queue::block_type::size; while (cnt--) { if ((cnt % 1000000) == 0) STXXL_MSG("Operations left: " << cnt << " queue size " << squeue1.size()); int rndtmp = rnd() % 3; if (rndtmp > 0 || squeue1.empty()) { my_type val = rnd(); xqueue1.push(val); squeue1.push(val); check(xqueue1, squeue1); } else { xqueue1.pop(); squeue1.pop(); check(xqueue1, squeue1); } } { // test proper destruction of a single-block queue stxxl::queue q; q.push(42); } return 0; } stxxl-1.4.1/tests/containers/test_ext_merger2.cpp000644 001411 000144 00000014072 12411366426 022003 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_ext_merger2.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008, 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include typedef int my_type; typedef stxxl::typed_block<4096, my_type> block_type; struct dummy_merger { int current, delta; dummy_merger() : current(0), delta(1) { } void operator () (int current_, int delta_) { current = current_; delta = delta_; } template void multi_merge(OutputIterator b, OutputIterator e) { while (b != e) { * b = current; ++b; current += delta; } } }; struct my_cmp : public std::greater { my_type min_value() const { return std::numeric_limits::max(); } }; my_type * make_sequence(dummy_merger& dummy, int l) { my_type* seq = new my_type[l + 1]; // + sentinel dummy.multi_merge(seq, seq + l); seq[l] = my_cmp().min_value(); // sentinel return seq; } // forced instantiation const unsigned volume = 3 * 1024 * 1024; // in KiB const unsigned mem_for_queue = 256 * 1024 * 1024; template class stxxl::PRIORITY_QUEUE_GENERATOR; template class stxxl::priority_queue_local::ext_merger; int main() { stxxl::config::get_instance(); const int B = block_type::size; dummy_merger dummy; if (1) { const unsigned volume = 3 * 1024 * 1024; // in KiB const unsigned mem_for_queue = 256 * 1024 * 1024; typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pq_type; pq_type pq(mem_for_queue, mem_for_queue); pq.push(42); STXXL_CHECK(pq.top() == 42); pq.pop(); pq.push(2); pq.push(0); pq.push(3); pq.push(1); my_type x0, x1, x2, x3; x0 = pq.top(); pq.pop(); x1 = pq.top(); pq.pop(); x2 = pq.top(); pq.pop(); x3 = pq.top(); pq.pop(); STXXL_MSG("Order: " << x0 << " " << x1 << " " << x2 << " " << x3); STXXL_CHECK(x0 <= x1); STXXL_CHECK(x1 <= x2); STXXL_CHECK(x2 <= x3); STXXL_CHECK(pq.empty()); } if (1) { // ext_merger test stxxl::read_write_pool pool(1, 2); stxxl::priority_queue_local::ext_merger merger(&pool); dummy(1, 0); merger.insert_segment(dummy, B * 2); dummy(2, 0); merger.insert_segment(dummy, B * 2); dummy(B, 1); merger.insert_segment(dummy, B * 4); dummy(B, 2); merger.insert_segment(dummy, B * 4); dummy(B, 4); merger.insert_segment(dummy, B * 4); std::vector output(B * 3); // zero length merge merger.multi_merge(output.begin(), output.begin()); merger.multi_merge(output.begin(), output.begin()); merger.multi_merge(output.begin(), output.begin()); while (merger.size() > 0) { stxxl::unsigned_type l = std::min( (stxxl::unsigned_type)merger.size(), output.size() ); merger.multi_merge(output.begin(), output.begin() + l); STXXL_CHECK(stxxl::is_sorted(output.begin(), output.begin() + l)); STXXL_MSG("merged " << l << " elements: (" << *output.begin() << ", ..., " << *(output.begin() + l - 1) << ")"); } // zero length merge on empty data structure merger.multi_merge(output.begin(), output.begin()); merger.multi_merge(output.begin(), output.begin()); merger.multi_merge(output.begin(), output.begin()); } // ext_merger test if (1) { // loser_tree test stxxl::priority_queue_local::loser_tree loser; dummy(1, 0); my_type* seq0 = make_sequence(dummy, 2 * B); dummy(2, 0); my_type* seq1 = make_sequence(dummy, 2 * B); dummy(B, 1); my_type* seq2 = make_sequence(dummy, 4 * B); dummy(B, 2); my_type* seq3 = make_sequence(dummy, 4 * B); dummy(2 * B, 1); my_type* seq4 = make_sequence(dummy, 4 * B); dummy(B, 4); my_type* seq5 = make_sequence(dummy, 4 * B); dummy(B, 8); my_type* seq6 = make_sequence(dummy, 4 * B); dummy(2 * B, 2); my_type* seq7 = make_sequence(dummy, 4 * B); loser.init(); loser.insert_segment(seq0, 2 * B); loser.insert_segment(seq1, 2 * B); loser.insert_segment(seq2, 4 * B); loser.insert_segment(seq3, 4 * B); loser.insert_segment(seq4, 4 * B); if (0) { loser.insert_segment(seq5, 4 * B); loser.insert_segment(seq6, 4 * B); loser.insert_segment(seq7, 4 * B); } else { delete[] seq5; delete[] seq6; delete[] seq7; } my_type* out = new my_type[2 * B]; // zero length merge loser.multi_merge(out, out); loser.multi_merge(out, out); loser.multi_merge(out, out); while (loser.size() > 0) { stxxl::uint64 l = std::min(loser.size(), B + B / 2 + 1); loser.multi_merge(out, out + l); STXXL_CHECK(stxxl::is_sorted(out, out + l)); STXXL_MSG("merged " << l << " elements: (" << out[0] << ", ..., " << out[l - 1] << ")"); } // zero length merge on empty data structure loser.multi_merge(out, out); loser.multi_merge(out, out); loser.multi_merge(out, out); delete[] out; } // loser_tree test } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/containers/test_sequence.cpp000644 001411 000144 00000005734 12405152405 021366 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_sequence.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012-2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include int main(int argc, char* argv[]) { stxxl::uint64 ops = (argc >= 2) ? stxxl::atouint64(argv[1]) : 32 * 1024 * 1024; stxxl::random_number32 random; stxxl::sequence XXLDeque; std::deque STDDeque; for (stxxl::uint64 i = 0; i < ops; ++i) { unsigned curOP = random() % 6; unsigned value = random(); switch (curOP) { case 0: // make insertion a bit more likely case 1: XXLDeque.push_front(value); STDDeque.push_front(value); break; case 2: // make insertion a bit more likely case 3: XXLDeque.push_back(value); STDDeque.push_back(value); break; case 4: if (!XXLDeque.empty()) { XXLDeque.pop_front(); STDDeque.pop_front(); } break; case 5: if (!XXLDeque.empty()) { XXLDeque.pop_back(); STDDeque.pop_back(); } break; } STXXL_CHECK(XXLDeque.empty() == STDDeque.empty()); STXXL_CHECK(XXLDeque.size() == STDDeque.size()); if (XXLDeque.size() > 0) { STXXL_CHECK(XXLDeque.back() == STDDeque.back()); STXXL_CHECK(XXLDeque.front() == STDDeque.front()); } if (!(i % 1000000)) { std::cout << "Complete check of sequence/deque (size " << XXLDeque.size() << ")\n"; stxxl::sequence::stream stream = XXLDeque.get_stream(); std::deque::const_iterator b = STDDeque.begin(); while (!stream.empty()) { STXXL_CHECK(b != STDDeque.end()); STXXL_CHECK(*stream == *b); ++stream; ++b; } STXXL_CHECK(b == STDDeque.end()); } if (!(i % 1000000)) { std::cout << "Complete check of reverse sequence/deque (size " << XXLDeque.size() << ")\n"; stxxl::sequence::reverse_stream stream = XXLDeque.get_reverse_stream(); std::deque::reverse_iterator b = STDDeque.rbegin(); while (!stream.empty()) { STXXL_CHECK(b != STDDeque.rend()); STXXL_CHECK(*stream == *b); ++stream; ++b; } STXXL_CHECK(b == STDDeque.rend()); } } return 0; } stxxl-1.4.1/tests/containers/test_vector.cpp000644 001411 000144 00000011702 12405375303 021054 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_vector.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2003, 2006 Roman Dementiev * Copyright (C) 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_vector.cpp //! This is an example of use of \c stxxl::vector and //! \c stxxl::VECTOR_GENERATOR. Vector type is configured //! to store 64-bit integers and have 2 pages each of 1 block #include #include #include #include struct element // 24 bytes, not a power of 2 intentionally { stxxl::int64 key; stxxl::int64 load0; stxxl::int64 load1; element& operator = (stxxl::int64 i) { key = i; load0 = i + 42; load1 = i ^ 42; return *this; } bool operator == (const element& e2) const { return key == e2.key && load0 == e2.load0 && load1 == e2.load1; } }; struct counter { int value; counter(int v) : value(v) { } int operator () () { int old_val = value; value++; return old_val; } }; template void test_const_iterator(const my_vec_type& x) { typename my_vec_type::const_iterator i = x.begin(); i = x.end() - 1; i.block_externally_updated(); i.flush(); i++; ++i; --i; i--; *i; } void test_vector1() { // use non-randomized striping to avoid side effects on random generator typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type v(32 * 1024 * 1024 / sizeof(element)); // test assignment const_iterator = iterator vector_type::const_iterator c_it = v.begin(); STXXL_UNUSED(c_it); unsigned int big_size = 2 * 32 * 1024 * 1024; typedef stxxl::vector vec_big; vec_big my_vec(big_size); vec_big::iterator big_it = my_vec.begin(); big_it += 6; test_const_iterator(v); stxxl::random_number32 rnd; int offset = rnd(); STXXL_MSG("write " << v.size() << " elements"); stxxl::ran32State = 0xdeadbeef; vector_type::size_type i; // fill the vector with increasing sequence of integer numbers for (i = 0; i < v.size(); ++i) { v[i].key = i + offset; STXXL_CHECK(v[i].key == stxxl::int64(i + offset)); } // fill the vector with random numbers stxxl::generate(v.begin(), v.end(), stxxl::random_number32(), 4); v.flush(); STXXL_MSG("seq read of " << v.size() << " elements"); stxxl::ran32State = 0xdeadbeef; // testing swap vector_type a; std::swap(v, a); std::swap(v, a); for (i = 0; i < v.size(); i++) STXXL_CHECK(v[i].key == rnd()); // check again STXXL_MSG("clear"); v.clear(); stxxl::ran32State = 0xdeadbeef + 10; v.resize(32 * 1024 * 1024 / sizeof(element)); STXXL_MSG("write " << v.size() << " elements"); stxxl::generate(v.begin(), v.end(), stxxl::random_number32(), 4); stxxl::ran32State = 0xdeadbeef + 10; STXXL_MSG("seq read of " << v.size() << " elements"); for (i = 0; i < v.size(); i++) STXXL_CHECK(v[i].key == rnd()); STXXL_MSG("copy vector of " << v.size() << " elements"); vector_type v_copy0(v); STXXL_CHECK(v == v_copy0); vector_type v_copy1; v_copy1 = v; STXXL_CHECK(v == v_copy1); } //! check vector::resize(n,true) void test_resize_shrink() { typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type vector; int n = 1 << 16; vector.resize(n); for (int i = 0; i < n; i += 100) vector[i] = i; vector.resize(1, true); vector.flush(); } int main() { test_vector1(); test_resize_shrink(); return 0; } // forced instantiation template struct stxxl::VECTOR_GENERATOR; template class stxxl::vector; template class stxxl::vector_iterator, 4>; template class stxxl::const_vector_iterator, 4>; //-tb bufreader instantiation work only for const_iterator! typedef stxxl::vector::const_iterator const_vector_iterator; template class stxxl::vector_bufreader; template class stxxl::vector_bufreader_reverse; template class stxxl::vector_bufreader_iterator >; typedef stxxl::vector::iterator vector_iterator; template class stxxl::vector_bufwriter; stxxl-1.4.1/tests/containers/hash_map/test_hash_map_iterators.cpp000644 001411 000144 00000033270 12411366426 025215 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/hash_map/test_hash_map_iterators.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include using stxxl::unsigned_type; struct rand_pairs { stxxl::random_number32& rand_; rand_pairs(stxxl::random_number32& rand) : rand_(rand) { } std::pair operator () () { int v = rand_(); return std::pair(v, v); } }; struct hash_int { size_t operator () (int key) const { // a simple integer hash function return (size_t)(key * 2654435761u); } }; struct cmp : public std::less { int min_value() const { return std::numeric_limits::min(); } int max_value() const { return std::numeric_limits::max(); } }; //////////////////////////////////////////////////////////////////////////////// void cmp_with_internal_map() { typedef std::pair value_type; const unsigned_type value_size = sizeof(value_type); const unsigned_type n_values = 15000; const unsigned_type n_tests = 7500; // make sure all changes will be buffered const unsigned_type buffer_size = 5 * n_values * (value_size + sizeof(int*)); const unsigned_type mem_to_sort = 32 * 1024 * 1024; const unsigned_type subblock_raw_size = 4 * 1024; const unsigned_type block_size = 4; typedef stxxl::hash_map::hash_map hash_map; typedef hash_map::const_iterator const_iterator; typedef stxxl::compat_hash_map::result int_hash_map; stxxl::stats_data stats_begin = *stxxl::stats::get_instance(); hash_map map; map.max_buffer_size(buffer_size); const hash_map& cmap = map; int_hash_map int_map; // generate random values stxxl::random_number32 rand32; std::vector values1(n_values); std::vector values2(n_values); std::vector values3(n_values); std::generate(values1.begin(), values1.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values2.begin(), values2.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values3.begin(), values3.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); // --- initial import: create a nice mix of externally (values1) and // --- internally (values2) stored values std::cout << "Initial import..."; map.insert(values1.begin(), values1.end(), mem_to_sort); int_map.insert(values1.begin(), values1.end()); std::vector::iterator val_it = values2.begin(); for ( ; val_it != values2.end(); ++val_it) { map.insert_oblivious(*val_it); int_map.insert(*val_it); } // --- erase and overwrite some external values std::random_shuffle(values1.begin(), values1.end()); val_it = values1.begin(); for ( ; val_it != values1.begin() + n_tests; ++val_it) { map.erase_oblivious(val_it->first); int_map.erase(val_it->first); } for ( ; val_it != values1.begin() + 2 * n_tests; ++val_it) { map.insert_oblivious(*val_it); int_map.insert(*val_it); } // --- scan and compare with internal memory hash-map std::cout << "Compare with internal-memory map..."; STXXL_CHECK(int_map.size() == map.size()); const_iterator cit = cmap.begin(); for ( ; cit != cmap.end(); ++cit) { int key = (*cit).first; STXXL_CHECK(int_map.find(key) != int_map.end()); } std::cout << "passed" << std::endl; // --- another bulk insert std::cout << "Compare with internal-memory map after another bulk-insert..."; map.insert(values3.begin(), values3.end(), mem_to_sort); int_map.insert(values3.begin(), values3.end()); STXXL_CHECK(map.size() == map.size()); cit = cmap.begin(); for ( ; cit != cmap.end(); ++cit) { int key = (*cit).first; STXXL_CHECK(int_map.find(key) != int_map.end()); } std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); } //////////////////////////////////////////////////////////////////////////////// void basic_iterator_test() { typedef std::pair value_type; const unsigned_type value_size = sizeof(value_type); const unsigned_type n_values = 15000; const unsigned_type n_tests = 7500; // make sure all changes will be buffered const unsigned_type buffer_size = 5 * n_values * (value_size + sizeof(int*)); const unsigned_type mem_to_sort = 32 * 1024 * 1024; const unsigned_type subblock_raw_size = 4 * 1024; const unsigned_type block_size = 4; typedef stxxl::hash_map::hash_map hash_map; typedef hash_map::iterator iterator; typedef hash_map::const_iterator const_iterator; stxxl::stats_data stats_begin = *stxxl::stats::get_instance(); hash_map map; map.max_buffer_size(buffer_size); const hash_map& cmap = map; // generate random values stxxl::random_number32 rand32; std::vector values1(n_values); std::vector values2(n_values); std::generate(values1.begin(), values1.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values2.begin(), values2.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); // --- initial import: create a nice mix of externally (values1) and // --- internally (values2) stored values std::cout << "Initial import..."; STXXL_CHECK(map.begin() == map.end()); map.insert(values1.begin(), values1.end(), mem_to_sort); for (std::vector::iterator val_it = values2.begin(); val_it != values2.end(); ++val_it) map.insert_oblivious(*val_it); STXXL_CHECK(map.begin() != map.end()); STXXL_CHECK(map.size() == 2 * n_values); std::cout << "passed" << std::endl; // --- actual testing begins: modfiy random values via iterator std::cout << "Lookup and modify..."; std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; ++i) { iterator it1 = map.find(values1[i].first); iterator it2 = map.find(values2[i].first); STXXL_CHECK(it1 != map.end()); STXXL_CHECK(it2 != map.end()); (*it1).second++; (*it2).second++; } // check again for (unsigned_type i = 0; i < n_tests; ++i) { const_iterator cit1 = cmap.find(values1[i].first); const_iterator cit2 = cmap.find(values2[i].first); STXXL_CHECK(cit1 != map.end()); STXXL_CHECK(cit2 != map.end()); value_type value1 = *cit1; value_type value2 = *cit2; STXXL_CHECK(value1.second == value1.first + 1); STXXL_CHECK(value2.second == value2.first + 1); } std::cout << "passed" << std::endl; // --- scan and modify std::cout << "Scan and modify..."; { for (iterator it = map.begin(); it != map.end(); ++it) (*it).second = (*it).first + 1; for (const_iterator cit = cmap.begin(); cit != cmap.end(); ++cit) { STXXL_CHECK((*cit).second == (*cit).first + 1); } } std::cout << "passed" << std::endl; // --- interator-value altered by insert_oblivious std::cout << "Iterator-value altered by insert_oblivious..."; std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; i++) { int key1 = values1[i].first; int key2 = values2[i].first; const_iterator cit1 = cmap.find(key1); STXXL_CHECK(cit1 != cmap.end()); const_iterator cit2 = cmap.find(key2); STXXL_CHECK(cit2 != cmap.end()); map.insert_oblivious(value_type(key1, key1 + 3)); map.insert_oblivious(value_type(key2, key2 + 3)); STXXL_CHECK((*cit1).second == key1 + 3); STXXL_CHECK((*cit2).second == key2 + 3); } std::cout << "passed" << std::endl; // --- iterator-value altered by other iterator std::cout << "Iterator-value altered by other iterator..."; std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; i++) { const_iterator cit1 = cmap.find(values1[i].first); STXXL_CHECK(cit1 != cmap.end()); const_iterator cit2 = cmap.find(values2[i].first); STXXL_CHECK(cit2 != cmap.end()); iterator it1 = map.find(values1[i].first); STXXL_CHECK(it1 != map.end()); iterator it2 = map.find(values2[i].first); STXXL_CHECK(it2 != map.end()); (*it1).second = (*it1).first + 5; (*it2).second = (*it2).first + 5; STXXL_CHECK((*cit1).second == (*cit1).first + 5); STXXL_CHECK((*cit2).second == (*cit2).first + 5); } std::cout << "passed" << std::endl; // --- erase by iterator std::cout << "Erase by iterator..."; std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; i++) { const_iterator cit1 = cmap.find(values1[i].first); STXXL_CHECK(cit1 != cmap.end()); const_iterator cit2 = cmap.find(values2[i].first); STXXL_CHECK(cit2 != cmap.end()); iterator it1 = map.find(values1[i].first); STXXL_CHECK(it1 != map.end()); iterator it2 = map.find(values2[i].first); STXXL_CHECK(it2 != map.end()); map.erase(it1); map.erase(it2); STXXL_CHECK(cit1 == cmap.end()); STXXL_CHECK(cit2 == cmap.end()); } std::cout << "passed" << std::endl; // --- erase by value (key) std::cout << "Erase by key..."; for (unsigned_type i = 0; i < n_tests; i++) { const_iterator cit1 = cmap.find(values1[i + n_tests].first); STXXL_CHECK(cit1 != cmap.end()); const_iterator cit2 = cmap.find(values2[i + n_tests].first); STXXL_CHECK(cit2 != cmap.end()); map.erase_oblivious(values1[i + n_tests].first); map.erase_oblivious(values2[i + n_tests].first); STXXL_CHECK(cit1 == cmap.end()); STXXL_CHECK(cit2 == cmap.end()); } std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); } //////////////////////////////////////////////////////////////////////////////// void more_iterator_test() { typedef std::pair value_type; const unsigned_type value_size = sizeof(value_type); const unsigned_type n_values = 15000; // make sure all changes will be buffered const unsigned_type buffer_size = 5 * n_values * (value_size + sizeof(int*)); const unsigned_type mem_to_sort = 32 * 1024 * 1024; const unsigned_type subblock_raw_size = 4 * 1024; const unsigned_type block_size = 4; typedef stxxl::hash_map::hash_map hash_map; typedef hash_map::const_iterator const_iterator; stxxl::stats_data stats_begin = *stxxl::stats::get_instance(); hash_map map; map.max_buffer_size(buffer_size); const hash_map& cmap = map; // generate random values stxxl::random_number32 rand32; std::vector values1(n_values); std::vector values2(n_values); std::generate(values1.begin(), values1.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values2.begin(), values2.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); // --- initial import map.insert(values1.begin(), values1.end(), mem_to_sort); for (std::vector::iterator val_it = values2.begin(); val_it != values2.end(); ++val_it) map.insert_oblivious(*val_it); // --- store some iterators, rebuild and check std::cout << "Rebuild test..."; std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); { const_iterator cit1 = cmap.find(values1[17].first); const_iterator cit2 = cmap.find(values2[19].first); *cit1; *cit2; map.rehash(); STXXL_CHECK(map.size() == 2 * n_values); STXXL_CHECK((*cit1).first == values1[17].first); STXXL_CHECK((*cit2).first == values2[19].first); } std::cout << "passed" << std::endl; // --- unusual cases while scanning std::cout << "Another scan-test..."; { const_iterator cit1 = cmap.find(values1[n_values / 2].first); const_iterator cit2 = cit1; ++cit1; int key1 = (*cit1).first; ++cit1; int key2 = (*cit1).first; map.erase_oblivious(key1); map.insert_oblivious(value_type(key2, key2 + 2)); STXXL_CHECK((*cit1).second == key2 + 2); ++cit2; STXXL_CHECK(cit1 == cit2); } std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); } //////////////////////////////////////////////////////////////////////////////// int main() { cmp_with_internal_map(); basic_iterator_test(); more_iterator_test(); return 0; } stxxl-1.4.1/tests/containers/hash_map/test_hash_map_block_cache.cpp000644 001411 000144 00000013240 12411366426 025411 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/hash_map/test_hash_map_block_cache.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include bool test_block_cache() { typedef std::pair value_type; const unsigned subblock_raw_size = 1024 * 8; // 8KB subblocks const unsigned block_size = 128; // 1MB blocks (=128 subblocks) const unsigned num_blocks = 64; // number of blocks to use for this test const unsigned cache_size = 8; // size of cache in blocks typedef stxxl::typed_block subblock_type; typedef stxxl::typed_block block_type; const unsigned subblock_size = subblock_type::size; // size in values typedef block_type::bid_type bid_type; typedef std::vector bid_container_type; // prepare test: allocate blocks, fill them with values and write to disk bid_container_type bids(num_blocks); stxxl::block_manager* bm = stxxl::block_manager::get_instance(); bm->new_blocks(stxxl::striping(), bids.begin(), bids.end()); block_type* block = new block_type; for (unsigned i_block = 0; i_block < num_blocks; i_block++) { for (unsigned i_subblock = 0; i_subblock < block_size; i_subblock++) { for (unsigned i_value = 0; i_value < subblock_size; i_value++) { int value = i_value + i_subblock * subblock_size + i_block * block_size; (*block)[i_subblock][i_value] = value_type(value, value); } } stxxl::request_ptr req = block->write(bids[i_block]); req->wait(); } stxxl::random_number32 rand32; // create block_cache typedef stxxl::hash_map::block_cache cache_type; cache_type cache(cache_size); // load random subblocks and check for values int n_runs = cache_size * 10; for (int i_run = 0; i_run < n_runs; i_run++) { int i_block = rand32() % num_blocks; int i_subblock = rand32() % block_size; subblock_type* subblock = cache.get_subblock(bids[i_block], i_subblock); int expected = i_block * block_size + i_subblock * subblock_size + 1; STXXL_CHECK((*subblock)[1].first == expected); } // do the same again but this time with prefetching for (int i_run = 0; i_run < n_runs; i_run++) { int i_block = rand32() % num_blocks; int i_subblock = rand32() % block_size; cache.prefetch_block(bids[i_block]); subblock_type* subblock = cache.get_subblock(bids[i_block], i_subblock); int expected = i_block * block_size + i_subblock * subblock_size + 1; STXXL_CHECK((*subblock)[1].first == expected); } // load and modify some subblocks; flush cache and check values unsigned myseed = stxxl::get_next_seed(); stxxl::set_seed(myseed); for (int i_run = 0; i_run < n_runs; i_run++) { int i_block = rand32() % num_blocks; int i_subblock = rand32() % block_size; subblock_type* subblock = cache.get_subblock(bids[i_block], i_subblock); STXXL_CHECK(cache.make_dirty(bids[i_block])); (*subblock)[1].first = (*subblock)[1].second + 42; } stxxl::set_seed(myseed); for (int i_run = 0; i_run < n_runs; i_run++) { int i_block = rand32() % num_blocks; int i_subblock = rand32() % block_size; subblock_type* subblock = cache.get_subblock(bids[i_block], i_subblock); int expected = i_block * block_size + i_subblock * subblock_size + 1; STXXL_CHECK((*subblock)[1].first == expected + 42); } // test retaining cache.clear(); // not yet cached STXXL_CHECK(cache.retain_block(bids[0]) == false); cache.prefetch_block(bids[0]); // cached, should be retained STXXL_CHECK(cache.retain_block(bids[0]) == true); // release again STXXL_CHECK(cache.release_block(bids[0]) == true); // retrain-count should be 0, release fails STXXL_CHECK(cache.release_block(bids[0]) == false); // cache new block subblock_type* kicked_subblock = cache.get_subblock(bids[1], 0); // load other blocks, so that kicked_subblock, well, gets kicked for (unsigned i = 0; i < cache_size + 5; i++) { cache.prefetch_block(bids[i + 3]); } // load kicked subblock again, should be at a different location STXXL_CHECK(cache.get_subblock(bids[1], 0) != kicked_subblock); subblock_type* retained_subblock = cache.get_subblock(bids[1], 0); // now retain subblock STXXL_CHECK(cache.retain_block(bids[1]) == true); for (unsigned i = 0; i < cache_size + 5; i++) { cache.prefetch_block(bids[i + 3]); } // retained_subblock should not have been kicked STXXL_CHECK(cache.get_subblock(bids[1], 0) == retained_subblock); cache.clear(); // test swapping subblock_type* a_subblock = cache.get_subblock(bids[6], 1); cache_type cache2(cache_size / 2); std::swap(cache, cache2); STXXL_CHECK(cache.size() == cache_size / 2); STXXL_CHECK(cache2.size() == cache_size); STXXL_CHECK(cache2.get_subblock(bids[6], 1) == a_subblock); STXXL_MSG("Passed Block-Cache Test"); return true; } int main() { test_block_cache(); return 0; } stxxl-1.4.1/tests/containers/hash_map/test_hash_map_reader_writer.cpp000644 001411 000144 00000014107 12411366426 026035 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/hash_map/test_hash_map_reader_writer.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include void reader_writer_test() { typedef std::pair value_type; const unsigned subblock_raw_size = 1024 * 8; // 8KB subblocks const unsigned block_size = 128; // 1MB blocks (=128 subblocks) const unsigned n_blocks = 64; // number of blocks to use for this test const unsigned cache_size = 8; // size of cache in blocks const unsigned buffer_size = 4; // write buffer size in blocks typedef stxxl::typed_block subblock_type; typedef stxxl::typed_block block_type; const unsigned subblock_size = subblock_type::size; // size in values typedef block_type::bid_type bid_type; typedef std::vector bid_container_type; typedef bid_container_type::iterator bid_iterator_type; typedef stxxl::hash_map::block_cache cache_type; typedef stxxl::hash_map::buffered_writer writer_type; typedef stxxl::hash_map::buffered_reader reader_type; bid_container_type bids; cache_type cache(cache_size); // plain writing { writer_type writer(&bids, buffer_size, buffer_size / 2); unsigned i = 0; for ( ; i < n_blocks * block_size * subblock_size; ++i) writer.append(value_type(i, i)); writer.flush(); STXXL_CHECK(bids.size() >= n_blocks); block_type* block = new block_type; i = 0; for (unsigned i_block = 0; i_block < n_blocks; i_block++) { stxxl::request_ptr req = block->read(bids[i_block]); req->wait(); for (unsigned inner = 0; inner < block_size * subblock_size; ++inner) { STXXL_CHECK((*block)[inner / subblock_size][inner % subblock_size].first == i); i++; } } delete block; } // reading with/without prefetching { // last parameter disables prefetching reader_type reader(bids.begin(), bids.end(), cache, 0, false); for (unsigned i = 0; i < n_blocks * block_size * subblock_size; ++i) { STXXL_CHECK(reader.const_value().first == i); ++reader; } // prefetching enabled by default reader_type reader2(bids.begin(), bids.end(), cache); for (unsigned i = 0; i < n_blocks * block_size * subblock_size; ++i) { STXXL_CHECK(reader2.const_value().first == i); ++reader2; } } // reading with skipping { // disable prefetching reader_type reader(bids.begin(), bids.end(), cache, 0, false); // I: first subblock reader.skip_to(bids.begin() + 10, 0); unsigned expected = block_size * subblock_size * 10 + subblock_size * 0; STXXL_CHECK(reader.const_value().first == expected); // II: subblock in the middle (same block) reader.skip_to(bids.begin() + 10, 2); expected = block_size * subblock_size * 10 + subblock_size * 2; STXXL_CHECK(reader.const_value().first == expected); // III: subblock in the middle (another block) reader.skip_to(bids.begin() + 13, 1); expected = block_size * subblock_size * 13 + subblock_size * 1; STXXL_CHECK(reader.const_value().first == expected); } // reading with modifying access { reader_type reader(bids.begin(), bids.end(), cache); for (unsigned i = 0; i < n_blocks * block_size * subblock_size; ++i) { reader.value().second = reader.const_value().first + 1; ++reader; } reader_type reader2(bids.begin(), bids.end(), cache); for (unsigned i = 0; i < n_blocks * block_size * subblock_size; ++i) { STXXL_CHECK(reader2.const_value().second == reader2.const_value().first + 1); ++reader2; } cache.flush(); block_type* block = new block_type; unsigned i = 0; for (unsigned i_block = 0; i_block < n_blocks; i_block++) { stxxl::request_ptr req = block->read(bids[i_block]); req->wait(); for (unsigned inner = 0; inner < block_size * subblock_size; ++inner) { STXXL_CHECK((*block)[inner / subblock_size][inner % subblock_size].first == i); STXXL_CHECK((*block)[inner / subblock_size][inner % subblock_size].second == i + 1); i++; } } delete block; } //cache.dump_cache(); cache.clear(); // finishinging subblocks: skip second half of each subblock { writer_type writer(&bids, buffer_size, buffer_size / 2); unsigned i = 0; for (unsigned outer = 0; outer < n_blocks * block_size; ++outer) { for (unsigned inner = 0; inner < subblock_size / 2; ++inner) { writer.append(value_type(i, i)); ++i; } writer.finish_subblock(); } writer.flush(); reader_type reader(bids.begin(), bids.end(), cache); i = 0; for (unsigned outer = 0; outer < n_blocks * block_size; ++outer) { for (unsigned inner = 0; inner < subblock_size / 2; ++inner) { STXXL_CHECK(reader.const_value().first == i); ++i; ++reader; } reader.next_subblock(); } } STXXL_MSG("Passed Reader-Writer Test"); } int main() { reader_writer_test(); return 0; } stxxl-1.4.1/tests/containers/hash_map/CMakeLists.txt000644 001411 000144 00000001407 12411366426 022333 0ustar00tbusers000000 000000 ############################################################################ # tests/containers/hash_map/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2014 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(test_hash_map) stxxl_build_test(test_hash_map_block_cache) stxxl_build_test(test_hash_map_iterators) stxxl_build_test(test_hash_map_reader_writer) stxxl_test(test_hash_map) stxxl_test(test_hash_map_block_cache) stxxl_test(test_hash_map_iterators) stxxl_test(test_hash_map_reader_writer) stxxl-1.4.1/tests/containers/hash_map/test_hash_map.cpp000644 001411 000144 00000025036 12411366426 023122 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/hash_map/test_hash_map.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Markus Westphal * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include using stxxl::unsigned_type; struct rand_pairs { stxxl::random_number32& rand_; rand_pairs(stxxl::random_number32& rand) : rand_(rand) { } std::pair operator () () { int v = (int)rand_(); return std::pair(v, v); } }; struct hash_int { size_t operator () (int key) const { // a simple integer hash function return (size_t)(key * 2654435761u); } }; struct cmp : public std::less { int min_value() const { return std::numeric_limits::min(); } int max_value() const { return std::numeric_limits::max(); } }; // forced instantiation template class stxxl::unordered_map; struct structA { int x, y; structA() { } structA(int _x, int _y) : x(_x), y(_y) { } }; struct structB { double u, v; }; struct hash_structA { size_t operator () (const structA& key) const { // a simple integer hash function return (size_t)((key.x + key.y) * 2654435761u); } }; struct cmp_structA { bool operator () (const structA& a, const structA& b) const { if (a.x == b.x) return a.y < b.y; return a.x < b.x; } structA min_value() const { return structA(std::numeric_limits::min(), std::numeric_limits::min()); } structA max_value() const { return structA(std::numeric_limits::max(), std::numeric_limits::max()); } }; // forced instantiation of a struct template class stxxl::unordered_map< structA, structB, hash_structA, cmp_structA, 4* 1024, 4 >; void basic_test() { typedef std::pair value_type; const unsigned_type value_size = sizeof(value_type); const unsigned_type n_values = 20000; const unsigned_type n_tests = 10000; // make sure all changes will be buffered (*) const unsigned_type buffer_size = 5 * n_values * (value_size + sizeof(int*)); const unsigned_type mem_to_sort = 32 * 1024 * 1024; const unsigned_type subblock_raw_size = 4 * 1024; const unsigned_type block_size = 4; typedef stxxl::unordered_map unordered_map; typedef unordered_map::iterator iterator; typedef unordered_map::const_iterator const_iterator; stxxl::stats_data stats_begin; unordered_map map; map.max_buffer_size(buffer_size); const unordered_map& cmap = map; // generate random values stxxl::random_number32 rand32; std::vector values1(n_values); std::vector values2(n_values); std::vector values3(n_values / 2); std::generate(values1.begin(), values1.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values2.begin(), values2.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); std::generate(values3.begin(), values3.end(), rand_pairs(rand32) _STXXL_FORCE_SEQUENTIAL); // --- initial import std::cout << "Initial import..."; stats_begin = *stxxl::stats::get_instance(); STXXL_CHECK(map.begin() == map.end()); map.insert(values1.begin(), values1.end(), mem_to_sort); STXXL_CHECK(map.begin() != map.end()); STXXL_CHECK(map.size() == n_values); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // (*) all these values are stored in external memory; the remaining // changes will be buffered in internal memory // --- insert: new (from values2) and existing (from values1) values, with // --- and without checking std::cout << "Insert..."; stats_begin = *stxxl::stats::get_instance(); for (unsigned_type i = 0; i < n_values / 2; i++) { // new without checking map.insert_oblivious(values2[2 * i]); // new with checking std::pair res = map.insert(values2[2 * i + 1]); STXXL_CHECK(res.second && (*(res.first)).first == values2[2 * i + 1].first); // existing without checking map.insert_oblivious(values1[2 * i]); // exiting with checking res = map.insert(values1[2 * i + 1]); STXXL_CHECK(!res.second && (*(res.first)).first == values1[2 * i + 1].first); } STXXL_CHECK(map.size() == 2 * n_values); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // "old" values are stored in external memory, "new" values are stored in // internal memory // --- find: existing (from external and internal memory) and non-existing // --- values std::cout << "Find..."; stats_begin = *stxxl::stats::get_instance(); std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; i++) { STXXL_CHECK(cmap.find(values1[i].first) != cmap.end()); STXXL_CHECK(cmap.find(values2[i].first) != cmap.end()); STXXL_CHECK(cmap.find(values3[i].first) == cmap.end()); } std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // --- insert with overwriting std::cout << "Insert with overwriting..."; stats_begin = *stxxl::stats::get_instance(); std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); for (unsigned_type i = 0; i < n_tests; i++) { value_type value1 = values1[i]; // in external memory value1.second++; map.insert_oblivious(value1); value_type value2 = values2[i]; // in internal memory value2.second++; map.insert_oblivious(value2); } // now check STXXL_CHECK(map.size() == 2 * n_values); // nothing added, nothing removed for (unsigned_type i = 0; i < n_tests; i++) { const_iterator it1 = cmap.find(values1[i].first); const_iterator it2 = cmap.find(values2[i].first); STXXL_CHECK((*it1).second == values1[i].second + 1); STXXL_CHECK((*it2).second == values2[i].second + 1); } std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // --- erase: existing and non-existing values, with and without checking std::cout << "Erase..."; stats_begin = *stxxl::stats::get_instance(); std::random_shuffle(values1.begin(), values1.end()); std::random_shuffle(values2.begin(), values2.end()); std::random_shuffle(values3.begin(), values3.end()); for (unsigned_type i = 0; i < n_tests / 2; i++) { // external // existing without checking map.erase_oblivious(values1[2 * i].first); // existing with checking STXXL_CHECK(map.erase(values1[2 * i + 1].first) == 1); } for (unsigned_type i = 0; i < n_tests / 2; i++) { // internal // existing without checking map.erase_oblivious(values2[2 * i].first); // existing with checking STXXL_CHECK(map.erase(values2[2 * i + 1].first) == 1); // non-existing without checking map.erase_oblivious(values3[i].first); // non-existing with checking } STXXL_CHECK(map.size() == 2 * n_values - 2 * n_tests); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); map.clear(); STXXL_CHECK(map.size() == 0); // --- find and manipulate values by []-operator // make sure there are some values in our unordered_map: externally // [0..n/2) and internally [n/2..n) from values1 std::cout << "[ ]-operator..."; stats_begin = *stxxl::stats::get_instance(); map.insert(values1.begin(), values1.begin() + n_values / 2, mem_to_sort); for (unsigned_type i = n_values / 2; i < n_values; i++) { map.insert_oblivious(values1[i]); } // lookup of existing values STXXL_CHECK(map[values1[5].first] == values1[5].second); // external STXXL_CHECK(map[values1[n_values / 2 + 5].first] == values1[n_values / 2 + 5].second); // internal // manipulate existing values ++(map[values1[7].first]); ++(map[values1[n_values / 2 + 7].first]); { const_iterator cit1 = cmap.find(values1[7].first); STXXL_CHECK((*cit1).second == (*cit1).first + 1); const_iterator cit2 = cmap.find(values1[n_values / 2 + 7].first); STXXL_CHECK((*cit2).second == (*cit2).first + 1); } // lookup of non-existing values STXXL_CHECK(map[values2[5].first] == unordered_map::mapped_type()); // assignment of non-existing values map[values2[7].first] = values2[7].second; { const_iterator cit = cmap.find(values2[7].first); STXXL_CHECK((*cit).first == values2[7].second); } STXXL_CHECK(map.size() == n_values + 2); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); map.clear(); STXXL_CHECK(map.size() == 0); // --- additional bulk insert test std::cout << "additional bulk-insert..."; stats_begin = *stxxl::stats::get_instance(); map.insert(values1.begin(), values1.begin() + n_values / 2, mem_to_sort); map.insert(values1.begin() + n_values / 2, values1.end(), mem_to_sort); STXXL_CHECK(map.size() == n_values); // lookup some random values std::random_shuffle(values1.begin(), values1.end()); for (unsigned_type i = 0; i < n_tests; i++) STXXL_CHECK(cmap.find(values1[i].first) != cmap.end()); std::cout << "passed" << std::endl; STXXL_MSG(stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin); // --- test equality predicate unordered_map::key_equal key_eq = map.key_eq(); STXXL_CHECK(key_eq(42, 42)); STXXL_CHECK(!key_eq(42, 6 * 9)); std::cout << "\nAll tests passed" << std::endl; map.buffer_size(); } int main() { basic_test(); return 0; } stxxl-1.4.1/tests/containers/btree/test_btree_insert_scan.cpp000644 001411 000144 00000006044 12424126671 024352 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/btree/test_btree_insert_scan.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include struct comp_type : public std::less { static int max_value() { return std::numeric_limits::max(); } static int min_value() { return std::numeric_limits::min(); } }; typedef stxxl::btree::btree btree_type; //typedef stxxl::btree::btree btree_type; std::ostream& operator << (std::ostream& o, const std::pair& obj) { o << obj.first << " " << obj.second; return o; } struct rnd_gen { stxxl::random_number32 rnd; int operator () () { return (rnd() >> 2); } }; bool operator == (const std::pair& a, const std::pair& b) { return a.first == b.first; } int main(int argc, char* argv[]) { if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " #log_ins"); return -1; } const int log_nins = atoi(argv[1]); if (log_nins > 31) { STXXL_ERRMSG("This test can't do more than 2^31 operations, you requested 2^" << log_nins); return -1; } btree_type BTree(1024 * 128, 1024 * 128); const stxxl::uint64 nins = 1ULL << log_nins; stxxl::ran32State = (unsigned int)time(NULL); stxxl::vector Values(nins); STXXL_MSG("Generating " << nins << " random values"); stxxl::generate(Values.begin(), Values.end(), rnd_gen(), 4); stxxl::vector::const_iterator it = Values.begin(); STXXL_MSG("Inserting " << nins << " random values into btree"); for ( ; it != Values.end(); ++it) BTree.insert(std::pair(*it, double(*it) + 1.0)); STXXL_MSG("Sorting the random values"); stxxl::sort(Values.begin(), Values.end(), comp_type(), 128 * 1024 * 1024); STXXL_MSG("Deleting unique values"); stxxl::vector::iterator NewEnd = std::unique(Values.begin(), Values.end()); Values.resize(NewEnd - Values.begin()); STXXL_CHECK(BTree.size() == Values.size()); STXXL_MSG("Size without duplicates: " << Values.size()); STXXL_MSG("Comparing content"); stxxl::vector::const_iterator vIt = Values.begin(); btree_type::iterator bIt = BTree.begin(); for ( ; vIt != Values.end(); ++vIt, ++bIt) { STXXL_CHECK(*vIt == bIt->first); STXXL_CHECK(double(bIt->first) + 1.0 == bIt->second); STXXL_CHECK(bIt != BTree.end()); } STXXL_CHECK(bIt == BTree.end()); STXXL_MSG("Test passed."); return 0; } stxxl-1.4.1/tests/containers/btree/test_btree_insert_erase.cpp000644 001411 000144 00000006677 12424126671 024541 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/btree/test_btree_insert_erase.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include struct comp_type : public std::less { static int max_value() { return std::numeric_limits::max(); } static int min_value() { return std::numeric_limits::min(); } }; typedef stxxl::btree::btree btree_type; //typedef stxxl::btree::btree btree_type; std::ostream& operator << (std::ostream& o, const std::pair& obj) { o << obj.first << " " << obj.second; return o; } struct rnd_gen { stxxl::random_number32 rnd; int operator () () { return (rnd() >> 2) * 3; } }; bool operator == (const std::pair& a, const std::pair& b) { return a.first == b.first; } int main(int argc, char* argv[]) { if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " #log_ins"); return -1; } const int log_nins = atoi(argv[1]); if (log_nins > 31) { STXXL_ERRMSG("This test can't do more than 2^31 operations, you requested 2^" << log_nins); return -1; } btree_type BTree(1024 * 128, 1024 * 128); const stxxl::uint64 nins = 1ULL << log_nins; stxxl::ran32State = (unsigned int)time(NULL); stxxl::vector Values(nins); STXXL_MSG("Generating " << nins << " random values"); stxxl::generate(Values.begin(), Values.end(), rnd_gen(), 4); STXXL_MSG("Sorting the random values"); stxxl::sort(Values.begin(), Values.end(), comp_type(), 128 * 1024 * 1024); STXXL_MSG("Deleting unique values"); stxxl::vector::iterator NewEnd = std::unique(Values.begin(), Values.end()); Values.resize(NewEnd - Values.begin()); STXXL_MSG("Randomly permute input values"); stxxl::random_shuffle(Values.begin(), Values.end(), 128 * 1024 * 1024); stxxl::vector::const_iterator it = Values.begin(); STXXL_MSG("Inserting " << Values.size() << " random values into btree"); for ( ; it != Values.end(); ++it) BTree.insert(std::pair(*it, double(*it) + 1.0)); STXXL_MSG("Number of elements in btree: " << BTree.size()); STXXL_MSG("Searching " << Values.size() << " existing elements and erasing them"); stxxl::vector::const_iterator vIt = Values.begin(); for ( ; vIt != Values.end(); ++vIt) { btree_type::iterator bIt = BTree.find(*vIt); STXXL_CHECK(bIt != BTree.end()); // erasing non-existent element STXXL_CHECK(BTree.erase((*vIt) + 1) == 0); // erasing existing element STXXL_CHECK(BTree.erase(*vIt) == 1); // checking it is not there STXXL_CHECK(BTree.find(*vIt) == BTree.end()); // trying to erase it again STXXL_CHECK(BTree.erase(*vIt) == 0); } STXXL_CHECK(BTree.empty()); STXXL_MSG("Test passed."); return 0; } stxxl-1.4.1/tests/containers/btree/test_btree.cpp000644 001411 000144 00000020701 12411366426 021756 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/btree/test_btree.cpp * * A very basic test * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include struct comp_type : public std::less { static int max_value() { return std::numeric_limits::max(); } }; typedef stxxl::btree::btree btree_type; // forced instantiation template class stxxl::btree::btree; std::ostream& operator << (std::ostream& o, const std::pair& obj) { o << obj.first << " " << obj.second; return o; } #define node_cache_size (25 * 1024 * 1024) #define leaf_cache_size (25 * 1024 * 1024) int main(int argc, char* argv[]) { if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " #ins"); return -1; } btree_type BTree1(node_cache_size, leaf_cache_size); unsigned nins = atoi(argv[1]); if (nins < 100) nins = 100; stxxl::random_number32 rnd; // .begin() .end() test BTree1[10] = 100.; btree_type::iterator begin = BTree1.begin(); btree_type::iterator end = BTree1.end(); STXXL_CHECK(begin == BTree1.begin()); BTree1[5] = 50.; btree_type::iterator nbegin = BTree1.begin(); btree_type::iterator nend = BTree1.end(); STXXL_CHECK(nbegin == BTree1.begin()); STXXL_CHECK(begin != nbegin); STXXL_CHECK(end == nend); STXXL_CHECK(begin != end); STXXL_CHECK(nbegin != end); STXXL_CHECK(begin->first == 10); STXXL_CHECK(begin->second == 100); STXXL_CHECK(nbegin->first == 5); STXXL_CHECK(nbegin->second == 50); BTree1[10] = 200.; STXXL_CHECK(begin->second == 200.); btree_type::iterator it = BTree1.find(5); STXXL_CHECK(it != BTree1.end()); STXXL_CHECK(it->first == 5); STXXL_CHECK(it->second == 50.); it = BTree1.find(6); STXXL_CHECK(it == BTree1.end()); it = BTree1.find(1000); STXXL_CHECK(it == BTree1.end()); btree_type::size_type f = BTree1.erase(5); STXXL_CHECK(f == 1); f = BTree1.erase(6); STXXL_CHECK(f == 0); f = BTree1.erase(5); STXXL_CHECK(f == 0); STXXL_CHECK(BTree1.count(10) == 1); STXXL_CHECK(BTree1.count(5) == 0); it = BTree1.insert(BTree1.begin(), std::pair(7, 70.)); STXXL_CHECK(it->second == 70.); STXXL_CHECK(BTree1.size() == 2); it = BTree1.insert(BTree1.begin(), std::pair(10, 300.)); STXXL_CHECK(it->second == 200.); STXXL_CHECK(BTree1.size() == 2); // test lower_bound it = BTree1.lower_bound(6); STXXL_CHECK(it != BTree1.end()); STXXL_CHECK(it->first == 7); it = BTree1.lower_bound(7); STXXL_CHECK(it != BTree1.end()); STXXL_CHECK(it->first == 7); it = BTree1.lower_bound(8); STXXL_CHECK(it != BTree1.end()); STXXL_CHECK(it->first == 10); it = BTree1.lower_bound(11); STXXL_CHECK(it == BTree1.end()); // test upper_bound it = BTree1.upper_bound(6); STXXL_CHECK(it != BTree1.end()); STXXL_CHECK(it->first == 7); it = BTree1.upper_bound(7); STXXL_CHECK(it != BTree1.end()); STXXL_CHECK(it->first == 10); it = BTree1.upper_bound(8); STXXL_CHECK(it != BTree1.end()); STXXL_CHECK(it->first == 10); it = BTree1.upper_bound(10); STXXL_CHECK(it == BTree1.end()); it = BTree1.upper_bound(11); STXXL_CHECK(it == BTree1.end()); // test equal_range std::pair it_pair = BTree1.equal_range(1); STXXL_CHECK(BTree1.find(7) == it_pair.first); STXXL_CHECK(BTree1.find(7) == it_pair.second); it_pair = BTree1.equal_range(7); STXXL_CHECK(BTree1.find(7) == it_pair.first); STXXL_CHECK(BTree1.find(10) == it_pair.second); it_pair = BTree1.equal_range(8); STXXL_CHECK(BTree1.find(10) == it_pair.first); STXXL_CHECK(BTree1.find(10) == it_pair.second); it_pair = BTree1.equal_range(10); STXXL_CHECK(BTree1.find(10) == it_pair.first); STXXL_CHECK(BTree1.end() == it_pair.second); it_pair = BTree1.equal_range(11); STXXL_CHECK(BTree1.end() == it_pair.first); STXXL_CHECK(BTree1.end() == it_pair.second); // it = BTree1.lower_bound(0); BTree1.erase(it); STXXL_CHECK(BTree1.size() == 1); BTree1.clear(); STXXL_CHECK(BTree1.size() == 0); for (unsigned int i = 0; i < nins / 2; ++i) { BTree1[rnd() % nins] = 10.1; } STXXL_MSG("Size of map: " << BTree1.size()); BTree1.clear(); for (unsigned int i = 0; i < nins / 2; ++i) { BTree1[rnd() % nins] = 10.1; } STXXL_MSG("Size of map: " << BTree1.size()); btree_type BTree2(comp_type(), node_cache_size, leaf_cache_size); STXXL_MSG("Construction of BTree3 from BTree1 that has " << BTree1.size() << " elements"); btree_type BTree3(BTree1.begin(), BTree1.end(), comp_type(), node_cache_size, leaf_cache_size); STXXL_CHECK(BTree3 == BTree1); STXXL_MSG("Bulk construction of BTree4 from BTree1 that has " << BTree1.size() << " elements"); btree_type BTree4(BTree1.begin(), BTree1.end(), comp_type(), node_cache_size, leaf_cache_size, true); STXXL_MSG("Size of BTree1: " << BTree1.size()); STXXL_MSG("Size of BTree4: " << BTree4.size()); STXXL_CHECK(BTree4 == BTree1); STXXL_CHECK(BTree3 == BTree4); BTree4.begin()->second = 0; STXXL_CHECK(BTree3 != BTree4); STXXL_CHECK(BTree4 < BTree3); BTree4.begin()->second = 1000; STXXL_CHECK(BTree4 > BTree3); STXXL_CHECK(BTree3 != BTree4); it = BTree4.begin(); ++it; STXXL_MSG("Size of Btree4 before erase: " << BTree4.size()); BTree4.erase(it, BTree4.end()); STXXL_MSG("Size of Btree4 after erase: " << BTree4.size()); STXXL_CHECK(BTree4.size() == 1); STXXL_MSG("Size of Btree1 before erase: " << BTree1.size()); BTree1.erase(BTree1.begin(), BTree1.end()); STXXL_MSG("Size of Btree1 after erase: " << BTree1.size()); STXXL_CHECK(BTree1.empty()); // a copy of BTree3 btree_type BTree5(BTree3.begin(), BTree3.end(), comp_type(), node_cache_size, leaf_cache_size, true); STXXL_CHECK(BTree5 == BTree3); btree_type::iterator b3 = BTree3.begin(); btree_type::iterator b4 = BTree4.begin(); btree_type::iterator e3 = BTree3.end(); btree_type::iterator e4 = BTree4.end(); STXXL_MSG("Testing swapping operation (std::swap)"); std::swap(BTree4, BTree3); STXXL_CHECK(b3 == BTree4.begin()); STXXL_CHECK(b4 == BTree3.begin()); STXXL_CHECK(e3 == BTree4.end()); STXXL_CHECK(e4 == BTree3.end()); STXXL_CHECK(BTree5 == BTree4); STXXL_CHECK(BTree5 != BTree3); btree_type::const_iterator cb = BTree3.begin(); btree_type::const_iterator ce = BTree3.end(); const btree_type& CBTree3 = BTree3; cb = CBTree3.begin(); STXXL_CHECK(!(b3 == cb)); STXXL_CHECK((b3 != cb)); STXXL_CHECK(!(cb == b3)); STXXL_CHECK((cb != b3)); ce = CBTree3.end(); btree_type::const_iterator cit = CBTree3.find(0); cit = CBTree3.lower_bound(0); cit = CBTree3.upper_bound(0); std::pair cit_pair = CBTree3.equal_range(1); STXXL_CHECK(CBTree3.max_size() >= CBTree3.size()); CBTree3.key_comp(); CBTree3.value_comp(); double sum = 0.0; STXXL_MSG(*stxxl::stats::get_instance()); stxxl::timer Timer2; Timer2.start(); cit = BTree5.begin(); for ( ; cit != BTree5.end(); ++cit) sum += cit->second; Timer2.stop(); STXXL_MSG("Scanning with const iterator: " << Timer2.mseconds() << " msec"); STXXL_MSG(*stxxl::stats::get_instance()); stxxl::timer Timer1; Timer1.start(); it = BTree5.begin(); for ( ; it != BTree5.end(); ++it) sum += it->second; Timer1.stop(); STXXL_MSG("Scanning with non const iterator: " << Timer1.mseconds() << " msec"); STXXL_MSG(*stxxl::stats::get_instance()); BTree5.disable_prefetching(); BTree5.enable_prefetching(); BTree5.prefetching_enabled(); STXXL_CHECK(BTree5.prefetching_enabled()); STXXL_MSG("All tests passed successfully"); return 0; } stxxl-1.4.1/tests/containers/btree/test_btree_const_scan.cpp000644 001411 000144 00000007530 12424126671 024175 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/btree/test_btree_const_scan.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include struct comp_type : public std::less { static int max_value() { return std::numeric_limits::max(); } }; #define NODE_BLOCK_SIZE 4096 #define LEAF_BLOCK_SIZE 128 * 1024 struct my_type { stxxl::uint64 data; char filler[24]; }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << " " << obj.data; return o; } std::ostream& operator << (std::ostream& o, const std::pair& obj) { o << obj.first << " " << obj.second; return o; } typedef stxxl::btree::btree btree_type; // forced instantiation template class stxxl::btree::btree; #define node_cache_size (25 * 1024 * 1024) #define leaf_cache_size (6 * LEAF_BLOCK_SIZE) stxxl::uint64 checksum = 0; void NC(btree_type& BTree) { stxxl::uint64 sum = 0; stxxl::timer Timer1; Timer1.start(); btree_type::iterator it = BTree.begin(), end = BTree.end(); for ( ; it != end; ++it) sum += it->second.data; Timer1.stop(); STXXL_MSG("Scanning with non const iterator: " << Timer1.mseconds() << " msec"); STXXL_CHECK(sum == checksum); } void C(btree_type& BTree) { stxxl::uint64 sum = 0; stxxl::timer Timer1; Timer1.start(); btree_type::const_iterator it = BTree.begin(), end = BTree.end(); for ( ; it != end; ++it) sum += it->second.data; Timer1.stop(); STXXL_MSG("Scanning with const iterator: " << Timer1.mseconds() << " msec"); STXXL_CHECK(sum == checksum); } int main(int argc, char* argv[]) { if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " #ins"); return -1; } const unsigned nins = atoi(argv[1]); STXXL_MSG("Data set size : " << nins * sizeof(std::pair) << " bytes"); STXXL_MSG("Node cache size: " << node_cache_size << " bytes"); STXXL_MSG("Leaf cache size: " << leaf_cache_size << " bytes"); //stxxl::random_number32 rnd; std::vector > Data(nins); for (unsigned int i = 0; i < nins; ++i) { Data[i].first = i; Data[i].second.data = i; checksum += i; } { btree_type BTree1(Data.begin(), Data.end(), comp_type(), node_cache_size, leaf_cache_size, true); btree_type BTree2(Data.begin(), Data.end(), comp_type(), node_cache_size, leaf_cache_size, true); //STXXL_MSG(*stxxl::stats::get_instance()); C(BTree1); //STXXL_MSG(*stxxl::stats::get_instance()); NC(BTree2); //STXXL_MSG(*stxxl::stats::get_instance()); } { btree_type BTree1(Data.begin(), Data.end(), comp_type(), node_cache_size, leaf_cache_size, true); btree_type BTree2(Data.begin(), Data.end(), comp_type(), node_cache_size, leaf_cache_size, true); STXXL_MSG("Disabling prefetching"); BTree1.disable_prefetching(); BTree2.disable_prefetching(); //STXXL_MSG(*stxxl::stats::get_instance()); C(BTree1); //STXXL_MSG(*stxxl::stats::get_instance()); NC(BTree2); //STXXL_MSG(*stxxl::stats::get_instance()); } STXXL_MSG("All tests passed successfully"); return 0; } stxxl-1.4.1/tests/containers/btree/test_btree_insert_find.cpp000644 001411 000144 00000005476 12424126671 024356 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/btree/test_btree_insert_find.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include struct comp_type : public std::less { static int max_value() { return std::numeric_limits::max(); } static int min_value() { return std::numeric_limits::min(); } }; typedef stxxl::btree::btree btree_type; std::ostream& operator << (std::ostream& o, const std::pair& obj) { o << obj.first << " " << obj.second; return o; } struct rnd_gen { stxxl::random_number32 rnd; int operator () () { return (rnd() >> 2) * 3; } }; bool operator == (const std::pair& a, const std::pair& b) { return a.first == b.first; } int main(int argc, char* argv[]) { if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " #log_ins"); return -1; } const int log_nins = atoi(argv[1]); if (log_nins > 31) { STXXL_ERRMSG("This test can't do more than 2^31 operations, you requested 2^" << log_nins); return -1; } btree_type BTree(1024 * 128, 1024 * 128); const stxxl::uint64 nins = 1ULL << log_nins; stxxl::ran32State = (unsigned int)time(NULL); stxxl::vector Values(nins); STXXL_MSG("Generating " << nins << " random values"); stxxl::generate(Values.begin(), Values.end(), rnd_gen(), 4); stxxl::vector::const_iterator it = Values.begin(); STXXL_MSG("Inserting " << nins << " random values into btree"); for ( ; it != Values.end(); ++it) BTree.insert(std::pair(*it, double(*it) + 1.0)); STXXL_MSG("Number of elements in btree: " << BTree.size()); STXXL_MSG("Searching " << nins << " existing elements"); stxxl::vector::const_iterator vIt = Values.begin(); for ( ; vIt != Values.end(); ++vIt) { btree_type::iterator bIt = BTree.find(*vIt); STXXL_CHECK(bIt != BTree.end()); STXXL_CHECK(bIt->first == *vIt); } STXXL_MSG("Searching " << nins << " non-existing elements"); stxxl::vector::const_iterator vIt1 = Values.begin(); for ( ; vIt1 != Values.end(); ++vIt1) { btree_type::iterator bIt = BTree.find((*vIt1) + 1); STXXL_CHECK(bIt == BTree.end()); } STXXL_MSG("Test passed."); return 0; } stxxl-1.4.1/tests/containers/btree/CMakeLists.txt000644 001411 000144 00000001742 12423762013 021651 0ustar00tbusers000000 000000 ############################################################################ # tests/containers/btree/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013-2014 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(test_btree) stxxl_build_test(test_btree_const_scan) stxxl_build_test(test_btree_insert_erase) stxxl_build_test(test_btree_insert_find) stxxl_build_test(test_btree_insert_scan) stxxl_test(test_btree 10000) stxxl_test(test_btree 100000) stxxl_test(test_btree 1000000) stxxl_test(test_btree_const_scan 10000) stxxl_test(test_btree_const_scan 100000) stxxl_test(test_btree_const_scan 1000000) stxxl_test(test_btree_insert_erase 14) stxxl_test(test_btree_insert_find 14) stxxl_test(test_btree_insert_scan 14) stxxl-1.4.1/tests/containers/test_matrix.cpp000644 001411 000144 00000027471 12405152405 021064 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_matrix.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include using stxxl::int_type; using stxxl::unsigned_type; // forced instantiation template class stxxl::matrix; template class stxxl::matrix_iterator; template class stxxl::const_matrix_iterator; template class stxxl::matrix_row_major_iterator; template class stxxl::matrix_col_major_iterator; template class stxxl::const_matrix_row_major_iterator; template class stxxl::const_matrix_col_major_iterator; template class stxxl::column_vector; template class stxxl::row_vector; template struct stxxl::matrix_local::matrix_operations; struct constant_one { const constant_one& operator ++ () const { return *this; } bool empty() const { return false; } int operator * () const { return 1; } }; struct modulus_integers { private: unsigned_type step, counter, modulus; public: modulus_integers(unsigned_type start = 1, unsigned_type step = 1, unsigned_type modulus = 0) : step(step), counter(start), modulus(modulus) { } modulus_integers& operator ++ () { counter += step; if (modulus != 0 && counter >= modulus) counter %= modulus; return *this; } bool empty() const { return false; } unsigned_type operator * () const { return counter; } }; struct diagonal_matrix { private: unsigned_type order, counter, value; public: diagonal_matrix(unsigned_type order, unsigned_type value = 1) : order(order), counter(0), value(value) { } diagonal_matrix& operator ++ () { ++counter; return *this; } bool empty() const { return false; } unsigned_type operator * () const { return (counter % (order + 1) == 0) * value; } }; struct inverse_diagonal_matrix { private: unsigned_type order, counter, value; public: inverse_diagonal_matrix(unsigned_type order, unsigned_type value = 1) : order(order), counter(0), value(value) { } inverse_diagonal_matrix& operator ++ () { ++counter; return *this; } bool empty() const { return false; } unsigned_type operator * () const { return (counter % order == order - 1 - counter / order) * value; } }; template class iterator_compare { typedef std::pair error_type; CompareIterator& compiter; ValueType current_value; stxxl::vector errors; public: iterator_compare(CompareIterator& co) : compiter(co), current_value(), errors() { } iterator_compare& operator ++ () { if (current_value != *compiter) errors.push_back(error_type(current_value, *compiter)); ++compiter; return *this; } bool empty() const { return compiter.empty(); } ValueType& operator * () { return current_value; } unsigned_type get_num_errors() { return errors.size(); } stxxl::vector & get_errors() { return errors; } }; const int small_block_order = 32; // must be a multiple of 32, assuming at least 4 bytes element size const int block_order = 32; // must be a multiple of 32, assuming at least 4 bytes element size unsigned_type internal_memory = 256 * 1024 * 1024; void test1(int rank) { STXXL_MSG("multiplying two int_type matrices of rank " << rank << " block order " << small_block_order); typedef int_type value_type; typedef stxxl::block_scheduler > block_scheduler_type; typedef stxxl::matrix matrix_type; typedef matrix_type::row_vector_type row_vector_type; typedef matrix_type::column_vector_type column_vector_type; typedef matrix_type::row_major_iterator row_major_iterator; typedef matrix_type::const_row_major_iterator const_row_major_iterator; // the block_scheduler may use internal_memory byte for caching block_scheduler_type* bs_ptr = new block_scheduler_type(internal_memory); // the block_scheduler may use 16 blocks for caching //block_scheduler_type * bs_ptr = new block_scheduler_type(16 * sizeof(value_type) * small_block_order * small_block_order); block_scheduler_type& bs = *bs_ptr; // create three matrices matrix_type * a = new matrix_type(bs, rank, rank), * b = new matrix_type(bs, rank, rank), * c = new matrix_type(bs, rank, rank); stxxl::stats_data stats_before, stats_after; stxxl::matrix_operation_statistic_data matrix_stats_before, matrix_stats_after; // ------ first run for (row_major_iterator mit = a->begin(); mit != a->end(); ++mit) *mit = 1; for (row_major_iterator mit = b->begin(); mit != b->end(); ++mit) *mit = 1; bs.flush(); STXXL_MSG("start mult"); matrix_stats_before.set(); stats_before = *stxxl::stats::get_instance(); *c = *a * *b; bs.flush(); stats_after = *stxxl::stats::get_instance(); matrix_stats_after.set(); STXXL_MSG("end mult"); STXXL_MSG(matrix_stats_after - matrix_stats_before); STXXL_MSG(stats_after - stats_before); { int_type num_err = 0; for (const_row_major_iterator mit = c->cbegin(); mit != c->cend(); ++mit) num_err += (*mit != rank); STXXL_CHECK2(num_err == 0, "c had " << num_err << " errors"); } // ------ second run { int_type i = 1; for (row_major_iterator mit = a->begin(); mit != a->end(); ++mit, ++i) *mit = i; } { b->set_zero(); matrix_type::iterator mit = b->begin(); for (int_type i = 0; i < b->get_height(); ++i) { mit.set_pos(i, i); *mit = 1; } } bs.flush(); STXXL_MSG("start mult"); matrix_stats_before.set(); stats_before = *stxxl::stats::get_instance(); *c = *a * *b; bs.flush(); stats_after = *stxxl::stats::get_instance(); matrix_stats_after.set(); STXXL_MSG("end mult"); *c *= 3; *c += *a; STXXL_MSG(matrix_stats_after - matrix_stats_before); STXXL_MSG(stats_after - stats_before); { int_type num_err = 0; int_type i = 1; for (const_row_major_iterator mit = c->cbegin(); mit != c->cend(); ++mit, ++i) num_err += (*mit != (i * 4)); STXXL_CHECK2(num_err == 0, "c had " << num_err << " errors"); } { column_vector_type x(rank), y; int_type i = 0; for (column_vector_type::iterator it = x.begin(); it != x.end(); ++it) *it = ++i; y = *b * x; y = y + x; y += x; y = y - x; y -= x; y = x * 5; y *= 5; row_vector_type w(rank), z; i = 0; for (row_vector_type::iterator it = w.begin(); it != w.end(); ++it) *it = ++i; z = w * *b; z = z + w; z += w; z = z - w; z -= w; z = w * 5; z *= 5; *a = matrix_type(bs, x, w); value_type v; v = w * x; stxxl::STXXL_UNUSED(v); } delete a; delete b; delete c; delete bs_ptr; } void test2(int rank, int mult_algo_num, int sched_algo_num) { STXXL_MSG("multiplying two full double matrices of rank " << rank << ", block order " << block_order << " using " << internal_memory << " bytes internal memory, multiplication-algo " << mult_algo_num << ", scheduling-algo " << sched_algo_num); typedef double value_type; typedef stxxl::block_scheduler > block_scheduler_type; typedef stxxl::matrix matrix_type; typedef matrix_type::row_major_iterator row_major_iterator; typedef matrix_type::const_row_major_iterator const_row_major_iterator; // the block_scheduler may use internal_memory byte for caching block_scheduler_type* bs_ptr = new block_scheduler_type(internal_memory); block_scheduler_type& bs = *bs_ptr; matrix_type * a = new matrix_type(bs, rank, rank), * b = new matrix_type(bs, rank, rank), * c = new matrix_type(bs, rank, rank); stxxl::stats_data stats_before, stats_after; stxxl::matrix_operation_statistic_data matrix_stats_before, matrix_stats_after; STXXL_MSG("writing input matrices"); for (row_major_iterator mit = a->begin(); mit != a->end(); ++mit) *mit = 1; for (row_major_iterator mit = b->begin(); mit != b->end(); ++mit) *mit = 1; bs.flush(); STXXL_MSG("start of multiplication"); matrix_stats_before.set(); stats_before = *stxxl::stats::get_instance(); if (mult_algo_num >= 0) *c = a->multiply(*b, mult_algo_num, sched_algo_num); else *c = a->multiply_internal(*b, sched_algo_num); bs.flush(); stats_after = *stxxl::stats::get_instance(); matrix_stats_after.set(); STXXL_MSG("end of multiplication"); STXXL_MSG(matrix_stats_after - matrix_stats_before); STXXL_MSG(stats_after - stats_before); { int_type num_err = 0; for (const_row_major_iterator mit = c->cbegin(); mit != c->cend(); ++mit) num_err += (*mit != rank); STXXL_CHECK2(num_err == 0, "c had " << num_err << " errors"); } delete a; delete b; delete c; delete bs_ptr; } int main(int argc, char** argv) { int test_case = -1; int rank = 500; int mult_algo_num = 2; int sched_algo_num = 1; stxxl::cmdline_parser cp; cp.set_description("stxxl matrix test"); cp.set_author("Raoul Steffen "); cp.add_opt_param_int("K", "number of the test case to run: 1 or 2, or by default: all", test_case); cp.add_int('r', "rank", "", "rank of the matrices, default: 500", rank); cp.add_bytes('m', "memory", "", "internal memory to, default: 256 MiB", internal_memory); cp.add_int('a', "mult-algo", "", "use multiplication-algorithm number N\n available are:\n 0: naive_multiply_and_add\n 1: recursive_multiply_and_add\n 2: strassen_winograd_multiply_and_add\n 3: multi_level_strassen_winograd_multiply_and_add\n 4: strassen_winograd_multiply (block-interleaved pre- and postadditions)\n 5: strassen_winograd_multiply_and_add_interleaved (block-interleaved preadditions)\n 6: multi_level_strassen_winograd_multiply_and_add_block_grained\n default: 2", mult_algo_num); cp.add_int('s', "scheduling-algo", "", "use scheduling-algorithm number N\n available are:\n 0: online LRU\n 1: offline LFD\n 2: offline LRU prefetching\n default: 1", sched_algo_num); if (!cp.process(argc, argv)) return 0; switch (test_case) { default: test1(rank); for (int mult_algo = 0; mult_algo <= 6; ++mult_algo) { for (int sched_algo = 0; sched_algo <= 2; ++sched_algo) { test2(rank, mult_algo, sched_algo); } } break; case 1: test1(rank); break; case 2: test2(rank, mult_algo_num, sched_algo_num); break; } STXXL_MSG("end of test"); return 0; } stxxl-1.4.1/tests/containers/test_vector_buf.cpp000644 001411 000144 00000015353 12411366426 021721 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_vector_buf.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include using stxxl::uint64; struct my_type // 24 bytes, not a power of 2 intentionally { uint64 key; uint64 load0; uint64 load1; my_type(uint64 i = 0) : key(i), load0(i + 1), load1(1 + 42) { } bool operator == (const my_type& b) const { return (key == b.key) && (load0 == b.load0) && (load1 == b.load1); } }; //! Verify contents of the vector template void check_vector(const VectorType& v) { typedef typename VectorType::value_type value_type; for (uint64 i = 0; i < v.size(); ++i) { STXXL_CHECK(v[i] == value_type(i)); } } //! Stream object generating lots of ValueTypes template class MyStream { uint64 i; public: typedef ValueType value_type; MyStream() : i(0) { } value_type operator * () const { return value_type(i); } MyStream& operator ++ () { ++i; return *this; } bool empty() const { return false; } }; template void test_vector_buf(uint64 size) { typedef typename stxxl::VECTOR_GENERATOR::result vector_type; typedef typename vector_type::iterator vector_iterator_type; { // fill vector using element access stxxl::scoped_print_timer tm("element access"); vector_type vec(size); for (uint64 i = 0; i < size; ++i) vec[i] = ValueType(i); check_vector(vec); } { // fill vector using iterator access stxxl::scoped_print_timer tm("iterator access"); vector_type vec(size); vector_iterator_type vi = vec.begin(); for (uint64 i = 0; i < size; ++i, ++vi) *vi = ValueType(i); check_vector(vec); } { // fill vector using vector_bufwriter stxxl::scoped_print_timer tm("vector_bufwriter"); vector_type vec(size); typename vector_type::bufwriter_type writer(vec.begin()); for (uint64 i = 0; i < size; ++i) writer << ValueType(i); writer.finish(); check_vector(vec); } { // fill empty vector using vector_bufwriter stxxl::scoped_print_timer tm("empty vector_bufwriter"); vector_type vec; typename vector_type::bufwriter_type writer(vec); for (uint64 i = 0; i < size; ++i) writer << ValueType(i); writer.finish(); check_vector(vec); } vector_type vec(size); { // fill vector using materialize stxxl::scoped_print_timer tm("materialize"); MyStream stream; stxxl::stream::materialize(stream, vec.begin(), vec.end()); check_vector(vec); } { // read vector using vector_bufreader stxxl::scoped_print_timer tm("vector_bufreader"); const vector_type& cvec = vec; typename vector_type::bufreader_type reader(cvec.begin(), cvec.end()); for (uint64 i = 0; i < size; ++i) { STXXL_CHECK(!reader.empty()); STXXL_CHECK(reader.size() == size - i); ValueType pv = *reader; ValueType v; reader >> v; STXXL_CHECK(v == ValueType(i)); STXXL_CHECK(pv == v); STXXL_CHECK(reader.size() == size - i - 1); } STXXL_CHECK(reader.empty()); // rewind reader and read again reader.rewind(); for (uint64 i = 0; i < size; ++i) { STXXL_CHECK(!reader.empty()); STXXL_CHECK(reader.size() == size - i); ValueType pv = *reader; ValueType v; reader >> v; STXXL_CHECK(v == ValueType(i)); STXXL_CHECK(pv == v); STXXL_CHECK(reader.size() == size - i - 1); } STXXL_CHECK(reader.empty()); } { // read vector using vector_bufreader_reverse stxxl::scoped_print_timer tm("vector_bufreader_reverse"); const vector_type& cvec = vec; typename vector_type::bufreader_reverse_type reader(cvec.begin(), cvec.end()); for (uint64 i = 0; i < size; ++i) { STXXL_CHECK(!reader.empty()); STXXL_CHECK(reader.size() == size - i); ValueType pv = *reader; ValueType v; reader >> v; STXXL_CHECK(v == ValueType(size - i - 1)); STXXL_CHECK(pv == v); STXXL_CHECK(reader.size() == size - i - 1); } STXXL_CHECK(reader.empty()); // rewind reader and read again reader.rewind(); for (uint64 i = 0; i < size; ++i) { STXXL_CHECK(!reader.empty()); STXXL_CHECK(reader.size() == size - i); ValueType pv = *reader; ValueType v; reader >> v; STXXL_CHECK(v == ValueType(size - i - 1)); STXXL_CHECK(pv == v); STXXL_CHECK(reader.size() == size - i - 1); } STXXL_CHECK(reader.empty()); } #if STXXL_HAVE_CXX11_RANGE_FOR_LOOP { // read vector using C++11 for loop construct stxxl::scoped_print_timer tm("C++11 for loop"); uint64 i = 0; for (auto it : vec) { STXXL_CHECK(it == ValueType(i)); ++i; } STXXL_CHECK(i == vec.size()); } { // read vector using C++11 for loop construct stxxl::scoped_print_timer tm("C++11 bufreader for loop"); typedef typename vector_type::bufreader_type bufreader_type; uint64 i = 0; for (auto it : bufreader_type(vec)) { STXXL_CHECK(it == ValueType(i)); ++i; } STXXL_CHECK(i == vec.size()); } #endif } int main(int argc, char* argv[]) { int size = (argc > 1) ? atoi(argv[1]) : 16; STXXL_MSG("Testing stxxl::vector with even size"); test_vector_buf(size * 1024 * 1024); STXXL_MSG("Testing stxxl::vector with odd size"); test_vector_buf(size * 1024 * 1024 + 501 + 42); STXXL_MSG("Testing stxxl::vector"); test_vector_buf(size * 1024 * 1024 + 501 + 42); STXXL_MSG("Testing stxxl::vector"); test_vector_buf(size * 1024 * 1024); return 0; } stxxl-1.4.1/tests/containers/test_sorter.cpp000644 001411 000144 00000010400 12405375303 021062 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_sorter.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2012 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_sorter.cpp //! This is an example of how to use \c stxxl::sorter() container #include #include #define RECORD_SIZE 16 struct my_type { typedef unsigned key_type; key_type m_key; char m_data[RECORD_SIZE - sizeof(key_type)]; key_type key() const { return m_key; } my_type() { } my_type(key_type k) : m_key(k) { #if STXXL_WITH_VALGRIND memset(m_data, 0, sizeof(m_data)); #endif } static my_type min_value() { return my_type(std::numeric_limits::min()); } static my_type max_value() { return my_type(std::numeric_limits::max()); } ~my_type() { } }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.m_key; return o; } bool operator == (const my_type& a, const my_type& b) { return a.key() == b.key(); } bool operator <= (const my_type& a, const my_type& b) { return a.key() <= b.key(); } struct Comparator : public std::less { inline bool operator () (const my_type& a, const my_type& b) const { return a.key() < b.key(); } my_type min_value() const { return my_type::min_value(); } my_type max_value() const { return my_type::max_value(); } }; // forced instantiation template class stxxl::sorter; int main() { #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif unsigned memory_to_use = 128 * 1024 * 1024; enum { block_size = 8192 }; // comparator object used for sorters Comparator cmp; // construct sorter type: stxxl::sorter typedef stxxl::sorter sorter_type; { // test small number of items that can be sorted internally sorter_type s(cmp, memory_to_use); // put in some items s.push(42); s.push(0); s.push(23); // finish input, switch to sorting stage. s.sort(); STXXL_CHECK(*s == 0); ++s; STXXL_CHECK(*s == 23); ++s; STXXL_CHECK(*s == 42); ++s; STXXL_CHECK(s.empty()); } { // large test with 384 MiB items const stxxl::uint64 n_records = stxxl::int64(384) * stxxl::int64(1024 * 1024) / sizeof(my_type); sorter_type s(cmp, memory_to_use); stxxl::random_number32 rnd; STXXL_MSG("Filling sorter..., input size = " << n_records << " elements (" << ((n_records * sizeof(my_type)) >> 20) << " MiB)"); for (stxxl::uint64 i = 0; i < n_records; i++) { STXXL_CHECK(s.size() == i); s.push(1 + (rnd() % 0xfffffff)); } // finish input, switch to sorting stage. s.sort(); STXXL_MSG("Checking order..."); STXXL_CHECK(!s.empty()); STXXL_CHECK(s.size() == n_records); my_type prev = *s; // get first item ++s; stxxl::uint64 count = n_records - 1; while (!s.empty()) { STXXL_CHECK(s.size() == count); if (!(prev <= *s)) STXXL_MSG("WRONG"); STXXL_CHECK(prev <= *s); ++s; --count; } STXXL_MSG("OK"); // rewind and read output again s.rewind(); STXXL_MSG("Checking order again..."); STXXL_CHECK(!s.empty()); STXXL_CHECK(s.size() == n_records); prev = *s; // get first item ++s; while (!s.empty()) { if (!(prev <= *s)) STXXL_MSG("WRONG"); STXXL_CHECK(prev <= *s); ++s; } STXXL_MSG("OK"); STXXL_CHECK(s.size() == 0); STXXL_MSG("Done"); } return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/containers/test_ext_merger.cpp000644 001411 000144 00000005541 12405375303 021717 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_ext_merger.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include typedef int my_type; typedef stxxl::typed_block<4096, my_type> block_type; struct dummy_merger { int& cnt; dummy_merger(int& c) : cnt(c) { } template void multi_merge(OutputIterator b, OutputIterator e) { while (b != e) { * b = cnt; ++b; ++cnt; } } }; struct my_cmp : public std::greater { my_type min_value() const { return std::numeric_limits::max(); } my_type max_value() const { return std::numeric_limits::min(); } }; my_type * make_sequence(dummy_merger& dummy, int l) { my_type* seq = new my_type[l + 1]; // + sentinel dummy.multi_merge(seq, seq + l); seq[l] = my_cmp().min_value(); // sentinel return seq; } // forced instantiation template class stxxl::priority_queue_local::ext_merger; template class stxxl::priority_queue_local::loser_tree; using stxxl::priority_queue_local::ext_merger; using stxxl::priority_queue_local::loser_tree; int main() { stxxl::read_write_pool pool(1, 2); int cnt = 0; dummy_merger dummy(cnt); std::vector output(1024 * 3); ext_merger merger(&pool); merger.insert_segment(dummy, 1024 * 3); cnt = 20; merger.insert_segment(dummy, 1024 * 4); cnt = 10; merger.insert_segment(dummy, 1024 * 4); cnt = -100; merger.insert_segment(dummy, 1024 * 4); merger.insert_segment(dummy, 1024 * 4); merger.multi_merge(output.begin(), output.end()); STXXL_CHECK(stxxl::is_sorted(output.begin(), output.end())); loser_tree loser; my_type* seq1 = make_sequence(dummy, 1024); cnt = 20; my_type* seq2 = make_sequence(dummy, 1024); cnt = 10; my_type* seq3 = make_sequence(dummy, 1024); cnt = -100; my_type* seq4 = make_sequence(dummy, 1024); my_type* out = new my_type[4 * 1024]; loser.init(); loser.insert_segment(seq1, 1024); loser.insert_segment(seq2, 1024); loser.insert_segment(seq3, 1024); loser.insert_segment(seq4, 1024); loser.multi_merge(out, out + 1024); STXXL_CHECK(stxxl::is_sorted(out, out + 1024)); delete[] out; } stxxl-1.4.1/tests/containers/test_many_stacks.cpp000644 001411 000144 00000002340 12405375303 022064 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_many_stacks.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_many_stacks.cpp //! This is an example of how to use \c stxxl::STACK_GENERATOR class //! to generate an \b external stack type //! with \c stxxl::grow_shrink_stack implementation, \b four blocks per page, //! block size \b 4096 bytes #include // forced instantiation template class stxxl::STACK_GENERATOR; int main(int argc, char* argv[]) { typedef stxxl::STACK_GENERATOR::result ext_stack_type; if (argc < 2) { STXXL_MSG("Usage: " << argv[0] << " number_of_stacks"); return -1; } ext_stack_type* my_stacks = new ext_stack_type[atoi(argv[1])]; delete[] my_stacks; return 0; } stxxl-1.4.1/tests/containers/test_deque.cpp000644 001411 000144 00000007122 12405152405 020652 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_deque.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include int main(int argc, char* argv[]) { if (argc != 2) { STXXL_MSG("Usage: " << argv[0] << " #ops"); return -1; } stxxl::deque Deque; stxxl::deque::const_iterator b = Deque.begin(); stxxl::deque::const_iterator e = Deque.end(); STXXL_CHECK(b == e); Deque.push_front(1); Deque.push_front(2); Deque.push_front(3); b = Deque.begin(); STXXL_CHECK(b != e); Deque.push_back(5); std::copy(Deque.begin(), Deque.end(), std::ostream_iterator(std::cout, " ")); stxxl::random_number32 rand; stxxl::deque XXLDeque; std::deque STDDeque; stxxl::uint64 ops = stxxl::atouint64(argv[1]); for (stxxl::uint64 i = 0; i < ops; ++i) { unsigned curOP = rand() % 6; unsigned value = rand(); switch (curOP) { case 0: case 1: XXLDeque.push_front(value); STDDeque.push_front(value); break; case 2: XXLDeque.push_back(value); STDDeque.push_back(value); break; case 3: if (!XXLDeque.empty()) { XXLDeque.pop_front(); STDDeque.pop_front(); } break; case 4: if (!XXLDeque.empty()) { XXLDeque.pop_back(); STDDeque.pop_back(); } break; case 5: if (XXLDeque.size() > 0) { stxxl::deque::iterator XXLI = XXLDeque.begin() + (value % XXLDeque.size()); std::deque::iterator STDI = STDDeque.begin() + (value % STDDeque.size()); *XXLI = value; *STDI = value; unsigned value1 = rand(); if (XXLI - XXLDeque.begin() == 0) break; XXLI = XXLI - (value1 % (XXLI - XXLDeque.begin())); STDI = STDI - (value1 % (STDI - STDDeque.begin())); *XXLI = value1; *STDI = value1; } break; } STXXL_CHECK(XXLDeque.empty() == STDDeque.empty()); STXXL_CHECK(XXLDeque.size() == STDDeque.size()); STXXL_CHECK(XXLDeque.end() - XXLDeque.begin() == STDDeque.end() - STDDeque.begin()); //STXXL_CHECK(std::equal(XXLDeque.begin(),XXLDeque.end(),STDDeque.begin() _STXXL_FORCE_SEQUENTIAL)); if (XXLDeque.size() > 0) { STXXL_CHECK(XXLDeque.back() == STDDeque.back()); STXXL_CHECK(XXLDeque.front() == STDDeque.front()); } if (!(i % 100000)) { STXXL_CHECK(std::equal(XXLDeque.begin(), XXLDeque.end(), STDDeque.begin() _STXXL_FORCE_SEQUENTIAL)); STXXL_MSG("Operations done: " << i << " size: " << STDDeque.size()); } } return 0; } // forced instantiation template class stxxl::deque; typedef stxxl::deque deque_type; template class stxxl::deque_iterator; template class stxxl::const_deque_iterator; stxxl-1.4.1/tests/containers/CMakeLists.txt000644 001411 000144 00000004132 12411366426 020551 0ustar00tbusers000000 000000 ############################################################################ # tests/containers/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ add_subdirectory(btree) add_subdirectory(hash_map) stxxl_build_test(test_deque) stxxl_build_test(test_ext_merger) stxxl_build_test(test_ext_merger2) stxxl_build_test(test_iterators) stxxl_build_test(test_many_stacks) stxxl_build_test(test_matrix) stxxl_build_test(test_migr_stack) stxxl_build_test(test_pqueue) stxxl_build_test(test_queue) stxxl_build_test(test_queue2) stxxl_build_test(test_sequence) stxxl_build_test(test_sorter) stxxl_build_test(test_stack) stxxl_build_test(test_vector) stxxl_build_test(test_vector_buf) stxxl_build_test(test_vector_export) stxxl_build_test(test_vector_sizes) stxxl_test(test_deque 3333333) stxxl_test(test_ext_merger) stxxl_test(test_ext_merger2) stxxl_test(test_iterators) stxxl_test(test_many_stacks 42) stxxl_test(test_matrix) stxxl_extra_test(test_matrix --rank 2000) stxxl_test(test_migr_stack) stxxl_test(test_pqueue) stxxl_test(test_queue) stxxl_test(test_queue2 200) stxxl_test(test_sequence) stxxl_test(test_sorter) stxxl_test(test_stack 1024) stxxl_test(test_vector) stxxl_test(test_vector_buf) stxxl_test(test_vector_export) stxxl_test(test_vector_sizes "${STXXL_TMPDIR}/out" syscall) if(STXXL_HAVE_MMAP_FILE) stxxl_test(test_vector_sizes "${STXXL_TMPDIR}/out" mmap) endif(STXXL_HAVE_MMAP_FILE) if(USE_BOOST) #-tb: boostfd does not work right with vector, because boostfd does not #correctly support set_size() (truncate and extending of files). FIXME #stxxl_test(test_vector_sizes "${STXXL_TMPDIR}/out" boostfd) endif(USE_BOOST) # TESTS_MAP stxxl_build_test(test_map) stxxl_build_test(test_map_random) stxxl_test(test_map 8) stxxl_test(test_map_random 2000) #-tb longer test for map stxxl_extra_test(test_map 16) stxxl-1.4.1/tests/containers/test_pqueue.cpp000644 001411 000144 00000007730 12405152405 021060 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_pqueue.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_pqueue.cpp //! This is an example of how to use \c stxxl::PRIORITY_QUEUE_GENERATOR //! and \c stxxl::priority_queue #include #include #include #define RECORD_SIZE 128 struct my_type { typedef int key_type; key_type key; char data[RECORD_SIZE - sizeof(key_type)]; my_type() { } explicit my_type(key_type k) : key(k) { #if STXXL_WITH_VALGRIND memset(data, 0, sizeof(data)); #endif } }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.key; return o; } struct my_cmp : std::binary_function // greater { bool operator () (const my_type& a, const my_type& b) const { return a.key > b.key; } my_type min_value() const { return my_type(std::numeric_limits::max()); } }; // forced instantiation const unsigned volume = 1024 * 1024; // in KiB template class stxxl::PRIORITY_QUEUE_GENERATOR; int main() { /* unsigned BufferSize1_ = 32, // equalize procedure call overheads etc. unsigned N_ = 512, // bandwidth unsigned IntKMAX_ = 64, // maximal arity for internal mergers unsigned IntLevels_ = 4, unsigned BlockSize_ = (2*1024*1024), unsigned ExtKMAX_ = 64, // maximal arity for external mergers unsigned ExtLevels_ = 2, */ //typedef priority_queue > pq_type; typedef stxxl::PRIORITY_QUEUE_GENERATOR gen; typedef gen::result pq_type; typedef pq_type::block_type block_type; STXXL_MSG("Block size: " << block_type::raw_size); STXXL_MSG("AI: " << gen::AI); STXXL_MSG("X : " << gen::X); STXXL_MSG("N : " << gen::N); STXXL_MSG("AE: " << gen::AE); stxxl::timer Timer; Timer.start(); const unsigned mem_for_pools = 128 * 1024 * 1024; stxxl::read_write_pool pool((mem_for_pools / 2) / block_type::raw_size, (mem_for_pools / 2) / block_type::raw_size); pq_type p(pool); stxxl::int64 nelements = stxxl::int64(volume * 1024 / sizeof(my_type)), i; STXXL_MSG("Internal memory consumption of the priority queue: " << p.mem_cons() << " B"); STXXL_MSG("Max elements: " << nelements); for (i = 0; i < nelements; i++) { if ((i % (1024 * 1024)) == 0) STXXL_MSG("Inserting element " << i); p.push(my_type(int(nelements - i))); } Timer.stop(); STXXL_MSG("Time spent for filling: " << Timer.seconds() << " s"); STXXL_CHECK(p.size() == (stxxl::uint64)nelements); #if 0 // test swap pq_type p1(pool); std::swap(p, p1); std::swap(p, p1); #endif STXXL_MSG("Internal memory consumption of the priority queue: " << p.mem_cons() << " B"); Timer.reset(); Timer.start(); for (i = 0; i < (nelements); ++i) { STXXL_CHECK(!p.empty()); //STXXL_MSG( p.top() ); STXXL_CHECK(p.top().key == i + 1); p.pop(); if ((i % (1024 * 1024)) == 0) STXXL_MSG("Element " << i << " popped"); } Timer.stop(); STXXL_MSG("Time spent for removing elements: " << Timer.seconds() << " s"); STXXL_CHECK(p.size() == 0); STXXL_CHECK(p.empty()); STXXL_MSG("Internal memory consumption of the priority queue: " << p.mem_cons() << " B"); } stxxl-1.4.1/tests/containers/test_vector_sizes.cpp000644 001411 000144 00000011450 12405375303 022271 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_vector_sizes.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include typedef int my_type; typedef stxxl::VECTOR_GENERATOR::result vector_type; typedef vector_type::block_type block_type; void test_write(const char* fn, const char* ft, stxxl::unsigned_type sz, my_type ofs) { stxxl::file* f = stxxl::create_file(ft, fn, stxxl::file::CREAT | stxxl::file::DIRECT | stxxl::file::RDWR); { vector_type v(f); v.resize(sz); STXXL_MSG("writing " << v.size() << " elements"); for (stxxl::unsigned_type i = 0; i < v.size(); ++i) v[i] = ofs + (int)i; } delete f; } template void test_rdwr(const char* fn, const char* ft, stxxl::unsigned_type sz, my_type ofs) { stxxl::file* f = stxxl::create_file(ft, fn, stxxl::file::DIRECT | stxxl::file::RDWR); { Vector v(f); STXXL_MSG("reading " << v.size() << " elements (RDWR)"); STXXL_CHECK(v.size() == sz); for (stxxl::unsigned_type i = 0; i < v.size(); ++i) STXXL_CHECK(v[i] == ofs + my_type(i)); } delete f; } template void test_rdonly(const char* fn, const char* ft, stxxl::unsigned_type sz, my_type ofs) { stxxl::file* f = stxxl::create_file(ft, fn, stxxl::file::DIRECT | stxxl::file::RDONLY); { Vector v(f); STXXL_MSG("reading " << v.size() << " elements (RDONLY)"); STXXL_CHECK(v.size() == sz); for (stxxl::unsigned_type i = 0; i < v.size(); ++i) STXXL_CHECK(v[i] == ofs + my_type(i)); } delete f; } void test(const char* fn, const char* ft, stxxl::unsigned_type sz, my_type ofs) { test_write(fn, ft, sz, ofs); test_rdwr(fn, ft, sz, ofs); test_rdwr(fn, ft, sz, ofs); // 2013-tb: there is a bug with read-only vectors on mmap backed files: // copying from mmapped area will fail for invalid ranges at the end, // whereas a usual read() will just stop short at the end. The read-only // vector however will always read the last block in full, thus causing a // segfault with mmap files. FIXME if (strcmp(ft, "mmap") == 0) return; test_rdonly(fn, ft, sz, ofs); //-tb: vector always writes data! FIXME //test_rdonly(fn, ft, sz, ofs); } int main(int argc, char** argv) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " file [filetype]" << std::endl; return -1; } stxxl::config::get_instance(); const char* fn = argv[1]; const char* ft = (argc >= 3) ? argv[2] : "syscall"; stxxl::unsigned_type start_elements = 42 * block_type::size; STXXL_MSG("using " << ft << " file"); // multiple of block size STXXL_MSG("running test with " << start_elements << " items"); test(fn, ft, start_elements, 100000000); // multiple of page size, but not block size STXXL_MSG("running test with " << start_elements << " + 4096 items"); test(fn, ft, start_elements + 4096, 200000000); // multiple of neither block size nor page size STXXL_MSG("running test with " << start_elements << " + 4096 + 23 items"); test(fn, ft, start_elements + 4096 + 23, 300000000); // truncate 1 byte { stxxl::syscall_file f(fn, stxxl::file::DIRECT | stxxl::file::RDWR); STXXL_MSG("file size is " << f.size() << " bytes"); f.set_size(f.size() - 1); STXXL_MSG("truncated to " << f.size() << " bytes"); } // will truncate after the last complete element test_rdwr(fn, ft, start_elements + 4096 + 23 - 1, 300000000); // truncate 1 more byte { stxxl::syscall_file f(fn, stxxl::file::DIRECT | stxxl::file::RDWR); STXXL_MSG("file size is " << f.size() << " bytes"); f.set_size(f.size() - 1); STXXL_MSG("truncated to " << f.size() << " bytes"); } // will not truncate //-tb: vector already writes data! TODO //test_rdonly(fn, ft, start_elements + 4096 + 23 - 2, 300000000); // check final size { stxxl::syscall_file f(fn, stxxl::file::DIRECT | stxxl::file::RDWR); STXXL_MSG("file size is " << f.size() << " bytes"); STXXL_CHECK(f.size() == (start_elements + 4096 + 23 - 1) * sizeof(my_type) - 1); } { stxxl::syscall_file f(fn, stxxl::file::DIRECT | stxxl::file::RDWR); f.close_remove(); } } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/containers/test_iterators.cpp000644 001411 000144 00000025121 12405375303 021566 0ustar00tbusers000000 000000 /*************************************************************************** * tests/containers/test_iterators.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) template const char * _() { const char* start = strchr(STXXL_PRETTY_FUNCTION_NAME, '['); if (start == NULL) return "unknown"; else return start; } template void dump_iterator_info(I&) { STXXL_MSG(STXXL_PRETTY_FUNCTION_NAME); STXXL_MSG(" category: " << _::iterator_category>()); STXXL_MSG(" value_type: " << _::value_type>()); STXXL_MSG(" difference_type: " << _::difference_type>()); STXXL_MSG(" pointer: " << _::pointer>()); STXXL_MSG(" reference: " << _::reference>()); } template void dump_container_info(C&) { STXXL_MSG(STXXL_PRETTY_FUNCTION_NAME); STXXL_MSG(" value_type: " << _()); STXXL_MSG(" size_type: " << _()); STXXL_MSG(" difference_type: " << _()); STXXL_MSG(" pointer: " << _()); STXXL_MSG(" const_pointer: " << _()); STXXL_MSG(" reference: " << _()); STXXL_MSG(" const_reference: " << _()); STXXL_MSG(" iterator: " << _()); STXXL_MSG(" const_iterator: " << _()); } template struct modify { void operator () (T& obj) const { ++obj; } }; template bool test_inc_dec(Iterator it) { Iterator i = it; ++i; i++; --i; i--; STXXL_CHECK(it == i); return it == i; } template bool test_inc_dec_random(Iterator it) { Iterator i = it; ++i; i = i + 2; i++; i += 3; --i; i = i - 3; i--; i -= 2; STXXL_CHECK(it == i); return it == i; } template struct test_comparison_lt_gt { void operator () (IteratorA, IteratorB) { // operators <, <=, >=, > are not available in all iterator categories } }; template struct test_comparison_lt_gt { void operator () (IteratorA a, IteratorB b) { STXXL_CHECK(! (a < b)); STXXL_CHECK((a <= b)); STXXL_CHECK(! (a > b)); STXXL_CHECK((a >= b)); STXXL_CHECK(! (b < a)); STXXL_CHECK((b <= a)); STXXL_CHECK(! (b > a)); STXXL_CHECK((b >= a)); } }; template void test_comparison(IteratorA a, IteratorB b) { STXXL_CHECK((a == b)); STXXL_CHECK(! (a != b)); STXXL_CHECK((b == a)); STXXL_CHECK(! (b != a)); test_comparison_lt_gt::iterator_category>() (a, b); } template void test_operators(Iterator it) { *it; it.operator -> (); test_comparison(it, it); } template void test(svt& sv) { dump_container_info(sv); typedef const svt csvt; typedef typename svt::value_type value_type; sv[0] = 108; typename svt::iterator svi = sv.begin(); dump_iterator_info(svi); modify() (*svi); typename svt::const_iterator svci = sv.begin(); dump_iterator_info(svci); //modify()(*svci); // read-only typename csvt::iterator xsvi = sv.begin(); modify() (*xsvi); // test assignment svci = xsvi; //xsvi = svci; // not allowed typename csvt::const_iterator xsvci = sv.begin(); //modify()(*xsvci); // read-only // test comparison between const and non-const iterators test_comparison(svci, xsvi); // test increment/decrement test_inc_dec(svi); test_inc_dec(svci); test_inc_dec(xsvi); test_inc_dec(xsvci); // test operators test_operators(svi); test_operators(svci); test_operators(xsvi); test_operators(xsvci); // test forward iteration for (typename svt::iterator i = sv.begin(); i != sv.end(); ++i) ; /////////////////////////////////////////////////////////////////////////// csvt& csv = sv; //csv[0] = 108; // read-only //typename csvt::iterator csvi = csv.begin(); // read-only //modify()(*csvi); // read-only typename csvt::const_iterator csvci = csv.begin(); //modify()(*csvci); // read-only //typename svt::iterator xcsvi = csv.begin(); // read-only //modify()(*xcsvi); // read-only typename svt::const_iterator xcsvci = csv.begin(); //modify()(*csvci); // read-only // test increment/decrement test_inc_dec(csvci); test_inc_dec(xcsvci); // test operators test_operators(csvci); test_operators(xcsvci); // test forward iteration for (typename svt::const_iterator ci = sv.begin(); ci != sv.end(); ++ci) ; } template void test_reverse(svt& sv) { dump_container_info(sv); typedef const svt csvt; typedef typename svt::value_type value_type; sv[0] = 108; typename svt::reverse_iterator svi = sv.rbegin(); dump_iterator_info(svi); modify() (*svi); typename svt::const_reverse_iterator svci = sv.rbegin(); dump_iterator_info(svci); //modify()(*svci); // read-only typename csvt::reverse_iterator xsvi = sv.rbegin(); modify() (*xsvi); // test assignment svci = xsvi; //xsvi = svci; // not allowed typename csvt::const_reverse_iterator xsvci = sv.rbegin(); //modify()(*xsvci); // read-only #if !defined(__GNUG__) || (GCC_VERSION >= 40100) // test comparison between const and non-const iterators test_comparison(svci, xsvi); #endif // test increment/decrement test_inc_dec(svi); test_inc_dec(svci); test_inc_dec(xsvi); test_inc_dec(xsvci); // test operators test_operators(svi); test_operators(svci); test_operators(xsvi); test_operators(xsvci); // test forward iteration for (typename svt::reverse_iterator i = sv.rbegin(); i != sv.rend(); ++i) ; /////////////////////////////////////////////////////////////////////////// csvt& csv = sv; //csv[0] = 108; // read-only //typename csvt::reverse_iterator csvi = csv.rbegin(); // read-only //modify()(*csvi); // read-only typename csvt::const_reverse_iterator csvci = csv.rbegin(); //modify()(*csvci); // read-only //typename svt::reverse_iterator xcsvi = csv.rbegin(); // read-only //modify()(*xcsvi); // read-only typename svt::const_reverse_iterator xcsvci = csv.rbegin(); //modify()(*csvci); // read-only // test increment/decrement test_inc_dec(csvci); test_inc_dec(xcsvci); // test operators test_operators(csvci); test_operators(xcsvci); // test forward iteration #if !defined(__GNUG__) || (GCC_VERSION >= 40100) for (typename svt::const_reverse_iterator ci = sv.rbegin(); ci != sv.rend(); ++ci) ; #else for (typename svt::const_reverse_iterator ci = sv.rbegin(); ci != typename svt::const_reverse_iterator(sv.rend()); ++ci) ; #endif } template void test_random_access(svt& sv) { typename svt::const_iterator svci = sv.begin(); typename svt::iterator xsvi = sv.begin(); // test subtraction of const and non-const iterators svci - xsvi; xsvi - svci; // bracket operators svci[0]; xsvi[0]; //svci[0] = 1; // read-only xsvi[0] = 1; // test +, -, +=, -= test_inc_dec_random(svci); test_inc_dec_random(xsvi); } template void test_random_access_reverse(svt& sv) { typename svt::const_reverse_iterator svcri = sv.rbegin(); typename svt::reverse_iterator xsvri = sv.rbegin(); #if !defined(__GNUG__) || (GCC_VERSION >= 40100) // test subtraction of const and non-const iterators svcri - xsvri; xsvri - svcri; #endif // bracket operators svcri[0]; //svcri[0] = 1; // read-only #ifndef _LIBCPP_VERSION // see note about problem with std::reverse_iterator::operator[] in // vector_iterator::operator [], in effect: // vector_reverse_iterator::operator[] does not work with libc++. xsvri[0]; xsvri[0] = 1; #endif // test +, -, +=, -= test_inc_dec_random(svcri); test_inc_dec_random(xsvri); } typedef float key_type; typedef double data_type; struct cmp : public std::less { static key_type min_value() { return std::numeric_limits::min(); } static key_type max_value() { return std::numeric_limits::max(); } }; template <> struct modify > { void operator () (std::pair& obj) const { ++(obj.second); } }; int main() { std::vector V(8); test(V); test_reverse(V); test_random_access(V); test_random_access_reverse(V); stxxl::vector Vector(8); test(Vector); test_reverse(Vector); test_random_access(Vector); test_random_access_reverse(Vector); std::map M; M[4] = 8; M[15] = 16; M[23] = 42; test(M); test_reverse(M); #if !defined(__GNUG__) || (GCC_VERSION >= 30400) typedef stxxl::map map_type; map_type Map(4096 * 10, 4096 * 10); Map[4] = 8; Map[15] = 16; Map[23] = 42; test(Map); test_reverse(Map); #endif std::deque D(8); test(D); test_reverse(D); test_random_access(D); test_random_access_reverse(D); stxxl::deque Deque(8); test(Deque); test_reverse(Deque); test_random_access(Deque); test_random_access_reverse(Deque); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/algo/test_scan.cpp000644 001411 000144 00000005134 12405375303 017255 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_scan.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/test_scan.cpp //! This is an example of how to use \c stxxl::for_each() and \c stxxl::find() algorithms #include #include #include #include using stxxl::int64; using stxxl::timestamp; template struct counter { type value; counter(type v = type(0)) : value(v) { } type operator () () { type old_val = value; value++; return old_val; } }; template struct square { void operator () (type& arg) { arg = arg * arg; } }; template struct fill_value { type val; fill_value(const type& v_) : val(v_) { } type operator () () { return val; } }; int main() { stxxl::vector::size_type i; stxxl::vector v(64 * int64(1024 * 1024)); double b, e; STXXL_MSG("write " << (v.end() - v.begin()) << " elements ..."); stxxl::generate(v.begin(), v.end(), counter(), 4); STXXL_MSG("for_each_m ..."); b = timestamp(); stxxl::for_each_m(v.begin(), v.end(), square(), 4); e = timestamp(); STXXL_MSG("for_each_m time: " << (e - b)); STXXL_MSG("check"); for (i = 0; i < v.size(); ++i) { STXXL_CHECK2(v[i] == int64(i * i), "Error at position " << i); } STXXL_MSG("Pos of value 1023: " << (stxxl::find(v.begin(), v.end(), 1023, 4) - v.begin())); STXXL_MSG("Pos of value 1048576: " << (stxxl::find(v.begin(), v.end(), 1024 * 1024, 4) - v.begin())); STXXL_MSG("Pos of value 1024: " << (stxxl::find(v.begin(), v.end(), 32 * 32, 4) - v.begin())); STXXL_MSG("generate ..."); b = timestamp(); stxxl::generate(v.begin() + 1, v.end() - 1, fill_value(555), 4); e = timestamp(); STXXL_MSG("generate: " << (e - b)); STXXL_MSG("check"); STXXL_CHECK2(v[0] == 0, "Error at position " << 0); STXXL_CHECK2(v[v.size() - 1] == int64((v.size() - 1) * (v.size() - 1)), "Error at position " << v.size() - 1); for (i = 1; i < v.size() - 1; ++i) { STXXL_CHECK2(v[i] == 555, "Error at position " << i); } return 0; } stxxl-1.4.1/tests/algo/test_bad_cmp.cpp000644 001411 000144 00000011625 12405375303 017720 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_bad_cmp.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2011 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/test_bad_cmp.cpp //! This is an example of how NOT to use \c stxxl::sort() algorithm. //! Here min_value and max_value are used as keys which is forbidden. #include #include #include struct my_type { typedef unsigned key_type; key_type m_key; key_type m_data; my_type() { } my_type(key_type k) : m_key(k), m_data(0) { } static my_type min_value() { return my_type(std::numeric_limits::min()); } static my_type max_value() { return my_type(std::numeric_limits::max()); } ~my_type() { } }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.m_key; return o; } bool operator < (const my_type& a, const my_type& b) { return a.m_key < b.m_key; } bool operator == (const my_type& a, const my_type& b) { return a.m_key == b.m_key; } bool operator != (const my_type& a, const my_type& b) { return a.m_key != b.m_key; } struct cmp : public std::less { my_type min_value() const { return my_type::min_value(); } my_type max_value() const { return my_type::max_value(); } }; int main(int argc, char* argv[]) { const stxxl::int_type SIZE = (argc >= 2) ? atoi(argv[1]) : 16; #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif stxxl::unsigned_type memory_to_use = SIZE * 1024 * 1024; typedef stxxl::vector vector_type; const stxxl::int64 n_records = stxxl::int64(SIZE * 2 + SIZE / 2) * stxxl::int64(1024 * 1024) / sizeof(my_type); vector_type v(n_records); stxxl::int64 aliens, not_stable; int bs = vector_type::block_type::size; STXXL_MSG("Filling vector with min_value..., input size = " << v.size() << " elements (" << ((v.size() * sizeof(my_type)) >> 20) << " MiB)"); for (vector_type::size_type i = 0; i < v.size(); i++) { v[i].m_key = 0; v[i].m_data = (int)(i + 1); } STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end(), cmp())); STXXL_MSG("Sorting (using " << (memory_to_use >> 20) << " MiB of memory)..."); stxxl::sort(v.begin(), v.end(), cmp(), memory_to_use); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end(), cmp())); aliens = not_stable = 0; for (vector_type::size_type i = 0; i < v.size(); i++) { if (v[i].m_data < 1) ++aliens; else if (v[i].m_data != i + 1) ++not_stable; v[i].m_data = (int)(i + 1); } STXXL_MSG("elements that were not in the input: " << aliens); STXXL_MSG("elements not on their expected location: " << not_stable); STXXL_MSG("Sorting subset (using " << (memory_to_use >> 20) << " MiB of memory)..."); stxxl::sort(v.begin() + bs - 1, v.end() - bs + 2, cmp(), memory_to_use); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end(), cmp())); aliens = not_stable = 0; for (vector_type::size_type i = 0; i < v.size(); i++) { if (v[i].m_data < 1) ++aliens; else if (v[i].m_data != i + 1) ++not_stable; v[i].m_data = (int)(i + 1); } STXXL_MSG("elements that were not in the input: " << aliens); STXXL_MSG("elements not on their expected location: " << not_stable); STXXL_MSG("Filling vector with max_value..., input size = " << v.size() << " elements (" << ((v.size() * sizeof(my_type)) >> 20) << " MiB)"); for (vector_type::size_type i = 0; i < v.size(); i++) { v[i].m_key = unsigned(-1); v[i].m_data = int(i + 1); } STXXL_MSG("Sorting subset (using " << (memory_to_use >> 20) << " MiB of memory)..."); stxxl::sort(v.begin() + bs - 1, v.end() - bs + 2, cmp(), memory_to_use); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end(), cmp())); aliens = not_stable = 0; for (vector_type::size_type i = 0; i < v.size(); i++) { if (v[i].m_data < 1) ++aliens; else if (v[i].m_data != i + 1) ++not_stable; v[i].m_data = int(i + 1); } STXXL_MSG("elements that were not in the input: " << aliens); STXXL_MSG("elements not on their expected location: " << not_stable); STXXL_MSG("Done, output size=" << v.size() << " block size=" << bs); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/algo/test_sort.cpp000644 001411 000144 00000006517 12405375303 017326 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_sort.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/test_sort.cpp //! This is an example of how to use \c stxxl::sort() algorithm #include #include #include #define RECORD_SIZE 8 struct my_type { typedef unsigned key_type; key_type m_key; char m_data[RECORD_SIZE - sizeof(key_type)]; key_type key() const { return m_key; } my_type() { } my_type(key_type k) : m_key(k) { #if STXXL_WITH_VALGRIND memset(m_data, 0, sizeof(m_data)); #endif } static my_type min_value() { return my_type(std::numeric_limits::min()); } static my_type max_value() { return my_type(std::numeric_limits::max()); } ~my_type() { } }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.m_key; return o; } bool operator < (const my_type& a, const my_type& b) { return a.key() < b.key(); } bool operator == (const my_type& a, const my_type& b) { return a.key() == b.key(); } bool operator != (const my_type& a, const my_type& b) { return a.key() != b.key(); } struct cmp : public std::less { my_type min_value() const { return my_type::min_value(); } my_type max_value() const { return my_type::max_value(); } }; int main() { #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif unsigned memory_to_use = 128 * 1024 * 1024; typedef stxxl::vector vector_type; { // test small vector that can be sorted internally vector_type v(3); v[0] = 42; v[1] = 0; v[2] = 23; STXXL_MSG("small vector unsorted " << v[0] << " " << v[1] << " " << v[2]); //stxxl::sort(v.begin(), v.end(), cmp(), memory_to_use); stxxl::stl_in_memory_sort(v.begin(), v.end(), cmp()); STXXL_MSG("small vector sorted " << v[0] << " " << v[1] << " " << v[2]); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end(), cmp())); } const stxxl::int64 n_records = stxxl::int64(384) * stxxl::int64(1024 * 1024) / sizeof(my_type); vector_type v(n_records); stxxl::random_number32 rnd; STXXL_MSG("Filling vector..., input size = " << v.size() << " elements (" << ((v.size() * sizeof(my_type)) >> 20) << " MiB)"); for (vector_type::size_type i = 0; i < v.size(); i++) v[i].m_key = 1 + (rnd() % 0xfffffff); STXXL_MSG("Checking order..."); STXXL_CHECK(!stxxl::is_sorted(v.begin(), v.end(), cmp())); STXXL_MSG("Sorting (using " << (memory_to_use >> 20) << " MiB of memory)..."); stxxl::sort(v.begin(), v.end(), cmp(), memory_to_use); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end(), cmp())); STXXL_MSG("Done, output size=" << v.size()); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/algo/test_sort_all_parameters.cpp000644 001411 000144 00000012540 12411366426 022375 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_sort_all_parameters.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //#define PLAY_WITH_OPT_PREF #include #include #include #include #include #include "test_sort_all_parameters.h" #ifndef RECORD_SIZE #define RECORD_SIZE 4 #endif #define MB (1024 * 1024) template void test(stxxl::uint64 data_mem, unsigned memory_to_use) { stxxl::uint64 records_to_sort = data_mem / sizeof(T); typedef stxxl::vector, block_size, alloc_strategy_type> vector_type; vector_type v(records_to_sort); size_t ndisks = stxxl::config::get_instance()->disks_number(); STXXL_MSG("Sorting " << records_to_sort << " records of size " << sizeof(T)); STXXL_MSG("Total volume " << (records_to_sort * sizeof(T)) / MB << " MiB"); STXXL_MSG("Using " << memory_to_use / MB << " MiB"); STXXL_MSG("Using " << ndisks << " disks"); STXXL_MSG("Using " << alloc_strategy_type::name() << " allocation strategy "); STXXL_MSG("Block size " << vector_type::block_type::raw_size / 1024 << " KiB"); STXXL_MSG("Filling vector..."); stxxl::generate(v.begin(), v.end(), stxxl::random_number32_r(), 32); STXXL_MSG("Sorting vector..."); stxxl::stats_data before(*stxxl::stats::get_instance()); stxxl::sort(v.begin(), v.end(), Cmp(), memory_to_use); stxxl::stats_data after(*stxxl::stats::get_instance()); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end(), Cmp())); STXXL_MSG("Sorting: " << (after - before)); STXXL_MSG("Total: " << *stxxl::stats::get_instance()); } template void test_all_strategies( stxxl::uint64 data_mem, unsigned memory_to_use, int strategy) { switch (strategy) { case 0: test(data_mem, memory_to_use); break; case 1: test(data_mem, memory_to_use); break; case 2: test(data_mem, memory_to_use); break; case 3: test(data_mem, memory_to_use); break; default: STXXL_ERRMSG("Unknown allocation strategy: " << strategy << ", aborting"); abort(); } } int main(int argc, char* argv[]) { if (argc < 6) { STXXL_ERRMSG("Usage: " << argv[0] << " "); return -1; } #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif stxxl::uint64 data_mem = stxxl::atouint64(argv[1]) * MB; int sort_mem = atoi(argv[2]) * MB; int strategy = atoi(argv[3]); int block_size = atoi(argv[4]); stxxl::set_seed((unsigned)strtoul(argv[5], NULL, 10)); STXXL_MSG("Seed " << stxxl::get_next_seed()); stxxl::srandom_number32(); typedef my_type my_default_type; switch (block_size) { case 0: test_all_strategies(data_mem, sort_mem, strategy); break; case 1: test_all_strategies(data_mem, sort_mem, strategy); break; case 2: test_all_strategies(data_mem, sort_mem, strategy); break; case 3: test_all_strategies(data_mem, sort_mem, strategy); break; case 4: test_all_strategies(data_mem, sort_mem, strategy); break; case 5: test_all_strategies(data_mem, sort_mem, strategy); break; case 6: test_all_strategies(data_mem, sort_mem, strategy); break; case 7: test_all_strategies(data_mem, sort_mem, strategy); break; case 8: test_all_strategies(data_mem, sort_mem, strategy); break; case 9: test_all_strategies(data_mem, sort_mem, strategy); break; case 10: test_all_strategies(data_mem, sort_mem, strategy); break; case 11: test_all_strategies, 2* MB>(data_mem, sort_mem, strategy); break; case 12: test_all_strategies, 2* MB + 4096>(data_mem, sort_mem, strategy); break; case 13: test_all_strategies, 2* MB + 4096>(data_mem, sort_mem, strategy); break; case 14: test_all_strategies, 2* MB>(data_mem, sort_mem, strategy); break; default: STXXL_ERRMSG("Unknown block size: " << block_size << ", aborting"); abort(); } return 0; } stxxl-1.4.1/tests/algo/test_sort_all_parameters.h000644 001411 000144 00000004731 12405375303 022042 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_sort_all_parameters.h * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include template struct bulk { char m_data[n]; bulk() { #if STXXL_WITH_VALGRIND memset(m_data, 0, n); #endif } }; template <> struct bulk<0> { }; template struct my_type { typedef KEY key_type; key_type m_key; bulk m_data; my_type() { } my_type(key_type k) : m_key(k) { } #ifdef KEY_COMPARE key_type key() const { return m_key; } #endif static my_type min_value() { return my_type(std::numeric_limits::min()); } static my_type max_value() { return my_type(std::numeric_limits::max()); } }; template std::ostream& operator << (std::ostream& o, const my_type obj) { o << obj.m_key; return o; } #ifndef KEY_COMPARE template bool operator < (const my_type& a, const my_type& b) { return a.m_key < b.m_key; } template bool operator == (const my_type& a, const my_type& b) { return a.m_key == b.m_key; } template bool operator != (const my_type& a, const my_type& b) { return a.m_key != b.m_key; } template struct Cmp : public std::less { bool operator () (const T& a, const T& b) const { return a.m_key < b.m_key; } static T min_value() { return T::min_value(); } static T max_value() { return T::max_value(); } }; #else template bool operator < (const my_type& a, const my_type& b) { return a.key() < b.key(); } #endif struct zero { unsigned operator () () { return 0; } }; stxxl-1.4.1/tests/algo/test_random_shuffle.cpp000644 001411 000144 00000005425 12405375303 021330 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_random_shuffle.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007 Manuel Krings * Copyright (C) 2007 Markus Westphal * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ // TODO: test both vector and non-vector variant of random_shuffle // TODO: test recursion, improve verboseness //! \example algo/test_random_shuffle.cpp //! Test \c stxxl::random_shuffle() #include #include template struct counter { type value; counter(type v = type(0)) : value(v) { } type operator () () { type old_val = value; value++; return old_val; } }; void long_test() { typedef stxxl::vector ext_vec_type; ext_vec_type STXXLVector(1024 * 1024 * 256 / sizeof(int)); STXXL_MSG("Filling vector with increasing values..."); stxxl::generate(STXXLVector.begin(), STXXLVector.end(), counter(), 4); stxxl::uint64 i; STXXL_MSG("Begin: "); for (i = 0; i < 10; i++) STXXL_MSG(STXXLVector[i]); STXXL_MSG("End: "); for (i = STXXLVector.size() - 10; i < STXXLVector.size(); i++) STXXL_MSG(STXXLVector[i]); STXXL_MSG("Permute randomly..."); stxxl::random_shuffle(STXXLVector.begin(), STXXLVector.end(), 1024 * 1024 * 128); STXXL_MSG("Begin: "); for (i = 0; i < 10; i++) STXXL_MSG(STXXLVector[i]); STXXL_MSG("End: "); for (i = STXXLVector.size() - 10; i < STXXLVector.size(); i++) STXXL_MSG(STXXLVector[i]); } void short_test() { STXXL_STATIC_ASSERT(sizeof(int) == 4); typedef stxxl::VECTOR_GENERATOR::result vector_type; vector_type::size_type i; vector_type v(2048); for (i = 0; i < v.size(); ++i) v[i] = (int)(i / 1024 + 1); std::cout << v.size() << std::endl; std::cout << "before shuffle:" << std::endl; for (i = 0; i < v.size(); ++i) { std::cout << v[i] << " "; if ((i & 511) == 511) std::cout << std::endl; } std::cout << std::endl; v.flush(); stxxl::random_shuffle(v.begin() + 512, v.begin() + 512 + 1024, 64 * 1024); std::cout << "after shuffle:" << std::endl; for (i = 0; i < v.size(); ++i) { std::cout << v[i] << " "; if ((i & 511) == 511) std::cout << std::endl; } std::cout << std::endl; } int main() { short_test(); long_test(); } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/algo/test_ksort_all_parameters.cpp000644 001411 000144 00000013020 12411366426 022542 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_ksort_all_parameters.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //#define PLAY_WITH_OPT_PREF #include #include #include #include #include #define KEY_COMPARE #include "test_sort_all_parameters.h" #ifndef RECORD_SIZE #define RECORD_SIZE 128 #endif #define MB (1024 * 1024) template void test(stxxl::uint64 data_mem, unsigned memory_to_use) { stxxl::uint64 records_to_sort = data_mem / sizeof(T); typedef stxxl::vector, block_size, alloc_strategy_type> vector_type; memory_to_use = stxxl::div_ceil(memory_to_use, vector_type::block_type::raw_size) * vector_type::block_type::raw_size; vector_type v(records_to_sort); size_t ndisks = stxxl::config::get_instance()->disks_number(); STXXL_MSG("Sorting " << records_to_sort << " records of size " << sizeof(T)); STXXL_MSG("Total volume " << (records_to_sort * sizeof(T)) / MB << " MiB"); STXXL_MSG("Using " << memory_to_use / MB << " MiB"); STXXL_MSG("Using " << ndisks << " disks"); STXXL_MSG("Using " << alloc_strategy_type::name() << " allocation strategy "); STXXL_MSG("Block size " << vector_type::block_type::raw_size / 1024 << " KiB"); STXXL_MSG("Filling vector..."); stxxl::generate(v.begin(), v.end(), stxxl::random_number32_r(), 32); //std::generate(v.begin(),v.end(),zero()); STXXL_MSG("Sorting vector..."); stxxl::stats_data before(*stxxl::stats::get_instance()); stxxl::ksort(v.begin(), v.end(), memory_to_use); stxxl::stats_data after(*stxxl::stats::get_instance()); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end())); STXXL_MSG("Sorting: " << (after - before)); STXXL_MSG("Total: " << *stxxl::stats::get_instance()); } template void test_all_strategies( stxxl::uint64 data_mem, unsigned memory_to_use, int strategy) { switch (strategy) { case 0: test(data_mem, memory_to_use); break; case 1: test(data_mem, memory_to_use); break; case 2: test(data_mem, memory_to_use); break; case 3: test(data_mem, memory_to_use); break; default: STXXL_ERRMSG("Unknown allocation strategy: " << strategy << ", aborting"); abort(); } } int main(int argc, char* argv[]) { if (argc < 6) { STXXL_ERRMSG("Usage: " << argv[0] << " "); return -1; } #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif stxxl::uint64 data_mem = stxxl::atouint64(argv[1]) * MB; int sort_mem = atoi(argv[2]) * MB; int strategy = atoi(argv[3]); int block_size = atoi(argv[4]); stxxl::set_seed((unsigned)strtoul(argv[5], NULL, 10)); STXXL_MSG("Seed " << stxxl::get_next_seed()); stxxl::srandom_number32(); typedef my_type my_default_type; switch (block_size) { case 0: test_all_strategies(data_mem, sort_mem, strategy); break; case 1: test_all_strategies(data_mem, sort_mem, strategy); break; case 2: test_all_strategies(data_mem, sort_mem, strategy); break; case 3: test_all_strategies(data_mem, sort_mem, strategy); break; case 4: test_all_strategies(data_mem, sort_mem, strategy); break; case 5: test_all_strategies(data_mem, sort_mem, strategy); break; case 6: test_all_strategies(data_mem, sort_mem, strategy); break; case 7: test_all_strategies(data_mem, sort_mem, strategy); break; case 8: test_all_strategies(data_mem, sort_mem, strategy); break; case 9: test_all_strategies(data_mem, sort_mem, strategy); break; case 10: test_all_strategies(data_mem, sort_mem, strategy); break; case 11: test_all_strategies, 2* MB>(data_mem, sort_mem, strategy); break; case 12: test_all_strategies, 2* MB + 4096>(data_mem, sort_mem, strategy); break; case 13: test_all_strategies, 2* MB + 4096>(data_mem, sort_mem, strategy); break; case 14: test_all_strategies, 2* MB>(data_mem, sort_mem, strategy); break; default: STXXL_ERRMSG("Unknown block size: " << block_size << ", aborting"); abort(); } return 0; } stxxl-1.4.1/tests/algo/test_stable_ksort.cpp000644 001411 000144 00000003766 12405375303 021036 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_stable_ksort.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003, 2008 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/test_stable_ksort.cpp //! This is an example of how to use \c stxxl::ksort() algorithm #include #include #include #include struct my_type { typedef unsigned key_type; key_type m_key; char m_data[128 - sizeof(key_type)]; key_type key() const { return m_key; } my_type() { } my_type(key_type k) : m_key(k) { } static my_type min_value() { return my_type(std::numeric_limits::min()); } static my_type max_value() { return my_type(std::numeric_limits::max()); } }; bool operator < (const my_type& a, const my_type& b) { return a.key() < b.key(); } int main() { #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif unsigned memory_to_use = 44 * 1024 * 1024; typedef stxxl::vector vector_type; const stxxl::int64 n_records = 2 * 32 * stxxl::int64(1024 * 1024) / sizeof(my_type); vector_type v(n_records); stxxl::random_number32 rnd; STXXL_MSG("Filling vector... " << rnd() << " " << rnd() << " " << rnd()); for (vector_type::size_type i = 0; i < v.size(); i++) v[i].m_key = (rnd() / 2) * 2; STXXL_MSG("Checking order..."); STXXL_CHECK(!stxxl::is_sorted(v.begin(), v.end())); STXXL_MSG("Sorting..."); stxxl::stable_ksort(v.begin(), v.end(), memory_to_use); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end())); return 0; } stxxl-1.4.1/tests/algo/test_asch.cpp000644 001411 000144 00000003735 12405375303 017254 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_asch.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include // Test async schedule algorithm int main(int argc, char* argv[]) { if (argc < 5) { STXXL_ERRMSG("Usage: " << argv[0] << " D L m seed"); return -1; } const int D = atoi(argv[1]); const int L = atoi(argv[2]); const stxxl::int_type m = atoi(argv[3]); stxxl::ran32State = atoi(argv[4]); stxxl::int_type* disks = new stxxl::int_type[L]; stxxl::int_type* prefetch_order = new stxxl::int_type[L]; int* count = new int[D]; for (int i = 0; i < D; i++) count[i] = 0; stxxl::random_number32 rnd; for (int i = 0; i < L; i++) { disks[i] = rnd(D); count[disks[i]]++; } for (int i = 0; i < D; i++) std::cout << "Disk " << i << " has " << count[i] << " blocks" << std::endl; stxxl::compute_prefetch_schedule(disks, disks + L, prefetch_order, m, D); STXXL_MSG("Prefetch order:"); for (int i = 0; i < L; ++i) { STXXL_MSG("request " << prefetch_order[i] << " on disk " << disks[prefetch_order[i]]); } STXXL_MSG("Request order:"); for (int i = 0; i < L; ++i) { int j; for (j = 0; prefetch_order[j] != i; ++j) ; STXXL_MSG("request " << i << " on disk " << disks[i] << " scheduled as " << j); } delete[] count; delete[] disks; delete[] prefetch_order; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/algo/test_stable_ksort_all_parameters.cpp000644 001411 000144 00000013042 12411366426 024100 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_stable_ksort_all_parameters.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //#define PLAY_WITH_OPT_PREF #include #include #include #include #define KEY_COMPARE #include "test_sort_all_parameters.h" #ifndef RECORD_SIZE #define RECORD_SIZE 128 #endif #define MB (1024 * 1024) template void test(stxxl::uint64 data_mem, unsigned memory_to_use) { stxxl::uint64 records_to_sort = data_mem / sizeof(T); typedef stxxl::vector, block_size, alloc_strategy_type> vector_type; memory_to_use = stxxl::div_ceil(memory_to_use, vector_type::block_type::raw_size) * vector_type::block_type::raw_size; vector_type v(records_to_sort); size_t ndisks = stxxl::config::get_instance()->disks_number(); STXXL_MSG("Sorting " << records_to_sort << " records of size " << sizeof(T)); STXXL_MSG("Total volume " << (records_to_sort * sizeof(T)) / MB << " MiB"); STXXL_MSG("Using " << memory_to_use / MB << " MiB"); STXXL_MSG("Using " << ndisks << " disks"); STXXL_MSG("Using " << alloc_strategy_type::name() << " allocation strategy "); STXXL_MSG("Block size " << vector_type::block_type::raw_size / 1024 << " KiB"); STXXL_MSG("Filling vector..."); std::generate(v.begin(), v.end(), stxxl::random_number64() _STXXL_FORCE_SEQUENTIAL); //std::generate(v.begin(),v.end(),zero()); STXXL_MSG("Sorting vector..."); stxxl::stats_data before(*stxxl::stats::get_instance()); stxxl::stable_ksort(v.begin(), v.end(), memory_to_use); stxxl::stats_data after(*stxxl::stats::get_instance()); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end())); STXXL_MSG("Sorting: " << (after - before)); STXXL_MSG("Total: " << *stxxl::stats::get_instance()); } template void test_all_strategies( stxxl::uint64 data_mem, unsigned memory_to_use, int strategy) { switch (strategy) { case 0: test(data_mem, memory_to_use); break; case 1: test(data_mem, memory_to_use); break; case 2: test(data_mem, memory_to_use); break; case 3: test(data_mem, memory_to_use); break; default: STXXL_ERRMSG("Unknown allocation strategy: " << strategy << ", aborting"); abort(); } } int main(int argc, char* argv[]) { if (argc < 6) { STXXL_ERRMSG("Usage: " << argv[0] << " "); return -1; } #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif stxxl::uint64 data_mem = stxxl::atouint64(argv[1]) * MB; int sort_mem = atoi(argv[2]) * MB; int strategy = atoi(argv[3]); int block_size = atoi(argv[4]); stxxl::set_seed((unsigned)strtoul(argv[5], NULL, 10)); STXXL_MSG("Seed " << stxxl::get_next_seed()); stxxl::srandom_number32(); typedef my_type my_default_type; switch (block_size) { case 0: test_all_strategies(data_mem, sort_mem, strategy); break; case 1: test_all_strategies(data_mem, sort_mem, strategy); break; case 2: test_all_strategies(data_mem, sort_mem, strategy); break; case 3: test_all_strategies(data_mem, sort_mem, strategy); break; case 4: test_all_strategies(data_mem, sort_mem, strategy); break; case 5: test_all_strategies(data_mem, sort_mem, strategy); break; case 6: test_all_strategies(data_mem, sort_mem, strategy); break; case 7: test_all_strategies(data_mem, sort_mem, strategy); break; case 8: test_all_strategies(data_mem, sort_mem, strategy); break; case 9: test_all_strategies(data_mem, sort_mem, strategy); break; case 10: test_all_strategies(data_mem, sort_mem, strategy); break; case 11: test_all_strategies, 2* MB>(data_mem, sort_mem, strategy); break; case 12: test_all_strategies, 2* MB + 4096>(data_mem, sort_mem, strategy); break; case 13: test_all_strategies, 2* MB + 4096>(data_mem, sort_mem, strategy); break; case 14: test_all_strategies, 2* MB>(data_mem, sort_mem, strategy); break; default: STXXL_ERRMSG("Unknown block size: " << block_size << ", aborting"); abort(); } return 0; } stxxl-1.4.1/tests/algo/test_parallel_sort.cpp000644 001411 000144 00000021164 12411366426 021200 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_parallel_sort.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007, 2009 Johannes Singler * Copyright (C) 2008, 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/test_parallel_sort.cpp //! This is an example of how to use the parallelized sorting algorithm. //! Setting all the parameters in optional, just compiling with parallel mode //! suffices. #if !defined(STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD) #define STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD 0 #endif #include #include #include #include #include #include #include using stxxl::unsigned_type; const unsigned long long megabyte = 1024 * 1024; const int block_size = STXXL_DEFAULT_BLOCK_SIZE(my_type); #define RECORD_SIZE 20 #define MAGIC 123 unsigned_type run_size; unsigned_type buffer_size; struct my_type { typedef unsigned long long key_type; key_type m_key; key_type m_load; char m_data[RECORD_SIZE - 2 * sizeof(key_type)]; key_type key() const { return m_key; } my_type() { } my_type(key_type k) : m_key(k) { } my_type(key_type k, key_type l) : m_key(k), m_load(l) { } void operator = (const key_type& k) { m_key = k; } void operator = (const my_type& mt) { m_key = mt.m_key; m_load = mt.m_load; } }; bool operator < (const my_type& a, const my_type& b); inline bool operator < (const my_type& a, const my_type& b) { return a.key() < b.key(); } inline bool operator == (const my_type& a, const my_type& b) { return a.key() == b.key(); } inline std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.m_key << "/" << obj.m_load; return o; } struct cmp_less_key : public std::less { my_type min_value() const { return my_type(std::numeric_limits::min(), MAGIC); } my_type max_value() const { return my_type(std::numeric_limits::max(), MAGIC); } }; typedef stxxl::vector, block_size, STXXL_DEFAULT_ALLOC_STRATEGY> vector_type; unsigned_type checksum(vector_type& input) { unsigned_type sum = 0; for (vector_type::const_iterator i = input.begin(); i != input.end(); ++i) sum += (unsigned_type)((*i).m_key); return sum; } void linear_sort_normal(vector_type& input) { unsigned_type sum1 = checksum(input); stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); double start = stxxl::timestamp(); stxxl::sort(input.begin(), input.end(), cmp_less_key(), run_size); double stop = stxxl::timestamp(); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; unsigned_type sum2 = checksum(input); std::cout << sum1 << " ?= " << sum2 << std::endl; STXXL_CHECK(stxxl::is_sorted(input.begin(), input.end())); std::cout << "Linear sorting normal took " << (stop - start) << " seconds." << std::endl; } void linear_sort_streamed(vector_type& input, vector_type& output) { unsigned_type sum1 = checksum(input); stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); double start = stxxl::timestamp(); typedef stxxl::stream::streamify_traits::stream_type input_stream_type; input_stream_type input_stream = stxxl::stream::streamify(input.begin(), input.end()); typedef cmp_less_key comparator_type; comparator_type cl; typedef stxxl::stream::sort sort_stream_type; sort_stream_type sort_stream(input_stream, cl, run_size); vector_type::iterator o = stxxl::stream::materialize(sort_stream, output.begin(), output.end()); STXXL_CHECK(o == output.end()); double stop = stxxl::timestamp(); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; unsigned_type sum2 = checksum(output); std::cout << sum1 << " ?= " << sum2 << std::endl; if (sum1 != sum2) STXXL_MSG("WRONG DATA"); STXXL_CHECK(stxxl::is_sorted(output.begin(), output.end(), comparator_type())); std::cout << "Linear sorting streamed took " << (stop - start) << " seconds." << std::endl; } int main(int argc, const char** argv) { if (argc < 6) { std::cout << "Usage: " << argv[0] << " [n in MiB] [p threads] [M in MiB] [sorting algorithm: m | q | qb | s] [merging algorithm: p | s | n]" << std::endl; return -1; } stxxl::config::get_instance(); #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif unsigned long megabytes_to_process = atoi(argv[1]); int p = atoi(argv[2]); unsigned_type memory_to_use = (unsigned_type)(atoi(argv[3]) * megabyte); run_size = memory_to_use; buffer_size = memory_to_use / 16; #ifdef STXXL_PARALLEL_MODE omp_set_num_threads(p); __gnu_parallel::_Settings parallel_settings(__gnu_parallel::_Settings::get()); parallel_settings.merge_splitting = __gnu_parallel::EXACT; parallel_settings.merge_minimal_n = 10000; parallel_settings.merge_oversampling = 10; parallel_settings.multiway_merge_algorithm = __gnu_parallel::LOSER_TREE; parallel_settings.multiway_merge_splitting = __gnu_parallel::EXACT; parallel_settings.multiway_merge_oversampling = 10; parallel_settings.multiway_merge_minimal_n = 10000; parallel_settings.multiway_merge_minimal_k = 2; if (!strcmp(argv[4], "q")) //quicksort parallel_settings.sort_algorithm = __gnu_parallel::QS; else if (!strcmp(argv[4], "qb")) //balanced quicksort parallel_settings.sort_algorithm = __gnu_parallel::QS_BALANCED; else if (!strcmp(argv[4], "m")) //merge sort parallel_settings.sort_algorithm = __gnu_parallel::MWMS; else /*if(!strcmp(argv[4], "s"))*/ //sequential (default) { parallel_settings.sort_algorithm = __gnu_parallel::QS; parallel_settings.sort_minimal_n = memory_to_use; } if (!strcmp(argv[5], "p")) //parallel { stxxl::SETTINGS::native_merge = false; //parallel_settings.multiway_merge_minimal_n = 1024; //leave as default } else if (!strcmp(argv[5], "s")) //sequential { stxxl::SETTINGS::native_merge = false; parallel_settings.multiway_merge_minimal_n = memory_to_use; //too much to be called } else /*if(!strcmp(argv[5], "n"))*/ //native (default) stxxl::SETTINGS::native_merge = true; parallel_settings.multiway_merge_minimal_k = 2; __gnu_parallel::_Settings::set(parallel_settings); STXXL_CHECK(&__gnu_parallel::_Settings::get() != ¶llel_settings); if (0) printf("%d %p: mwms %d, q %d, qb %d", __gnu_parallel::_Settings::get().sort_algorithm, (void*)&__gnu_parallel::_Settings::get().sort_algorithm, __gnu_parallel::MWMS, __gnu_parallel::QS, __gnu_parallel::QS_BALANCED); #endif std::cout << "Sorting " << megabytes_to_process << " MiB of data (" << (megabytes_to_process * megabyte / sizeof(my_type)) << " elements) using " << (memory_to_use / megabyte) << " MiB of internal memory and " << p << " thread(s), block size " << block_size << ", element size " << sizeof(my_type) << std::endl; const stxxl::int64 n_records = stxxl::int64(megabytes_to_process) * stxxl::int64(megabyte) / sizeof(my_type); vector_type input(n_records); stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); double generate_start = stxxl::timestamp(); stxxl::generate(input.begin(), input.end(), stxxl::random_number64(), memory_to_use / STXXL_DEFAULT_BLOCK_SIZE(my_type)); double generate_stop = stxxl::timestamp(); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; std::cout << "Generating took " << (generate_stop - generate_start) << " seconds." << std::endl; STXXL_CHECK(!stxxl::is_sorted(input.begin(), input.end())); { vector_type output(n_records); linear_sort_streamed(input, output); linear_sort_normal(input); } return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tests/algo/CMakeLists.txt000644 001411 000144 00000007443 12404624022 017325 0ustar00tbusers000000 000000 ############################################################################ # tests/algo/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(test_asch) stxxl_build_test(test_bad_cmp) stxxl_build_test(test_ksort) stxxl_build_test(test_random_shuffle) stxxl_build_test(test_scan) stxxl_build_test(test_sort) stxxl_build_test(test_stable_ksort) add_define(test_bad_cmp "STXXL_VERBOSE_LEVEL=0") add_define(test_ksort "STXXL_VERBOSE_LEVEL=1" "STXXL_CHECK_ORDER_IN_SORTS") add_define(test_random_shuffle "STXXL_VERBOSE_LEVEL=0") add_define(test_sort "STXXL_VERBOSE_LEVEL=0") stxxl_test(test_asch 3 100 1000 42) stxxl_test(test_bad_cmp 16) stxxl_test(test_ksort) stxxl_test(test_random_shuffle) stxxl_test(test_scan) stxxl_test(test_sort) stxxl_test(test_stable_ksort) if(NOT CYGWIN AND NOT MINGW) #-tb too big to build on cygwin stxxl_build_test(test_ksort_all_parameters) stxxl_build_test(test_stable_ksort_all_parameters) stxxl_build_test(test_sort_all_parameters) add_define(test_ksort_all_parameters "STXXL_VERBOSE_STABLE_KSORT=STXXL_VERBOSE0" "STXXL_VERBOSE_LEVEL=0" "STXXL_CHECK_ORDER_IN_SORTS") if(MSVC AND BUILD_TESTS) # requires /bigobj flag to build set_target_properties(test_ksort_all_parameters PROPERTIES COMPILE_FLAGS /bigobj) set_target_properties(test_sort_all_parameters PROPERTIES COMPILE_FLAGS /bigobj) set_target_properties(test_stable_ksort_all_parameters PROPERTIES COMPILE_FLAGS /bigobj) endif() ### extra sort tests: #test_sort_all_parameters #test_ksort_all_parameters #test_stable_ksort_all_parameters foreach(DATA 1024) foreach(RAM 384) foreach(STRATEGY 0 1 2 3) foreach(BLK_SIZE 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14) stxxl_extra_test(test_sort_all_parameters ${DATA} ${RAM} ${STRATEGY} ${BLK_SIZE} 42) stxxl_extra_test(test_ksort_all_parameters ${DATA} ${RAM} ${STRATEGY} ${BLK_SIZE} 42) #-tb not fully implemented #stxxl_extra_test(test_stable_ksort_all_parameters ${DATA} ${RAM} ${STRATEGY} ${BLK_SIZE} 42) endforeach(BLK_SIZE) endforeach(STRATEGY) endforeach(RAM) endforeach(DATA) if(NOT BUILD_EXTRAS) foreach(STRATEGY 0 1 2 3) stxxl_test(test_sort_all_parameters 512 192 ${STRATEGY} 10 42) stxxl_test(test_ksort_all_parameters 512 192 ${STRATEGY} 10 42) #-tb not fully implemented #stxxl_test(test_stable_ksort_all_parameters 512 192 ${STRATEGY} 10 42) endforeach(STRATEGY) endif(NOT BUILD_EXTRAS) endif() if(USE_GNU_PARALLEL) stxxl_build_test(test_parallel_sort) ### parallel sort tests #algo/test_parallel_sort [n in MiB] [p threads] [M in MiB] [sorting algorithm: m | q | qb | s] [merging algorithm: p | s | n] foreach(DATA 768) foreach(RAM 128) foreach(THREADS 1 2 3 4) foreach(SORT m q qb s) foreach(MERGE p s n) stxxl_extra_test(test_parallel_sort ${DATA} ${THREADS} ${RAM} ${SORT} ${MERGE}) endforeach(MERGE) endforeach(SORT) endforeach(THREADS) endforeach(RAM) endforeach(DATA) if(NOT BUILD_EXTRAS) foreach(SORT m q qb s) foreach(MERGE p s n) stxxl_test(test_parallel_sort 256 4 96 ${SORT} ${MERGE}) endforeach(MERGE) endforeach(SORT) endif(NOT BUILD_EXTRAS) endif(USE_GNU_PARALLEL) stxxl-1.4.1/tests/algo/test_ksort.cpp000644 001411 000144 00000006277 12405375303 017504 0ustar00tbusers000000 000000 /*************************************************************************** * tests/algo/test_ksort.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example algo/test_ksort.cpp //! This is an example of how to use \c stxxl::ksort() algorithm #include #include #include struct my_type { typedef stxxl::uint64 key_type1; key_type1 m_key; key_type1 m_key_copy; char m_data[32 - 2 * sizeof(key_type1)]; key_type1 key() const { return m_key; } my_type() : m_key(0), m_key_copy(0) { } my_type(key_type1 k) : m_key(k), m_key_copy(k) { } my_type min_value() const { return my_type(std::numeric_limits::min()); } my_type max_value() const { return my_type(std::numeric_limits::max()); } }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.m_key << " " << obj.m_key_copy; return o; } struct get_key { typedef my_type::key_type1 key_type; my_type dummy; key_type operator () (const my_type& obj) const { return obj.m_key; } my_type min_value() const { return my_type((dummy.min_value)()); } my_type max_value() const { return my_type((dummy.max_value)()); } }; bool operator < (const my_type& a, const my_type& b) { return a.key() < b.key(); } int main() { STXXL_MSG("Check config..."); try { stxxl::block_manager::get_instance(); } catch (std::exception& e) { STXXL_MSG("Exception: " << e.what()); abort(); } #if STXXL_PARALLEL_MULTIWAY_MERGE STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); #endif unsigned memory_to_use = 32 * 1024 * 1024; typedef stxxl::VECTOR_GENERATOR::result vector_type; const stxxl::int64 n_records = 3 * 32 * stxxl::int64(1024 * 1024) / sizeof(my_type); vector_type v(n_records); stxxl::random_number32 rnd; STXXL_MSG("Filling vector... "); for (vector_type::size_type i = 0; i < v.size(); i++) { v[i].m_key = rnd() + 1; v[i].m_key_copy = v[i].m_key; } STXXL_MSG("Checking order..."); STXXL_CHECK(!stxxl::is_sorted(v.begin(), v.end())); STXXL_MSG("Sorting..."); stxxl::ksort(v.begin(), v.end(), get_key(), memory_to_use); //stxxl::ksort(v.begin(),v.end(),memory_to_use); STXXL_MSG("Checking order..."); STXXL_CHECK(stxxl::is_sorted(v.begin(), v.end())); STXXL_MSG("Checking content..."); my_type prev; for (vector_type::size_type i = 0; i < v.size(); i++) { if (v[i].m_key != v[i].m_key_copy) { STXXL_MSG("Bug at position " << i); abort(); } if (i > 0 && prev.m_key == v[i].m_key) { STXXL_MSG("Duplicate at position " << i << " key=" << v[i].m_key); //abort(); } prev = v[i]; } STXXL_MSG("OK"); return 0; } stxxl-1.4.1/tests/CMakeLists.txt000644 001411 000144 00000001123 12350112610 016363 0ustar00tbusers000000 000000 ############################################################################ # tests/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ add_subdirectory(algo) add_subdirectory(common) add_subdirectory(containers) add_subdirectory(io) add_subdirectory(mng) add_subdirectory(stream) stxxl-1.4.1/lib/libstxxl.symbols000644 001411 000144 00000030033 12350112610 016514 0ustar00tbusers000000 000000 libstxxl.so.0 libstxxl #MINVER# _Z20gprof_pthread_createPmP14pthread_attr_tPFPvS2_ES2_@Base 0.9 _ZN5stxxl10disk_queue11add_readreqERNS_11request_ptrE@Base 0.9 _ZN5stxxl10disk_queue12add_writereqERNS_11request_ptrE@Base 0.9 _ZN5stxxl10disk_queue6workerEPv@Base 0.9 _ZN5stxxl10disk_queueC1Ei@Base 0.9 _ZN5stxxl10disk_queueC2Ei@Base 0.9 _ZN5stxxl10disk_queueD1Ev@Base 0.9 _ZN5stxxl10disk_queueD2Ev@Base 0.9 _ZN5stxxl10ran32StateE@Base 0.9 _ZN5stxxl11FileCreator6createERKSsS2_ii@Base 0.9 _ZN5stxxl11FileCreatorD0Ev@Base 1.0 _ZN5stxxl11FileCreatorD1Ev@Base 1.0 _ZN5stxxl11disk_queues8instanceE@Base 0.9 #MISSING: 1.0# _ZN5stxxl11request_ptr7sub_refEv@Base 0.9 #MISSING: 1.0# _ZN5stxxl11request_ptraSERKS0_@Base 0.9 _ZN5stxxl12DiskGeometry8add_zoneERiiiS1_@Base 0.9 _ZN5stxxl12DiskGeometry9get_delayExm@Base 0.9 _ZN5stxxl12mmap_request5serveEv@Base 0.9 _ZN5stxxl12mmap_request7io_typeEv@Base 0.9 _ZN5stxxl12mmap_requestD0Ev@Base 0.9 _ZN5stxxl12mmap_requestD1Ev@Base 0.9 _ZN5stxxl12onoff_switch2onEv@Base 0.9 _ZN5stxxl12syscall_file5areadEPvxmNS_18completion_handlerE@Base 0.9 _ZN5stxxl12syscall_file6awriteEPvxmNS_18completion_handlerE@Base 0.9 _ZN5stxxl12syscall_fileC1ERKSsii@Base 0.9 _ZN5stxxl12syscall_fileC2ERKSsii@Base 0.9 _ZN5stxxl12syscall_fileD0Ev@Base 0.9 _ZN5stxxl12syscall_fileD1Ev@Base 0.9 _ZN5stxxl13DiskAllocator4dumpEv@Base 0.9 _ZN5stxxl13block_manager12get_instanceEv@Base 0.9 _ZN5stxxl13block_manager8instanceE@Base 0.9 _ZN5stxxl13block_managerC1Ev@Base 0.9 _ZN5stxxl13block_managerC2Ev@Base 0.9 _ZN5stxxl13block_managerD1Ev@Base 0.9 _ZN5stxxl13block_managerD2Ev@Base 0.9 _ZN5stxxl13sim_disk_file5areadEPvxmNS_18completion_handlerE@Base 0.9 _ZN5stxxl13sim_disk_file6awriteEPvxmNS_18completion_handlerE@Base 0.9 _ZN5stxxl13sim_disk_file8set_sizeEx@Base 0.9 _ZN5stxxl13sim_disk_fileD0Ev@Base 0.9 _ZN5stxxl13sim_disk_fileD1Ev@Base 0.9 _ZN5stxxl13ufs_file_base4lockEv@Base 1.0 _ZN5stxxl13ufs_file_base4sizeEv@Base 0.9 _ZN5stxxl13ufs_file_base8set_sizeEx@Base 0.9 _ZN5stxxl13ufs_file_baseC1ERKSsii@Base 0.9 _ZN5stxxl13ufs_file_baseC2ERKSsii@Base 0.9 _ZN5stxxl13ufs_file_baseD0Ev@Base 0.9 _ZN5stxxl13ufs_file_baseD1Ev@Base 0.9 _ZN5stxxl13ufs_file_baseD2Ev@Base 0.9 _ZN5stxxl14IC35L080AVVA07C1Ev@Base 0.9 _ZN5stxxl14IC35L080AVVA07C2Ev@Base 0.9 _ZN5stxxl14resource_errorD0Ev@Base 1.0 _ZN5stxxl14resource_errorD1Ev@Base 1.0 _ZN5stxxl15syscall_request5serveEv@Base 0.9 _ZN5stxxl15syscall_request7io_typeEv@Base 0.9 _ZN5stxxl15syscall_requestC1EPNS_12syscall_fileEPvxmNS_7request12request_typeENS_18completion_handlerE@Base 0.9 _ZN5stxxl15syscall_requestC2EPNS_12syscall_fileEPvxmNS_7request12request_typeENS_18completion_handlerE@Base 0.9 _ZN5stxxl15syscall_requestD0Ev@Base 0.9 _ZN5stxxl15syscall_requestD1Ev@Base 0.9 _ZN5stxxl16sim_disk_request5serveEv@Base 0.9 _ZN5stxxl16sim_disk_request7io_typeEv@Base 0.9 _ZN5stxxl16sim_disk_requestD0Ev@Base 0.9 _ZN5stxxl16sim_disk_requestD1Ev@Base 0.9 _ZN5stxxl16ufs_request_base10add_waiterEPNS_12onoff_switchE@Base 0.9 _ZN5stxxl16ufs_request_base13delete_waiterEPNS_12onoff_switchE@Base 0.9 _ZN5stxxl16ufs_request_base14check_aligningEv@Base 0.9 _ZN5stxxl16ufs_request_base4pollEv@Base 0.9 _ZN5stxxl16ufs_request_base4waitEv@Base 0.9 _ZN5stxxl16ufs_request_base7io_typeEv@Base 0.9 _ZN5stxxl16ufs_request_base8nwaitersEv@Base 0.9 _ZN5stxxl16ufs_request_baseC1EPNS_13ufs_file_baseEPvxmNS_7request12request_typeENS_18completion_handlerE@Base 0.9 _ZN5stxxl16ufs_request_baseC2EPNS_13ufs_file_baseEPvxmNS_7request12request_typeENS_18completion_handlerE@Base 0.9 _ZN5stxxl16ufs_request_baseD0Ev@Base 0.9 _ZN5stxxl16ufs_request_baseD1Ev@Base 0.9 _ZN5stxxl16ufs_request_baseD2Ev@Base 0.9 _ZN5stxxl17wait_time_counterE@Base 0.9 _ZN5stxxl20simulate_async_writeEPiiiiPSt4pairIiiE@Base 0.9 _ZN5stxxl25compute_prefetch_scheduleEPiS0_S0_ii@Base 0.9 _ZN5stxxl4file4lockEv@Base 1.0 _ZN5stxxl4fileD0Ev@Base 0.9 _ZN5stxxl4fileD1Ev@Base 0.9 _ZN5stxxl5mutex4lockEv@Base 0.9 _ZN5stxxl5mutex6unlockEv@Base 0.9 _ZN5stxxl5mutexC1Ev@Base 1.0 _ZN5stxxl5mutexD1Ev@Base 0.9 _ZN5stxxl5splitERKSsS1_@Base 0.9 _ZN5stxxl5state6set_toEi@Base 0.9 _ZN5stxxl5state8wait_forEi@Base 0.9 _ZN5stxxl5stateC1Ei@Base 1.0 _ZN5stxxl5stateD1Ev@Base 1.0 _ZN5stxxl5stateclEv@Base 0.9 _ZN5stxxl5stats12get_instanceEv@Base 0.9 _ZN5stxxl5stats12read_startedEj@Base 0.9 _ZN5stxxl5stats13read_finishedEv@Base 0.9 _ZN5stxxl5stats13write_startedEj@Base 0.9 _ZN5stxxl5stats14write_finishedEv@Base 0.9 _ZN5stxxl5stats19_reset_io_wait_timeEv@Base 0.9 _ZN5stxxl5stats22increment_io_wait_timeEd@Base 0.9 _ZN5stxxl5stats5resetEv@Base 0.9 _ZN5stxxl5stats8instanceE@Base 0.9 _ZN5stxxl5statsC1Ev@Base 0.9 _ZN5stxxl5statsC2Ev@Base 0.9 _ZN5stxxl6config12get_instanceEv@Base 0.9 _ZN5stxxl6config8instanceE@Base 0.9 _ZN5stxxl6configC1EPKc@Base 0.9 _ZN5stxxl6configC2EPKc@Base 0.9 _ZN5stxxl6logger12get_instanceEv@Base 0.9 _ZN5stxxl6logger8instanceE@Base 0.9 _ZN5stxxl7request7io_typeEv@Base 0.9 _ZN5stxxl7request7sub_refEv@Base 0.9 _ZN5stxxl7requestD0Ev@Base 0.9 _ZN5stxxl7requestD1Ev@Base 0.9 _ZN5stxxl8debugmon10io_startedEPc@Base 0.9 _ZN5stxxl8debugmon11io_finishedEPc@Base 0.9 _ZN5stxxl8debugmon12get_instanceEv@Base 0.9 _ZN5stxxl8debugmon15block_allocatedEPcS1_m@Base 0.9 _ZN5stxxl8debugmon17block_deallocatedEPc@Base 0.9 _ZN5stxxl8debugmon8instanceE@Base 0.9 _ZN5stxxl8io_errorD0Ev@Base 1.0 _ZN5stxxl8io_errorD1Ev@Base 1.0 _ZN5stxxl9mmap_file5areadEPvxmNS_18completion_handlerE@Base 0.9 _ZN5stxxl9mmap_file6awriteEPvxmNS_18completion_handlerE@Base 0.9 _ZN5stxxl9mmap_fileD0Ev@Base 0.9 _ZN5stxxl9mmap_fileD1Ev@Base 0.9 _ZN5stxxl9semaphoreC1Ei@Base 1.0 _ZN5stxxl9semaphoreD1Ev@Base 0.9 _ZN5stxxl9semaphoremmEi@Base 0.9 _ZN5stxxl9semaphoreppEi@Base 0.9 _ZN5stxxllsERSoRKNS_5statsE@Base 0.9 _ZNK5stxxl11request_ptrptEv@Base 0.9 _ZNK5stxxl13ufs_file_base12get_file_desEv@Base 0.9 #MISSING: 1.0# _ZNK5stxxl16ufs_request_base4sizeEv@Base 0.9 _ZNK5stxxl5stats10get_writesEv@Base 0.9 _ZNK5stxxl5stats12get_pio_timeEv@Base 0.9 _ZNK5stxxl5stats13get_read_timeEv@Base 0.9 _ZNK5stxxl5stats14get_pread_timeEv@Base 0.9 _ZNK5stxxl5stats14get_write_timeEv@Base 0.9 _ZNK5stxxl5stats15get_pwrite_timeEv@Base 0.9 _ZNK5stxxl5stats15get_read_volumeEv@Base 0.9 _ZNK5stxxl5stats16get_io_wait_timeEv@Base 0.9 _ZNK5stxxl5stats18get_written_volumeEv@Base 0.9 _ZNK5stxxl5stats19get_last_reset_timeEv@Base 0.9 _ZNK5stxxl5stats9get_readsEv@Base 0.9 _ZNK5stxxl7request5printERSo@Base 0.9 _ZNSs12_S_constructIPcEES0_T_S1_RKSaIcESt20forward_iterator_tag@Base 1.0 _ZNSt11_Deque_baseIN5stxxl11request_ptrESaIS1_EE15_M_create_nodesEPPS1_S5_@Base 0.9 _ZNSt11_Deque_baseIN5stxxl11request_ptrESaIS1_EE17_M_initialize_mapEm@Base 0.9 _ZNSt11_Deque_baseIiSaIiEE15_M_create_nodesEPPiS3_@Base 0.9 _ZNSt11_Deque_baseIiSaIiEE17_M_initialize_mapEm@Base 0.9 _ZNSt17_Temporary_bufferIPSt4pairIiiES1_EC1ES2_S2_@Base 0.9 _ZNSt5dequeIN5stxxl11request_ptrESaIS1_EE16_M_push_back_auxERKS1_@Base 0.9 _ZNSt5dequeIN5stxxl11request_ptrESaIS1_EE17_M_reallocate_mapEmb@Base 0.9 _ZNSt5dequeIN5stxxl11request_ptrESaIS1_EE19_M_destroy_data_auxESt15_Deque_iteratorIS1_RS1_PS1_ES7_@Base 0.9 _ZNSt5dequeIN5stxxl11request_ptrESaIS1_EED1Ev@Base 0.9 _ZNSt5dequeIiSaIiEE16_M_push_back_auxERKi@Base 0.9 _ZNSt5dequeIiSaIiEE17_M_reallocate_mapEmb@Base 0.9 _ZNSt5dequeIiSaIiEED1Ev@Base 0.9 _ZNSt6vectorIN5stxxl6config9DiskEntryESaIS2_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS2_S4_EERKS2_@Base 0.9 _ZNSt6vectorIN5stxxl9sim_eventESaIS1_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS1_S3_EERKS1_@Base 0.9 _ZNSt6vectorIN5stxxl9sim_eventESaIS1_EED1Ev@Base 0.9 _ZNSt6vectorIPN9__gnu_cxx15_Hashtable_nodeISt4pairIKPcN5stxxl8debugmon3tagEEEESaISA_EE14_M_fill_insertENS0_17__normal_iteratorIPSA_SC_EEmRKSA_@Base 0.9 _ZNSt6vectorIPN9__gnu_cxx15_Hashtable_nodeISt4pairIKPcN5stxxl8debugmon3tagEEEESaISA_EE7reserveEm@Base 0.9 _ZNSt6vectorISsSaISsEE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPSsS1_EERKSs@Base 0.9 _ZNSt6vectorISsSaISsEEaSERKS1_@Base 0.9 _ZNSt8_Rb_treeIN5stxxl12DiskGeometry4ZoneES2_St9_IdentityIS2_ENS1_7ZoneCmpESaIS2_EE16_M_insert_uniqueERKS2_@Base 0.9 _ZNSt8_Rb_treeIN5stxxl12DiskGeometry4ZoneES2_St9_IdentityIS2_ENS1_7ZoneCmpESaIS2_EE8_M_eraseEPSt13_Rb_tree_nodeIS2_E@Base 0.9 _ZNSt8_Rb_treeIN5stxxl12DiskGeometry4ZoneES2_St9_IdentityIS2_ENS1_7ZoneCmpESaIS2_EE9_M_insertEPSt18_Rb_tree_node_baseS9_RKS2_@Base 0.9 _ZNSt8_Rb_treeIPN5stxxl12onoff_switchES2_St9_IdentityIS2_ESt4lessIS2_ESaIS2_EE16_M_insert_uniqueERKS2_@Base 0.9 _ZNSt8_Rb_treeIPN5stxxl12onoff_switchES2_St9_IdentityIS2_ESt4lessIS2_ESaIS2_EE5eraseERKS2_@Base 0.9 _ZNSt8_Rb_treeIPN5stxxl12onoff_switchES2_St9_IdentityIS2_ESt4lessIS2_ESaIS2_EE8_M_eraseEPSt13_Rb_tree_nodeIS2_E@Base 0.9 _ZNSt8_Rb_treeIPN5stxxl12onoff_switchES2_St9_IdentityIS2_ESt4lessIS2_ESaIS2_EE9_M_insertEPSt18_Rb_tree_node_baseSA_RKS2_@Base 0.9 _ZNSt8_Rb_treeIxSt4pairIKxPN5stxxl10disk_queueEESt10_Select1stIS5_ESt4lessIxESaIS5_EE16_M_insert_uniqueERKS5_@Base 0.9 _ZNSt8_Rb_treeIxSt4pairIKxPN5stxxl10disk_queueEESt10_Select1stIS5_ESt4lessIxESaIS5_EE16_M_insert_uniqueESt17_Rb_tree_iteratorIS5_ERKS5_@Base 0.9 _ZNSt8_Rb_treeIxSt4pairIKxPN5stxxl10disk_queueEESt10_Select1stIS5_ESt4lessIxESaIS5_EE9_M_insertEPSt18_Rb_tree_node_baseSD_RKS5_@Base 0.9 _ZNSt8_Rb_treeIxSt4pairIKxxESt10_Select1stIS2_ESt4lessIxESaIS2_EE16_M_insert_uniqueERKS2_@Base 0.9 _ZNSt8_Rb_treeIxSt4pairIKxxESt10_Select1stIS2_ESt4lessIxESaIS2_EE16_M_insert_uniqueESt17_Rb_tree_iteratorIS2_ERKS2_@Base 0.9 _ZNSt8_Rb_treeIxSt4pairIKxxESt10_Select1stIS2_ESt4lessIxESaIS2_EE8_M_eraseEPSt13_Rb_tree_nodeIS2_E@Base 0.9 _ZNSt8_Rb_treeIxSt4pairIKxxESt10_Select1stIS2_ESt4lessIxESaIS2_EE9_M_insertEPSt18_Rb_tree_node_baseSA_RKS2_@Base 0.9 _ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPN5stxxl9sim_eventESt6vectorIS3_SaIS3_EEEElS3_NS2_13sim_event_cmpEEvT_T0_SB_T1_T2_@Base 0.9 _ZSt16__merge_adaptiveIPSt4pairIiiElS2_N5stxxl14write_time_cmpEEvT_S5_S5_T0_S6_T1_S6_T2_@Base 0.9 _ZSt16__merge_backwardIPSt4pairIiiES2_S2_N5stxxl14write_time_cmpEET1_T_S6_T0_S7_S5_T2_@Base 0.9 _ZSt17__merge_sort_loopIPSt4pairIiiES2_lN5stxxl14write_time_cmpEEvT_S5_T0_T1_T2_@Base 0.9 _ZSt17__rotate_adaptiveIPSt4pairIiiES2_lET_S3_S3_S3_T1_S4_T0_S4_@Base 0.9 _ZSt21__inplace_stable_sortIPSt4pairIiiEN5stxxl14write_time_cmpEEvT_S5_T0_@Base 0.9 _ZSt22__merge_without_bufferIPSt4pairIiiElN5stxxl14write_time_cmpEEvT_S5_S5_T0_S6_T1_@Base 0.9 _ZSt22__stable_sort_adaptiveIPSt4pairIiiES2_lN5stxxl14write_time_cmpEEvT_S5_T0_T1_T2_@Base 0.9 _ZSt24__merge_sort_with_bufferIPSt4pairIiiES2_N5stxxl14write_time_cmpEEvT_S5_T0_T1_@Base 0.9 _ZSt3decRSt8ios_base@Base 0.9 _ZSt3hexRSt8ios_base@Base 0.9 _ZSt8__rotateIPSt4pairIiiEEvT_S3_S3_St26random_access_iterator_tag@Base 0.9 _ZTIN5stxxl11FileCreatorE@Base 0.9 _ZTIN5stxxl12DiskGeometryE@Base 0.9 _ZTIN5stxxl12mmap_requestE@Base 0.9 _ZTIN5stxxl12syscall_fileE@Base 0.9 _ZTIN5stxxl13sim_disk_fileE@Base 0.9 _ZTIN5stxxl13ufs_file_baseE@Base 0.9 _ZTIN5stxxl14IC35L080AVVA07E@Base 0.9 _ZTIN5stxxl14resource_errorE@Base 1.0 _ZTIN5stxxl15syscall_requestE@Base 0.9 _ZTIN5stxxl16sim_disk_requestE@Base 0.9 _ZTIN5stxxl16ufs_request_baseE@Base 0.9 _ZTIN5stxxl4fileE@Base 0.9 _ZTIN5stxxl7requestE@Base 0.9 _ZTIN5stxxl8io_errorE@Base 1.0 _ZTIN5stxxl9mmap_fileE@Base 0.9 _ZTSN5stxxl11FileCreatorE@Base 0.9 _ZTSN5stxxl12DiskGeometryE@Base 0.9 _ZTSN5stxxl12mmap_requestE@Base 0.9 _ZTSN5stxxl12syscall_fileE@Base 0.9 _ZTSN5stxxl13sim_disk_fileE@Base 0.9 _ZTSN5stxxl13ufs_file_baseE@Base 0.9 _ZTSN5stxxl14IC35L080AVVA07E@Base 0.9 _ZTSN5stxxl14resource_errorE@Base 1.0 _ZTSN5stxxl15syscall_requestE@Base 0.9 _ZTSN5stxxl16sim_disk_requestE@Base 0.9 _ZTSN5stxxl16ufs_request_baseE@Base 0.9 _ZTSN5stxxl4fileE@Base 0.9 _ZTSN5stxxl7requestE@Base 0.9 _ZTSN5stxxl8io_errorE@Base 1.0 _ZTSN5stxxl9mmap_fileE@Base 0.9 _ZTVN5stxxl11FileCreatorE@Base 0.9 _ZTVN5stxxl12mmap_requestE@Base 0.9 _ZTVN5stxxl12syscall_fileE@Base 0.9 _ZTVN5stxxl13sim_disk_fileE@Base 0.9 _ZTVN5stxxl13ufs_file_baseE@Base 0.9 _ZTVN5stxxl14resource_errorE@Base 1.0 _ZTVN5stxxl15syscall_requestE@Base 0.9 _ZTVN5stxxl16sim_disk_requestE@Base 0.9 _ZTVN5stxxl16ufs_request_baseE@Base 0.9 _ZTVN5stxxl4fileE@Base 0.9 _ZTVN5stxxl7requestE@Base 0.9 _ZTVN5stxxl8io_errorE@Base 1.0 _ZTVN5stxxl9mmap_fileE@Base 0.9 stxxl-1.4.1/lib/common/utils.cpp000644 001411 000144 00000007320 12405152405 016377 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/utils.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include STXXL_BEGIN_NAMESPACE //! Parse a string like "343KB" or " 44 GiB " into the corresponding size in //! bytes. bool parse_SI_IEC_size(const std::string& str, uint64& size, char def_unit) { char* endptr; size = strtoul(str.c_str(), &endptr, 10); if (!endptr) return false; // parse failed, no number while (endptr[0] == ' ') ++endptr; // skip over spaces // multiply with base ^ power unsigned int base = 1000; unsigned int power = 0; if (endptr[0] == 'k' || endptr[0] == 'K') power = 1, ++endptr; else if (endptr[0] == 'm' || endptr[0] == 'M') power = 2, ++endptr; else if (endptr[0] == 'g' || endptr[0] == 'G') power = 3, ++endptr; else if (endptr[0] == 't' || endptr[0] == 'T') power = 4, ++endptr; else if (endptr[0] == 'p' || endptr[0] == 'P') power = 5, ++endptr; // switch to power of two (only if power was set above) if ((endptr[0] == 'i' || endptr[0] == 'I') && power != 0) base = 1024, ++endptr; // byte indicator if (endptr[0] == 'b' || endptr[0] == 'B') { ++endptr; } else if (power == 0) { // no explicit power indicator, and no 'b' or 'B' -> apply default unit switch (def_unit) { default: break; case 'k': power = 1, base = 1000; break; case 'm': power = 2, base = 1000; break; case 'g': power = 3, base = 1000; break; case 't': power = 4, base = 1000; break; case 'p': power = 5, base = 1000; break; case 'K': power = 1, base = 1024; break; case 'M': power = 2, base = 1024; break; case 'G': power = 3, base = 1024; break; case 'T': power = 4, base = 1024; break; case 'P': power = 5, base = 1024; break; } } // skip over spaces while (endptr[0] == ' ') ++endptr; // multiply size for (unsigned int p = 0; p < power; ++p) size *= base; return (endptr[0] == 0); } std::string format_SI_size(uint64 number) { // may not overflow, std::numeric_limits::max() == 16 EiB double multiplier = 1000.0; static const char* SIendings[] = { "", "k", "M", "G", "T", "P", "E" }; unsigned int scale = 0; double number_d = (double)number; while (number_d >= multiplier) { number_d /= multiplier; ++scale; } std::ostringstream out; out << std::fixed << std::setprecision(3) << number_d << ' ' << SIendings[scale]; return out.str(); } std::string format_IEC_size(uint64 number) { // may not overflow, std::numeric_limits::max() == 16 EiB double multiplier = 1024.0; static const char* IECendings[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; unsigned int scale = 0; double number_d = (double)number; while (number_d >= multiplier) { number_d /= multiplier; ++scale; } std::ostringstream out; out << std::fixed << std::setprecision(3) << number_d << ' ' << IECendings[scale]; return out.str(); } STXXL_END_NAMESPACE stxxl-1.4.1/lib/common/verbose.cpp000644 001411 000144 00000004461 12405375303 016713 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/verbose.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #ifndef STXXL_THREAD_ID # if STXXL_STD_THREADS || STXXL_BOOST_THREADS # define STXXL_THREAD_ID (-1) # else # define STXXL_THREAD_ID pthread_self() # endif #endif STXXL_BEGIN_NAMESPACE static const double program_start_time_stamp = timestamp(); void print_msg(const char* label, const std::string& msg, unsigned flags) { std::string s; #ifdef STXXL_PRINT_TIMESTAMP_ALWAYS const bool timestamp_always = true; #else const bool timestamp_always = false; #endif if (timestamp_always || (flags & _STXXL_PRNT_TIMESTAMP)) { double t = timestamp() - program_start_time_stamp; char tstr[23]; /* "[364:23:59:59.999999] " */ snprintf(tstr, sizeof(tstr), "[%d.%02d:%02d:%02d.%06d] ", int(t / (24 * 60 * 60)), int(t / (60 * 60)) % 24, int(t / 60) % 60, int(t) % 60, int((t - floor(t)) * 1000000)); s += tstr; } if (label) { s += '['; s += label; s += "] "; } if (flags & _STXXL_PRNT_THREAD_ID) { char tstr[32]; snprintf(tstr, sizeof(tstr), "[T%ld] ", long(STXXL_THREAD_ID)); s += tstr; } s += msg; if (flags & _STXXL_PRNT_ADDNEWLINE) s += '\n'; if (flags & _STXXL_PRNT_COUT) std::cout << s << std::flush; if (flags & _STXXL_PRNT_CERR) std::cerr << s << std::flush; logger* logger_instance = logger::get_instance(); if (flags & _STXXL_PRNT_LOG) logger_instance->log_stream() << s << std::flush; if (flags & _STXXL_PRNT_ERRLOG) logger_instance->errlog_stream() << s << std::flush; } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/common/exithandler.cpp000644 001411 000144 00000003351 12405375303 017552 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/exithandler.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include // 1. do nothing for default handler // 2. #define STXXL_NON_DEFAULT_EXIT_HANDLER for a handler that does not use atexit() // 3. #define STXXL_EXTERNAL_EXIT_HANDLER to provide your own implementation #ifndef STXXL_EXTERNAL_EXIT_HANDLER #ifndef STXXL_NON_DEFAULT_EXIT_HANDLER #include STXXL_BEGIN_NAMESPACE // default exit handler int register_exit_handler(void (* function)(void)) { return atexit(function); } // default exit handler void run_exit_handlers() { // nothing to do } STXXL_END_NAMESPACE #else // STXXL_NON_DEFAULT_EXIT_HANDLER #include #include STXXL_BEGIN_NAMESPACE mutex exit_handler_mutex; std::vector exit_handlers; int register_exit_handler(void (* function)(void)) { scoped_mutex_lock lock(exit_handler_mutex); exit_handlers.push_back(function); return 0; } // default exit handler void run_exit_handlers() { scoped_mutex_lock lock(exit_handler_mutex); while (!exit_handlers.empty()) { (*(exit_handlers.back()))(); exit_handlers.pop_back(); } } STXXL_END_NAMESPACE #endif // STXXL_NON_DEFAULT_EXIT_HANDLER #endif // STXXL_EXTERNAL_EXIT_HANDLER // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/common/version.cpp000644 001411 000144 00000002125 12405375303 016726 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/version.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007, 2008, 2011 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include STXXL_BEGIN_NAMESPACE int version_major() { return STXXL_VERSION_MAJOR; } int version_minor() { return STXXL_VERSION_MINOR; } int version_patch() { return STXXL_VERSION_PATCH; } int version_integer() { return STXXL_VERSION_INTEGER; } const char * get_library_version_string() { return get_version_string(); } const char * get_library_version_string_long() { return get_version_string_long(); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/common/cmdline.cpp000644 001411 000144 00000024433 12411366426 016665 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/cmdline.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include STXXL_BEGIN_NAMESPACE void cmdline_parser::output_wrap(std::ostream& os, const std::string& text, size_t wraplen, size_t indent_first, size_t indent_rest, size_t current, size_t indent_newline) { std::string::size_type t = 0; size_t indent = indent_first; while (t != text.size()) { std::string::size_type to = t, lspace = t; // scan forward in text until we hit a newline or wrap point while (to != text.size() && to + current + indent < t + wraplen && text[to] != '\n') { if (text[to] == ' ') lspace = to; ++to; } // go back to last space if (to != text.size() && text[to] != '\n' && lspace != t) to = lspace + 1; // output line os << std::string(indent, ' ') << text.substr(t, to - t) << std::endl; current = 0; indent = indent_rest; // skip over last newline if (to != text.size() && text[to] == '\n') { indent = indent_newline; ++to; } t = to; } } void cmdline_parser::print_usage(std::ostream& os) { std::ios state(NULL); state.copyfmt(os); os << "Usage: " << m_progname << (m_optlist.size() ? " [options]" : ""); for (arglist_type::const_iterator it = m_paramlist.begin(); it != m_paramlist.end(); ++it) { const argument* arg = *it; os << (arg->m_required ? " <" : " [") << arg->m_longkey << (arg->m_repeated ? " ..." : "") << (arg->m_required ? '>' : ']'); } os << std::endl; if (m_description.size()) { os << std::endl; output_wrap(os, m_description, m_linewrap); } if (m_author.size()) { os << "Author: " << m_author << std::endl; } if (m_description.size() || m_author.size()) os << std::endl; if (m_paramlist.size()) { os << "Parameters:" << std::endl; for (arglist_type::const_iterator it = m_paramlist.begin(); it != m_paramlist.end(); ++it) { const argument* arg = *it; os << " " << std::setw(m_param_maxlong) << std::left << arg->param_text(); output_wrap(os, arg->m_desc, m_linewrap, 0, m_param_maxlong + 2, m_param_maxlong + 2, 8); } } if (m_optlist.size()) { os << "Options:" << std::endl; for (arglist_type::const_iterator it = m_optlist.begin(); it != m_optlist.end(); ++it) { const argument* arg = *it; os << " " << std::setw(m_opt_maxlong) << std::left << arg->option_text(); output_wrap(os, arg->m_desc, m_linewrap, 0, m_opt_maxlong + 2, m_opt_maxlong + 2, 8); } } os.copyfmt(state); } void cmdline_parser::print_option_error( int argc, const char* const* argv, const argument* arg, std::ostream& os) { os << "Error: Argument "; if (argc != 0) os << '"' << argv[0] << '"'; os << " for " << arg->type_name() << " option " << arg->option_text() << (argc == 0 ? " is missing!" : " is invalid!") << std::endl << std::endl; print_usage(os); } void cmdline_parser::print_param_error(int argc, const char* const* argv, const argument* arg, std::ostream& os) { os << "Error: Argument "; if (argc != 0) os << '"' << argv[0] << '"'; os << " for " << arg->type_name() << " parameter " << arg->param_text() << (argc == 0 ? " is missing!" : " is invalid!") << std::endl << std::endl; print_usage(os); } bool cmdline_parser::process(int argc, const char* const* argv, std::ostream& os) { m_progname = argv[0]; --argc, ++argv; // search for help string and output help for (int i = 0; i < argc; ++i) { if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { print_usage(os); return false; } } arglist_type::iterator argi = m_paramlist.begin(); // current argument in m_paramlist bool end_optlist = false; while (argc != 0) { const char* arg = argv[0]; if (arg[0] == '-' && !end_optlist) { // option, advance to argument --argc, ++argv; if (arg[1] == '-') { if (arg[2] == '-') { end_optlist = true; } else { // long option arglist_type::const_iterator oi = m_optlist.begin(); for ( ; oi != m_optlist.end(); ++oi) { if ((arg + 2) == (*oi)->m_longkey) { if (!(*oi)->process(argc, argv)) { print_option_error(argc, argv, *oi, os); return false; } else if (m_verbose_process) { os << "Option " << (*oi)->option_text() << " set to "; (*oi)->print_value(os); os << '.' << std::endl; } break; } } if (oi == m_optlist.end()) { os << "Error: Unknown option \"" << arg << "\"." << std::endl << std::endl; print_usage(os); return false; } } } else { // short option if (arg[1] == 0) { os << "Invalid option \"" << arg << '"' << std::endl; } else { arglist_type::const_iterator oi = m_optlist.begin(); for ( ; oi != m_optlist.end(); ++oi) { if (arg[1] == (*oi)->m_key) { if (!(*oi)->process(argc, argv)) { print_option_error(argc, argv, *oi, os); return false; } else if (m_verbose_process) { os << "Option " << (*oi)->option_text() << " set to "; (*oi)->print_value(os); os << '.' << std::endl; } break; } } if (oi == m_optlist.end()) { os << "Error: Unknown option \"" << arg << "\"." << std::endl << std::endl; print_usage(os); return false; } } } } else { if (argi != m_paramlist.end()) { if (!(*argi)->process(argc, argv)) { print_param_error(argc, argv, *argi, os); return false; } else if (m_verbose_process) { os << "Parameter " << (*argi)->param_text() << " set to "; (*argi)->print_value(os); os << '.' << std::endl; } (*argi)->m_found = true; if (!(*argi)->m_repeated) ++argi; } else { os << "Error: Unexpected extra argument \"" << argv[0] << "\"." << std::endl << std::endl; --argc, ++argv; print_usage(os); return false; } } } bool good = true; for (arglist_type::const_iterator it = m_paramlist.begin(); it != m_paramlist.end(); ++it) { if ((*it)->m_required && !(*it)->m_found) { os << "Error: Argument for parameter " << (*it)->m_longkey << " is required!" << std::endl; good = false; } } if (!good) { os << std::endl; print_usage(os); } return good; } void cmdline_parser::print_result(std::ostream& os) { std::ios state(NULL); state.copyfmt(os); int maxlong = STXXL_MAX(m_param_maxlong, m_opt_maxlong); if (m_paramlist.size()) { os << "Parameters:" << std::endl; for (arglist_type::const_iterator it = m_paramlist.begin(); it != m_paramlist.end(); ++it) { const argument* arg = *it; os << " " << std::setw(maxlong) << std::left << arg->param_text(); std::string typestr = "(" + std::string(arg->type_name()) + ")"; os << std::setw(m_maxtypename + 4) << typestr; arg->print_value(os); os << std::endl; } } if (m_optlist.size()) { os << "Options:" << std::endl; for (arglist_type::const_iterator it = m_optlist.begin(); it != m_optlist.end(); ++it) { const argument* arg = *it; os << " " << std::setw(maxlong) << std::left << arg->option_text(); std::string typestr = "(" + std::string(arg->type_name()) + ")"; os << std::setw(m_maxtypename + 4) << std::left << typestr; arg->print_value(os); os << std::endl; } } os.copyfmt(state); } STXXL_END_NAMESPACE stxxl-1.4.1/lib/common/rand.cpp000644 001411 000144 00000001324 12405375303 016165 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/rand.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include STXXL_BEGIN_NAMESPACE unsigned ran32State = get_next_seed(); STXXL_END_NAMESPACE stxxl-1.4.1/lib/common/log.cpp000644 001411 000144 00000002554 12405152405 016024 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/log.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2004-2005 Roman Dementiev * Copyright (C) 2008 Johannes Singler * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include STXXL_BEGIN_NAMESPACE logger::logger() : waitlog_stream_(NULL) { const char* log_filename = getenv("STXXLLOGFILE"); log_stream_.open(log_filename == NULL ? "stxxl.log" : log_filename); const char* errlog_filename = getenv("STXXLERRLOGFILE"); errlog_stream_.open(errlog_filename == NULL ? "stxxl.errlog" : errlog_filename); #ifdef STXXL_WAIT_LOG_ENABLED const char* waitlog_filename = getenv("STXXLWAITLOGFILE"); if (waitlog_filename) { waitlog_stream_ = new std::ofstream(waitlog_filename); *waitlog_stream_ << "# time\trd_incr\twr_incr\tw_read\tw_write" << std::endl; } #endif } logger::~logger() { delete waitlog_stream_; } STXXL_END_NAMESPACE stxxl-1.4.1/lib/common/seed.cpp000644 001411 000144 00000003406 12411366426 016167 0ustar00tbusers000000 000000 /*************************************************************************** * lib/common/seed.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007, 2008 Andreas Beckmann * Copyright (C) 2008 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #if STXXL_WINDOWS #ifndef NOMINMAX #define NOMINMAX #endif #include #include #else #include #include #endif STXXL_BEGIN_NAMESPACE inline unsigned initial_seed(); struct seed_generator_t { unsigned seed; mutex mtx; seed_generator_t(unsigned s) : seed(s) { } }; seed_generator_t & seed_generator() { static seed_generator_t sg(initial_seed()); return sg; } inline unsigned initial_seed() { #ifndef NDEBUG static bool initialized = false; assert(!initialized); // this should only be called once! initialized = true; #endif // NDEBUG #if STXXL_WINDOWS // GetTickCount(): ms since system start return GetTickCount() ^ GetCurrentProcessId(); #else struct timeval tv; gettimeofday(&tv, 0); return (unsigned)(tv.tv_sec ^ tv.tv_usec ^ (getpid() << 16)); #endif } void set_seed(unsigned seed) { scoped_mutex_lock Lock(seed_generator().mtx); seed_generator().seed = seed; } unsigned get_next_seed() { scoped_mutex_lock Lock(seed_generator().mtx); return seed_generator().seed++; } STXXL_END_NAMESPACE stxxl-1.4.1/lib/mng/disk_allocator.cpp000644 001411 000144 00000013350 12405375303 017526 0ustar00tbusers000000 000000 /*************************************************************************** * lib/mng/disk_allocator.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE void disk_allocator::dump() const { int64 total = 0; sortseq::const_iterator cur = free_space.begin(); STXXL_ERRMSG("Free regions dump:"); for ( ; cur != free_space.end(); ++cur) { STXXL_ERRMSG("Free chunk: begin: " << (cur->first) << " size: " << (cur->second)); total += cur->second; } STXXL_ERRMSG("Total bytes: " << total); } void disk_allocator::deallocation_error( stxxl::int64 block_pos, stxxl::int64 block_size, const sortseq::iterator& pred, const sortseq::iterator& succ) const { STXXL_ERRMSG("Error deallocating block at " << block_pos << " size " << block_size); STXXL_ERRMSG(((pred == succ) ? "pred==succ" : "pred!=succ")); if (pred == free_space.end()) { STXXL_ERRMSG("pred==free_space.end()"); } else { if (pred == free_space.begin()) STXXL_ERRMSG("pred==free_space.begin()"); STXXL_ERRMSG("pred: begin=" << pred->first << " size=" << pred->second); } if (succ == free_space.end()) { STXXL_ERRMSG("succ==free_space.end()"); } else { if (succ == free_space.begin()) STXXL_ERRMSG("succ==free_space.begin()"); STXXL_ERRMSG("succ: begin=" << succ->first << " size=" << succ->second); } dump(); } void disk_allocator::add_free_region(stxxl::int64 block_pos, stxxl::int64 block_size) { //assert(block_size); //dump(); STXXL_VERBOSE2("Deallocating a block with size: " << block_size << " position: " << block_pos); stxxl::int64 region_pos = block_pos; stxxl::int64 region_size = block_size; if (!free_space.empty()) { sortseq::iterator succ = free_space.upper_bound(region_pos); sortseq::iterator pred = succ; if (pred != free_space.begin()) pred--; if (pred != free_space.end()) { if (pred->first <= region_pos && pred->first + pred->second > region_pos) { STXXL_THROW2(bad_ext_alloc, "disk_allocator::check_corruption", "Error: double deallocation of external memory, trying to deallocate region " << region_pos << " + " << region_size << " in empty space [" << pred->first << " + " << pred->second << "]"); } } if (succ != free_space.end()) { if (region_pos <= succ->first && region_pos + region_size > succ->first) { STXXL_THROW2(bad_ext_alloc, "disk_allocator::check_corruption", "Error: double deallocation of external memory, trying to deallocate region " << region_pos << " + " << region_size << " which overlaps empty space [" << succ->first << " + " << succ->second << "]"); } } if (succ == free_space.end()) { if (pred == free_space.end()) { deallocation_error(block_pos, block_size, pred, succ); assert(pred != free_space.end()); } if ((*pred).first + (*pred).second == region_pos) { // coalesce with predecessor region_size += (*pred).second; region_pos = (*pred).first; free_space.erase(pred); } } else { if (free_space.size() > 1) { #if 0 if (pred == succ) { deallocation_error(block_pos, block_size, pred, succ); assert(pred != succ); } #endif bool succ_is_not_the_first = (succ != free_space.begin()); if ((*succ).first == region_pos + region_size) { // coalesce with successor region_size += (*succ).second; free_space.erase(succ); //-tb: set succ to pred afterwards due to iterator invalidation succ = pred; } if (succ_is_not_the_first) { if (pred == free_space.end()) { deallocation_error(block_pos, block_size, pred, succ); assert(pred != free_space.end()); } if ((*pred).first + (*pred).second == region_pos) { // coalesce with predecessor region_size += (*pred).second; region_pos = (*pred).first; free_space.erase(pred); } } } else { if ((*succ).first == region_pos + region_size) { // coalesce with successor region_size += (*succ).second; free_space.erase(succ); } } } } free_space[region_pos] = region_size; free_bytes += block_size; //dump(); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/mng/block_manager.cpp000644 001411 000144 00000006335 12405152405 017321 0ustar00tbusers000000 000000 /*************************************************************************** * lib/mng/block_manager.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2008, 2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE class io_error; block_manager::block_manager() { config* config = config::get_instance(); // initialize config (may read config files now) config->check_initialized(); // allocate disk_allocators ndisks = config->disks_number(); disk_allocators = new disk_allocator*[ndisks]; disk_files = new file*[ndisks]; uint64 total_size = 0; for (unsigned i = 0; i < ndisks; ++i) { disk_config& cfg = config->disk(i); // assign queues in order of disks. if (cfg.queue == file::DEFAULT_QUEUE) cfg.queue = i; try { disk_files[i] = create_file(cfg, file::CREAT | file::RDWR, i); STXXL_MSG("Disk '" << cfg.path << "' is allocated, space: " << (cfg.size) / (1024 * 1024) << " MiB, I/O implementation: " << cfg.fileio_string()); } catch (io_error&) { STXXL_MSG("Error allocating disk '" << cfg.path << "', space: " << (cfg.size) / (1024 * 1024) << " MiB, I/O implementation: " << cfg.fileio_string()); throw; } total_size += cfg.size; disk_allocators[i] = new disk_allocator(disk_files[i], cfg); } if (ndisks > 1) { STXXL_MSG("In total " << ndisks << " disks are allocated, space: " << (total_size / (1024 * 1024)) << " MiB"); } #if STXXL_MNG_COUNT_ALLOCATION m_current_allocation = 0; m_total_allocation = 0; m_maximum_allocation = 0; #endif // STXXL_MNG_COUNT_ALLOCATION } block_manager::~block_manager() { STXXL_VERBOSE1("Block manager destructor"); for (size_t i = ndisks; i > 0; ) { --i; delete disk_allocators[i]; delete disk_files[i]; } delete[] disk_allocators; delete[] disk_files; } uint64 block_manager::get_total_bytes() const { uint64 total = 0; for (unsigned i = 0; i < ndisks; ++i) total += disk_allocators[i]->get_total_bytes(); return total; } uint64 block_manager::get_free_bytes() const { uint64 total = 0; for (unsigned i = 0; i < ndisks; ++i) total += disk_allocators[i]->get_free_bytes(); return total; } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/mng/config.cpp000644 001411 000144 00000031420 12411366426 016002 0ustar00tbusers000000 000000 /*************************************************************************** * lib/mng/config.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2007, 2009 Johannes Singler * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #if STXXL_WINDOWS #ifndef NOMINMAX #define NOMINMAX #endif #include #else #include #endif STXXL_BEGIN_NAMESPACE static inline bool exist_file(const std::string& path) { //STXXL_MSG("Checking " << path << " for disk configuration."); std::ifstream in(path.c_str()); return in.good(); } config::~config() { for (disk_list_type::const_iterator it = disks_list.begin(); it != disks_list.end(); it++) { if (it->delete_on_exit) { STXXL_ERRMSG("Removing disk file: " << it->path); unlink(it->path.c_str()); } } } void config::initialize() { // if disks_list is empty, then try to load disk configuration files if (disks_list.size() == 0) { find_config(); } m_max_device_id = 0; is_initialized = true; } void config::find_config() { // check several locations for disk configuration files // check STXXLCFG environment path const char* stxxlcfg = getenv("STXXLCFG"); if (stxxlcfg && exist_file(stxxlcfg)) return load_config_file(stxxlcfg); #if !STXXL_WINDOWS // read environment, unix style const char* hostname = getenv("HOSTNAME"); const char* home = getenv("HOME"); const char* suffix = ""; #else // read environment, windows style const char* hostname = getenv("COMPUTERNAME"); const char* home = getenv("APPDATA"); const char* suffix = ".txt"; #endif // check current directory { std::string basepath = "./.stxxl"; if (hostname && exist_file(basepath + "." + hostname + suffix)) return load_config_file(basepath + "." + hostname + suffix); if (exist_file(basepath + suffix)) return load_config_file(basepath + suffix); } // check home directory if (home) { std::string basepath = std::string(home) + "/.stxxl"; if (hostname && exist_file(basepath + "." + hostname + suffix)) return load_config_file(basepath + "." + hostname + suffix); if (exist_file(basepath + suffix)) return load_config_file(basepath + suffix); } // load default configuration load_default_config(); } void config::load_default_config() { STXXL_ERRMSG("Warning: no config file found."); STXXL_ERRMSG("Using default disk configuration."); #if !STXXL_WINDOWS disk_config entry1("/var/tmp/stxxl", 1000 * 1024 * 1024, "syscall"); entry1.delete_on_exit = true; entry1.autogrow = true; #else disk_config entry1("", 1000 * 1024 * 1024, "wincall"); entry1.delete_on_exit = true; entry1.autogrow = true; char* tmpstr = new char[255]; if (GetTempPath(255, tmpstr) == 0) STXXL_THROW_WIN_LASTERROR(resource_error, "GetTempPath()"); entry1.path = tmpstr; entry1.path += "stxxl.tmp"; delete[] tmpstr; #endif disks_list.push_back(entry1); // no flash disks first_flash = (unsigned int)disks_list.size(); } void config::load_config_file(const std::string& config_path) { std::vector flash_list; std::ifstream cfg_file(config_path.c_str()); if (!cfg_file) return load_default_config(); std::string line; while (std::getline(cfg_file, line)) { // skip comments if (line.size() == 0 || line[0] == '#') continue; disk_config entry; entry.parse_line(line); // throws on errors if (!entry.flash) disks_list.push_back(entry); else flash_list.push_back(entry); } cfg_file.close(); // put flash devices after regular disks first_flash = (unsigned int)disks_list.size(); disks_list.insert(disks_list.end(), flash_list.begin(), flash_list.end()); if (disks_list.empty()) { STXXL_THROW(std::runtime_error, "No disks found in '" << config_path << "'."); } } //! Returns automatic physical device id counter unsigned int config::get_max_device_id() { return m_max_device_id; } //! Returns next automatic physical device id counter unsigned int config::get_next_device_id() { return m_max_device_id++; } //! Update the automatic physical device id counter void config::update_max_device_id(unsigned int devid) { if (m_max_device_id < devid + 1) m_max_device_id = devid + 1; } uint64 config::total_size() const { assert(is_initialized); uint64 total_size = 0; for (disk_list_type::const_iterator it = disks_list.begin(); it != disks_list.end(); it++) { total_size += it->size; } return total_size; } //////////////////////////////////////////////////////////////////////////////// disk_config::disk_config() : size(0), autogrow(false), delete_on_exit(false), direct(DIRECT_TRY), flash(false), queue(file::DEFAULT_QUEUE), device_id(file::DEFAULT_DEVICE_ID), raw_device(false), unlink_on_open(false), queue_length(0) { } disk_config::disk_config(const std::string& _path, uint64 _size, const std::string& _io_impl) : path(_path), size(_size), io_impl(_io_impl), autogrow(false), delete_on_exit(false), direct(DIRECT_TRY), flash(false), queue(file::DEFAULT_QUEUE), device_id(file::DEFAULT_DEVICE_ID), raw_device(false), unlink_on_open(false), queue_length(0) { parse_fileio(); } disk_config::disk_config(const std::string& line) : size(0), autogrow(false), delete_on_exit(false), direct(DIRECT_TRY), flash(false), queue(file::DEFAULT_QUEUE), device_id(file::DEFAULT_DEVICE_ID), raw_device(false), unlink_on_open(false), queue_length(0) { parse_line(line); } void disk_config::parse_line(const std::string& line) { // split off disk= or flash= std::vector eqfield = split(line, "=", 2, 2); if (eqfield[0] == "disk") { flash = false; } else if (eqfield[0] == "flash") { flash = true; } else { STXXL_THROW(std::runtime_error, "Unknown configuration token " << eqfield[0]); } // *** Set Default Extra Options *** autogrow = false; delete_on_exit = false; direct = DIRECT_TRY; // flash is already set queue = file::DEFAULT_QUEUE; device_id = file::DEFAULT_DEVICE_ID; unlink_on_open = false; // *** Save Basic Options *** // split at commands, at least 3 fields std::vector cmfield = split(eqfield[1], ",", 3, 3); // path: path = cmfield[0]; // replace ### -> pid in path { std::string::size_type pos; if ((pos = path.find("###")) != std::string::npos) { #if !STXXL_WINDOWS int pid = getpid(); #else DWORD pid = GetCurrentProcessId(); #endif path.replace(pos, 3, to_str(pid)); } } // size: (default unit MiB) if (!parse_SI_IEC_size(cmfield[1], size, 'M')) { STXXL_THROW(std::runtime_error, "Invalid disk size '" << cmfield[1] << "' in disk configuration file."); } if (size == 0) { autogrow = true; delete_on_exit = true; } // io_impl: io_impl = cmfield[2]; parse_fileio(); } void disk_config::parse_fileio() { // skip over leading spaces size_t leadspace = io_impl.find_first_not_of(' '); if (leadspace > 0) io_impl = io_impl.substr(leadspace); // split off extra fileio parameters size_t spacepos = io_impl.find(' '); if (spacepos == std::string::npos) return; // no space in fileio // *** Parse Extra Fileio Parameters *** std::string paramstr = io_impl.substr(spacepos + 1); io_impl = io_impl.substr(0, spacepos); std::vector param = split(paramstr, " "); for (std::vector::const_iterator p = param.begin(); p != param.end(); ++p) { // split at equal sign std::vector eq = split(*p, "=", 2, 2); // *** PLEASE try to keep the elseifs sorted by parameter name! if (*p == "") { // skip blank options } else if (*p == "autogrow") { // TODO: which fileio implementation support autogrow? autogrow = true; } else if (*p == "delete" || *p == "delete_on_exit") { delete_on_exit = true; } else if (*p == "direct" || *p == "nodirect" || eq[0] == "direct") { // io_impl is not checked here, but I guess that is okay for DIRECT // since it depends highly platform _and_ build-time configuration. if (*p == "direct") direct = DIRECT_ON; // force ON else if (*p == "nodirect") direct = DIRECT_OFF; // force OFF else if (eq[1] == "off") direct = DIRECT_OFF; else if (eq[1] == "try") direct = DIRECT_TRY; else if (eq[1] == "on") direct = DIRECT_ON; else if (eq[1] == "no") direct = DIRECT_OFF; else if (eq[1] == "yes") direct = DIRECT_ON; else { STXXL_THROW(std::runtime_error, "Invalid parameter '" << *p << "' in disk configuration file."); } } else if (eq[0] == "queue") { if (io_impl == "linuxaio") { STXXL_THROW(std::runtime_error, "Parameter '" << *p << "' invalid for fileio '" << io_impl << "' in disk configuration file."); } char* endp; queue = (int)strtoul(eq[1].c_str(), &endp, 10); if (endp && *endp != 0) { STXXL_THROW(std::runtime_error, "Invalid parameter '" << *p << "' in disk configuration file."); } } else if (eq[0] == "device_id" || eq[0] == "devid") { char* endp; device_id = (int)strtoul(eq[1].c_str(), &endp, 10); if (endp && *endp != 0) { STXXL_THROW(std::runtime_error, "Invalid parameter '" << *p << "' in disk configuration file."); } } else if (*p == "raw_device") { if (!(io_impl == "syscall")) { STXXL_THROW(std::runtime_error, "Parameter '" << *p << "' invalid for fileio '" << io_impl << "' in disk configuration file."); } raw_device = true; } else if (*p == "unlink" || *p == "unlink_on_open") { if (!(io_impl == "syscall" || io_impl == "linuxaio" || io_impl == "mmap" || io_impl == "wbtl")) { STXXL_THROW(std::runtime_error, "Parameter '" << *p << "' invalid for fileio '" << io_impl << "' in disk configuration file."); } unlink_on_open = true; } else { STXXL_THROW(std::runtime_error, "Invalid optional parameter '" << *p << "' in disk configuration file."); } } } std::string disk_config::fileio_string() const { std::ostringstream oss; oss << io_impl; if (autogrow) oss << " autogrow"; if (delete_on_exit) oss << " delete_on_exit"; // tristate direct variable: OFF, TRY, ON if (direct == DIRECT_OFF) oss << " direct=off"; else if (direct == DIRECT_TRY) ; // silenced: oss << " direct=try"; else if (direct == DIRECT_ON) oss << " direct=on"; else STXXL_THROW(std::runtime_error, "Invalid setting for 'direct' option."); if (flash) oss << " flash"; if (queue != file::DEFAULT_QUEUE && queue != file::DEFAULT_LINUXAIO_QUEUE) oss << " queue=" << queue; if (device_id != file::DEFAULT_DEVICE_ID) oss << " devid=" << device_id; if (raw_device) oss << " raw_device"; if (unlink_on_open) oss << " unlink_on_open"; if (queue_length != 0) oss << " queue_length=" << queue_length; return oss.str(); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/iostats.cpp000644 001411 000144 00000030543 12411366426 016056 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/iostats.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2004 Roman Dementiev * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2009, 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE stats::stats() : reads(0), writes(0), volume_read(0), volume_written(0), c_reads(0), c_writes(0), c_volume_read(0), c_volume_written(0), t_reads(0.0), t_writes(0.0), p_reads(0.0), p_writes(0.0), p_begin_read(0.0), p_begin_write(0.0), p_ios(0.0), p_begin_io(0.0), t_waits(0.0), p_waits(0.0), p_begin_wait(0.0), t_wait_read(0.0), p_wait_read(0.0), p_begin_wait_read(0.0), t_wait_write(0.0), p_wait_write(0.0), p_begin_wait_write(0.0), acc_reads(0), acc_writes(0), acc_ios(0), acc_waits(0), acc_wait_read(0), acc_wait_write(0), last_reset(timestamp()) { } #ifndef STXXL_IO_STATS_RESET_FORBIDDEN void stats::reset() { { scoped_mutex_lock ReadLock(read_mutex); //assert(acc_reads == 0); if (acc_reads) STXXL_ERRMSG("Warning: " << acc_reads << " read(s) not yet finished"); reads = 0; volume_read = 0; c_reads = 0; c_volume_read = 0; t_reads = 0; p_reads = 0.0; } { scoped_mutex_lock WriteLock(write_mutex); //assert(acc_writes == 0); if (acc_writes) STXXL_ERRMSG("Warning: " << acc_writes << " write(s) not yet finished"); writes = 0; volume_written = 0; c_writes = 0; c_volume_written = 0; t_writes = 0.0; p_writes = 0.0; } { scoped_mutex_lock IOLock(io_mutex); //assert(acc_ios == 0); if (acc_ios) STXXL_ERRMSG("Warning: " << acc_ios << " io(s) not yet finished"); p_ios = 0.0; } { scoped_mutex_lock WaitLock(wait_mutex); //assert(acc_waits == 0); if (acc_waits) STXXL_ERRMSG("Warning: " << acc_waits << " wait(s) not yet finished"); t_waits = 0.0; p_waits = 0.0; t_wait_read = 0.0; p_wait_read = 0.0; t_wait_write = 0.0; p_wait_write = 0.0; } last_reset = timestamp(); } #endif #if STXXL_IO_STATS void stats::write_started(unsigned_type size_, double now) { if (now == 0.0) now = timestamp(); { scoped_mutex_lock WriteLock(write_mutex); ++writes; volume_written += size_; double diff = now - p_begin_write; t_writes += double(acc_writes) * diff; p_begin_write = now; p_writes += (acc_writes++) ? diff : 0.0; } { scoped_mutex_lock IOLock(io_mutex); double diff = now - p_begin_io; p_ios += (acc_ios++) ? diff : 0.0; p_begin_io = now; } } void stats::write_canceled(unsigned_type size_) { { scoped_mutex_lock WriteLock(write_mutex); --writes; volume_written -= size_; } write_finished(); } void stats::write_finished() { double now = timestamp(); { scoped_mutex_lock WriteLock(write_mutex); double diff = now - p_begin_write; t_writes += double(acc_writes) * diff; p_begin_write = now; p_writes += (acc_writes--) ? diff : 0.0; } { scoped_mutex_lock IOLock(io_mutex); double diff = now - p_begin_io; p_ios += (acc_ios--) ? diff : 0.0; p_begin_io = now; } } void stats::write_cached(unsigned_type size_) { scoped_mutex_lock WriteLock(write_mutex); ++c_writes; c_volume_written += size_; } void stats::read_started(unsigned_type size_, double now) { if (now == 0.0) now = timestamp(); { scoped_mutex_lock ReadLock(read_mutex); ++reads; volume_read += size_; double diff = now - p_begin_read; t_reads += double(acc_reads) * diff; p_begin_read = now; p_reads += (acc_reads++) ? diff : 0.0; } { scoped_mutex_lock IOLock(io_mutex); double diff = now - p_begin_io; p_ios += (acc_ios++) ? diff : 0.0; p_begin_io = now; } } void stats::read_canceled(unsigned_type size_) { { scoped_mutex_lock ReadLock(read_mutex); --reads; volume_read -= size_; } read_finished(); } void stats::read_finished() { double now = timestamp(); { scoped_mutex_lock ReadLock(read_mutex); double diff = now - p_begin_read; t_reads += double(acc_reads) * diff; p_begin_read = now; p_reads += (acc_reads--) ? diff : 0.0; } { scoped_mutex_lock IOLock(io_mutex); double diff = now - p_begin_io; p_ios += (acc_ios--) ? diff : 0.0; p_begin_io = now; } } void stats::read_cached(unsigned_type size_) { scoped_mutex_lock ReadLock(read_mutex); ++c_reads; c_volume_read += size_; } #endif #ifndef STXXL_DO_NOT_COUNT_WAIT_TIME void stats::wait_started(wait_op_type wait_op) { double now = timestamp(); { scoped_mutex_lock WaitLock(wait_mutex); double diff = now - p_begin_wait; t_waits += double(acc_waits) * diff; p_begin_wait = now; p_waits += (acc_waits++) ? diff : 0.0; if (wait_op == WAIT_OP_READ) { diff = now - p_begin_wait_read; t_wait_read += double(acc_wait_read) * diff; p_begin_wait_read = now; p_wait_read += (acc_wait_read++) ? diff : 0.0; } else /* if (wait_op == WAIT_OP_WRITE) */ { // wait_any() is only used from write_pool and buffered_writer, so account WAIT_OP_ANY for WAIT_OP_WRITE, too diff = now - p_begin_wait_write; t_wait_write += double(acc_wait_write) * diff; p_begin_wait_write = now; p_wait_write += (acc_wait_write++) ? diff : 0.0; } } } void stats::wait_finished(wait_op_type wait_op) { double now = timestamp(); { scoped_mutex_lock WaitLock(wait_mutex); double diff = now - p_begin_wait; t_waits += double(acc_waits) * diff; p_begin_wait = now; p_waits += (acc_waits--) ? diff : 0.0; if (wait_op == WAIT_OP_READ) { double diff = now - p_begin_wait_read; t_wait_read += double(acc_wait_read) * diff; p_begin_wait_read = now; p_wait_read += (acc_wait_read--) ? diff : 0.0; } else /* if (wait_op == WAIT_OP_WRITE) */ { double diff = now - p_begin_wait_write; t_wait_write += double(acc_wait_write) * diff; p_begin_wait_write = now; p_wait_write += (acc_wait_write--) ? diff : 0.0; } #ifdef STXXL_WAIT_LOG_ENABLED std::ofstream* waitlog = stxxl::logger::get_instance()->waitlog_stream(); if (waitlog) *waitlog << (now - last_reset) << "\t" << ((wait_op == WAIT_OP_READ) ? diff : 0.0) << "\t" << ((wait_op != WAIT_OP_READ) ? diff : 0.0) << "\t" << t_wait_read << "\t" << t_wait_write << std::endl << std::flush; #endif } } #endif void stats::_reset_io_wait_time() { #ifndef STXXL_DO_NOT_COUNT_WAIT_TIME { scoped_mutex_lock WaitLock(wait_mutex); //assert(acc_waits == 0); if (acc_waits) STXXL_ERRMSG("Warning: " << acc_waits << " wait(s) not yet finished"); t_waits = 0.0; p_waits = 0.0; } #endif } std::string format_with_SI_IEC_unit_multiplier(uint64 number, const char* unit, int multiplier) { // may not overflow, std::numeric_limits::max() == 16 EB static const char* endings[] = { "", "k", "M", "G", "T", "P", "E" }; static const char* binary_endings[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; std::ostringstream out; out << number << ' '; int scale = 0; double number_d = (double)number; double multiplier_d = multiplier; while (number_d >= multiplier_d) { number_d /= multiplier_d; ++scale; } if (scale > 0) out << '(' << std::fixed << std::setprecision(3) << number_d << ' ' << (multiplier == 1024 ? binary_endings[scale] : endings[scale]) << (unit ? unit : "") << ") "; else if (unit && *unit) out << unit << ' '; return out.str(); } std::ostream& operator << (std::ostream& o, const stats_data& s) { #define hr add_IEC_binary_multiplier o << "STXXL I/O statistics" << std::endl; #if STXXL_IO_STATS o << " total number of reads : " << hr(s.get_reads()) << std::endl; o << " average block size (read) : " << hr(s.get_reads() ? s.get_read_volume() / s.get_reads() : 0, "B") << std::endl; o << " number of bytes read from disks : " << hr(s.get_read_volume(), "B") << std::endl; o << " time spent in serving all read requests : " << s.get_read_time() << " s" << " @ " << ((double)s.get_read_volume() / 1048576.0 / s.get_read_time()) << " MiB/s" << std::endl; o << " time spent in reading (parallel read time) : " << s.get_pread_time() << " s" << " @ " << ((double)s.get_read_volume() / 1048576.0 / s.get_pread_time()) << " MiB/s" << std::endl; if (s.get_cached_reads()) { o << " total number of cached reads : " << hr(s.get_cached_reads()) << std::endl; o << " average block size (cached read) : " << hr(s.get_cached_read_volume() / s.get_cached_reads(), "B") << std::endl; o << " number of bytes read from cache : " << hr(s.get_cached_read_volume(), "B") << std::endl; } if (s.get_cached_writes()) { o << " total number of cached writes : " << hr(s.get_cached_writes()) << std::endl; o << " average block size (cached write) : " << hr(s.get_cached_written_volume() / s.get_cached_writes(), "B") << std::endl; o << " number of bytes written to cache : " << hr(s.get_cached_written_volume(), "B") << std::endl; } o << " total number of writes : " << hr(s.get_writes()) << std::endl; o << " average block size (write) : " << hr(s.get_writes() ? s.get_written_volume() / s.get_writes() : 0, "B") << std::endl; o << " number of bytes written to disks : " << hr(s.get_written_volume(), "B") << std::endl; o << " time spent in serving all write requests : " << s.get_write_time() << " s" << " @ " << ((double)s.get_written_volume() / 1048576.0 / s.get_write_time()) << " MiB/s" << std::endl; o << " time spent in writing (parallel write time): " << s.get_pwrite_time() << " s" << " @ " << ((double)s.get_written_volume() / 1048576.0 / s.get_pwrite_time()) << " MiB/s" << std::endl; o << " time spent in I/O (parallel I/O time) : " << s.get_pio_time() << " s" << " @ " << ((double)(s.get_read_volume() + s.get_written_volume()) / 1048576.0 / s.get_pio_time()) << " MiB/s" << std::endl; #else o << " n/a" << std::endl; #endif #ifndef STXXL_DO_NOT_COUNT_WAIT_TIME o << " I/O wait time : " << s.get_io_wait_time() << " s" << std::endl; if (s.get_wait_read_time() != 0.0) o << " I/O wait4read time : " << s.get_wait_read_time() << " s" << std::endl; if (s.get_wait_write_time() != 0.0) o << " I/O wait4write time : " << s.get_wait_write_time() << " s" << std::endl; #endif o << " Time since the last reset : " << s.get_elapsed_time() << " s" << std::endl; return o; #undef hr } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/boostfd_file.cpp000644 001411 000144 00000013617 12414452316 017027 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/boostfd_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2009, 2010 Johannes Singler * Copyright (C) 2008, 2010 Andreas Beckmann * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #if STXXL_HAVE_BOOSTFD_FILE #include #include #include #include #include STXXL_BEGIN_NAMESPACE void boostfd_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { scoped_mutex_lock fd_lock(m_fd_mutex); try { m_file_des.seek(offset, BOOST_IOS::beg); } catch (const std::exception& ex) { STXXL_THROW_ERRNO (io_error, "Error doing seek() in boostfd_request::serve()" << " offset=" << offset << " this=" << this << " buffer=" << buffer << " bytes=" << bytes << " type=" << ((type == request::READ) ? "READ" : "WRITE") << " : " << ex.what()); } stats::scoped_read_write_timer read_write_timer(bytes, type == request::WRITE); if (type == request::READ) { try { std::streamsize rc = m_file_des.read((char*)buffer, bytes); if (rc != std::streamsize(bytes)) { STXXL_THROW_ERRNO(io_error, " partial read: " << rc << " missing " << (bytes - rc) << " out of " << bytes << " bytes"); } } catch (const std::exception& ex) { STXXL_THROW_ERRNO (io_error, "Error doing read() in boostfd_request::serve()" << " offset=" << offset << " this=" << this << " buffer=" << buffer << " bytes=" << bytes << " type=" << ((type == request::READ) ? "READ" : "WRITE") << " : " << ex.what()); } } else { try { std::streamsize rc = m_file_des.write((char*)buffer, bytes); if (rc != std::streamsize(bytes)) { STXXL_THROW_ERRNO(io_error, " partial write: " << rc << " missing " << (bytes - rc) << " out of " << bytes << " bytes"); } } catch (const std::exception& ex) { STXXL_THROW_ERRNO (io_error, "Error doing write() in boostfd_request::serve()" << " offset=" << offset << " this=" << this << " buffer=" << buffer << " bytes=" << bytes << " type=" << ((type == request::READ) ? "READ" : "WRITE") << " : " << ex.what()); } } } const char* boostfd_file::io_type() const { return "boostfd"; } boostfd_file::boostfd_file( const std::string& filename, int mode, int queue_id, int allocator_id, unsigned int device_id) : file(device_id), disk_queued_file(queue_id, allocator_id), m_mode(mode) { BOOST_IOS::openmode boostfd_mode = (mode & RDWR) ? (BOOST_IOS::out | BOOST_IOS::in) : (mode & WRONLY) ? (BOOST_IOS::out) : (mode & RDONLY) ? (BOOST_IOS::in) : BOOST_IOS::in; #if defined(BOOST_FILESYSTEM_VERSION) && (BOOST_FILESYSTEM_VERSION >= 3) const boost::filesystem::path fspath(filename); #else const boost::filesystem::path fspath(filename, boost::filesystem::native); #endif if (mode & TRUNC) { if (boost::filesystem::exists(fspath)) { boost::filesystem::remove(fspath); boost::filesystem::ofstream f(fspath); f.close(); assert(boost::filesystem::exists(fspath)); } } if (mode & CREAT) { // need to be emulated: if (!boost::filesystem::exists(fspath)) { boost::filesystem::ofstream f(fspath); f.close(); assert(boost::filesystem::exists(fspath)); } } if (mode & DIRECT) { // direct mode not supported in Boost STXXL_MSG("Warning: open()ing " << filename << " without DIRECT mode, boostfd does not support it."); } if (mode & REQUIRE_DIRECT) { // direct mode not supported in Boost STXXL_ERRMSG("Error: open()ing " << filename << " with REQUIRE_DIRECT mode, but boostfd does not support it."); return; } if (mode & SYNC) { // ??? } #if (BOOST_VERSION >= 104100) m_file_des.open(filename, boostfd_mode); // also compiles with earlier Boost versions, but differs semantically #else m_file_des.open(filename, boostfd_mode, boostfd_mode); #endif } boostfd_file::~boostfd_file() { scoped_mutex_lock fd_lock(m_fd_mutex); m_file_des.close(); } inline file::offset_type boostfd_file::_size() { return m_file_des.seek(0, BOOST_IOS::end); } file::offset_type boostfd_file::size() { scoped_mutex_lock fd_lock(m_fd_mutex); return _size(); } void boostfd_file::set_size(offset_type newsize) { scoped_mutex_lock fd_lock(m_fd_mutex); offset_type oldsize = _size(); m_file_des.seek(newsize, BOOST_IOS::beg); m_file_des.seek(0, BOOST_IOS::beg); // not important ? STXXL_ASSERT(_size() >= oldsize); } void boostfd_file::lock() { // FIXME: is there no locking possible/needed/... for boostfd? } STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_BOOSTFD_FILE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/request_queue_impl_qwqr.cpp000644 001411 000144 00000015724 12405152405 021354 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/request_queue_impl_qwqr.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2005 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #if STXXL_STD_THREADS && STXXL_MSVC >= 1700 #include #endif #ifndef STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION #define STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION 1 #endif STXXL_BEGIN_NAMESPACE struct file_offset_match : public std::binary_function { bool operator () ( const request_ptr& a, const request_ptr& b) const { // matching file and offset are enough to cause problems return (a->get_offset() == b->get_offset()) && (a->get_file() == b->get_file()); } }; request_queue_impl_qwqr::request_queue_impl_qwqr(int n) : m_thread_state(NOT_RUNNING), m_sem(0) { STXXL_UNUSED(n); start_thread(worker, static_cast(this), m_thread, m_thread_state); } void request_queue_impl_qwqr::add_request(request_ptr& req) { if (req.empty()) STXXL_THROW_INVALID_ARGUMENT("Empty request submitted to disk_queue."); if (m_thread_state() != RUNNING) STXXL_THROW_INVALID_ARGUMENT("Request submitted to not running queue."); if (!dynamic_cast(req.get())) STXXL_ERRMSG("Incompatible request submitted to running queue."); if (req.get()->get_type() == request::READ) { #if STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION { scoped_mutex_lock Lock(m_write_mutex); if (std::find_if(m_write_queue.begin(), m_write_queue.end(), bind2nd(file_offset_match(), req) _STXXL_FORCE_SEQUENTIAL) != m_write_queue.end()) { STXXL_ERRMSG("READ request submitted for a BID with a pending WRITE request"); } } #endif scoped_mutex_lock Lock(m_read_mutex); m_read_queue.push_back(req); } else { #if STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION { scoped_mutex_lock Lock(m_read_mutex); if (std::find_if(m_read_queue.begin(), m_read_queue.end(), bind2nd(file_offset_match(), req) _STXXL_FORCE_SEQUENTIAL) != m_read_queue.end()) { STXXL_ERRMSG("WRITE request submitted for a BID with a pending READ request"); } } #endif scoped_mutex_lock Lock(m_write_mutex); m_write_queue.push_back(req); } m_sem++; } bool request_queue_impl_qwqr::cancel_request(request_ptr& req) { if (req.empty()) STXXL_THROW_INVALID_ARGUMENT("Empty request canceled disk_queue."); if (m_thread_state() != RUNNING) STXXL_THROW_INVALID_ARGUMENT("Request canceled to not running queue."); if (!dynamic_cast(req.get())) STXXL_ERRMSG("Incompatible request submitted to running queue."); bool was_still_in_queue = false; if (req.get()->get_type() == request::READ) { scoped_mutex_lock Lock(m_read_mutex); queue_type::iterator pos = std::find(m_read_queue.begin(), m_read_queue.end(), req _STXXL_FORCE_SEQUENTIAL); if (pos != m_read_queue.end()) { m_read_queue.erase(pos); was_still_in_queue = true; m_sem--; } } else { scoped_mutex_lock Lock(m_write_mutex); queue_type::iterator pos = std::find(m_write_queue.begin(), m_write_queue.end(), req _STXXL_FORCE_SEQUENTIAL); if (pos != m_write_queue.end()) { m_write_queue.erase(pos); was_still_in_queue = true; m_sem--; } } return was_still_in_queue; } request_queue_impl_qwqr::~request_queue_impl_qwqr() { stop_thread(m_thread, m_thread_state, m_sem); } void* request_queue_impl_qwqr::worker(void* arg) { self* pthis = static_cast(arg); bool write_phase = true; for ( ; ; ) { pthis->m_sem--; if (write_phase) { scoped_mutex_lock WriteLock(pthis->m_write_mutex); if (!pthis->m_write_queue.empty()) { request_ptr req = pthis->m_write_queue.front(); pthis->m_write_queue.pop_front(); WriteLock.unlock(); //assert(req->get_reference_count()) > 1); dynamic_cast(req.get())->serve(); } else { WriteLock.unlock(); pthis->m_sem++; if (pthis->m_priority_op == WRITE) write_phase = false; } if (pthis->m_priority_op == NONE || pthis->m_priority_op == READ) write_phase = false; } else { scoped_mutex_lock ReadLock(pthis->m_read_mutex); if (!pthis->m_read_queue.empty()) { request_ptr req = pthis->m_read_queue.front(); pthis->m_read_queue.pop_front(); ReadLock.unlock(); STXXL_VERBOSE2("queue: before serve request has " << req->get_reference_count() << " references "); //assert(req->get_reference_count() > 1); dynamic_cast(req.get())->serve(); STXXL_VERBOSE2("queue: after serve request has " << req->get_reference_count() << " references "); } else { ReadLock.unlock(); pthis->m_sem++; if (pthis->m_priority_op == READ) write_phase = true; } if (pthis->m_priority_op == NONE || pthis->m_priority_op == WRITE) write_phase = true; } // terminate if it has been requested and queues are empty if (pthis->m_thread_state() == TERMINATING) { if ((pthis->m_sem--) == 0) break; else pthis->m_sem++; } } pthis->m_thread_state.set_to(TERMINATED); #if STXXL_STD_THREADS && STXXL_MSVC >= 1700 // Workaround for deadlock bug in Visual C++ Runtime 2012 and 2013, see // request_queue_impl_worker.cpp. -tb ExitThread(NULL); #else return NULL; #endif } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/linuxaio_file.cpp000644 001411 000144 00000003317 12411366426 017216 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/linuxaio_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #if STXXL_HAVE_LINUXAIO_FILE #include #include STXXL_BEGIN_NAMESPACE request_ptr linuxaio_file::aread( void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl) { request_ptr req(new linuxaio_request(on_cmpl, this, buffer, pos, bytes, request::READ)); disk_queues::get_instance()->add_request(req, get_queue_id()); return req; } request_ptr linuxaio_file::awrite( void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl) { request_ptr req(new linuxaio_request(on_cmpl, this, buffer, pos, bytes, request::WRITE)); disk_queues::get_instance()->add_request(req, get_queue_id()); return req; } void linuxaio_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { // req need not be an linuxaio_request if (type == request::READ) aread(buffer, offset, bytes)->wait(); else awrite(buffer, offset, bytes)->wait(); } const char* linuxaio_file::io_type() const { return "linuxaio"; } STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_LINUXAIO_FILE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/linuxaio_queue.cpp000644 001411 000144 00000021650 12414452316 017420 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/linuxaio_queue.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #if STXXL_HAVE_LINUXAIO_FILE #include #include #include #include #include #include #include #include #ifndef STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION #define STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION 1 #endif STXXL_BEGIN_NAMESPACE linuxaio_queue::linuxaio_queue(int desired_queue_length) : num_waiting_requests(0), num_free_events(0), num_posted_requests(0), post_thread_state(NOT_RUNNING), wait_thread_state(NOT_RUNNING) { if (desired_queue_length == 0) { // default value, 64 entries per queue (i.e. usually per disk) should // be enough max_events = 64; } else max_events = desired_queue_length; // negotiate maximum number of simultaneous events with the OS context = 0; long result; while ((result = syscall(SYS_io_setup, max_events, &context)) == -1 && errno == EAGAIN && max_events > 1) { max_events <<= 1; // try with half as many events } if (result != 0) { STXXL_THROW_ERRNO(io_error, "linuxaio_queue::linuxaio_queue" " io_setup() nr_events=" << max_events); } for (int e = 0; e < max_events; ++e) num_free_events++; // cannot set semaphore to value directly STXXL_MSG("Set up an linuxaio queue with " << max_events << " entries."); start_thread(post_async, static_cast(this), post_thread, post_thread_state); start_thread(wait_async, static_cast(this), wait_thread, wait_thread_state); } linuxaio_queue::~linuxaio_queue() { stop_thread(post_thread, post_thread_state, num_waiting_requests); stop_thread(wait_thread, wait_thread_state, num_posted_requests); syscall(SYS_io_destroy, context); } void linuxaio_queue::add_request(request_ptr& req) { if (req.empty()) STXXL_THROW_INVALID_ARGUMENT("Empty request submitted to disk_queue."); if (post_thread_state() != RUNNING) STXXL_ERRMSG("Request submitted to stopped queue."); if (!dynamic_cast(req.get())) STXXL_ERRMSG("Non-LinuxAIO request submitted to LinuxAIO queue."); scoped_mutex_lock lock(waiting_mtx); waiting_requests.push_back(req); num_waiting_requests++; } bool linuxaio_queue::cancel_request(request_ptr& req) { if (req.empty()) STXXL_THROW_INVALID_ARGUMENT("Empty request canceled disk_queue."); if (post_thread_state() != RUNNING) STXXL_ERRMSG("Request canceled in stopped queue."); if (!dynamic_cast(req.get())) STXXL_ERRMSG("Non-LinuxAIO request submitted to LinuxAIO queue."); queue_type::iterator pos; { scoped_mutex_lock lock(waiting_mtx); pos = std::find(waiting_requests.begin(), waiting_requests.end(), req _STXXL_FORCE_SEQUENTIAL); if (pos != waiting_requests.end()) { waiting_requests.erase(pos); // polymorphic_downcast to linuxaio_request, // request is canceled, but was not yet posted. dynamic_cast(pos->get())->completed(false, true); num_waiting_requests--; // will never block return true; } } scoped_mutex_lock lock(posted_mtx); pos = std::find(posted_requests.begin(), posted_requests.end(), req _STXXL_FORCE_SEQUENTIAL); if (pos != posted_requests.end()) { // polymorphic_downcast to linuxaio_request, bool canceled_io_operation = (dynamic_cast(req.get()))->cancel_aio(); if (canceled_io_operation) { posted_requests.erase(pos); // polymorphic_downcast to linuxaio_request, // request is canceled, already posted dynamic_cast(pos->get())->completed(true, true); num_free_events++; num_posted_requests--; // will never block return true; } } return false; } // internal routines, run by the posting thread void linuxaio_queue::post_requests() { request_ptr req; io_event* events = new io_event[max_events]; for ( ; ; ) // as long as thread is running { // might block until next request or message comes in int num_currently_waiting_requests = num_waiting_requests--; // terminate if termination has been requested if (post_thread_state() == TERMINATING && num_currently_waiting_requests == 0) break; scoped_mutex_lock lock(waiting_mtx); if (!waiting_requests.empty()) { req = waiting_requests.front(); waiting_requests.pop_front(); lock.unlock(); num_free_events--; // might block because too many requests are posted // polymorphic_downcast while (!dynamic_cast(req.get())->post()) { // post failed, so first handle events to make queues (more) // empty, then try again. // wait for at least one event to complete, no time limit long num_events = syscall(SYS_io_getevents, context, 1, max_events, events, NULL); if (num_events < 0) { STXXL_THROW_ERRNO(io_error, "linuxaio_queue::post_requests" " io_getevents() nr_events=" << num_events); } handle_events(events, num_events, false); } // request is finally posted { scoped_mutex_lock lock(posted_mtx); posted_requests.push_back(req); num_posted_requests++; } } else { lock.unlock(); // num_waiting_requests-- was premature, compensate for that num_waiting_requests++; } } delete[] events; } void linuxaio_queue::handle_events(io_event* events, long num_events, bool canceled) { for (int e = 0; e < num_events; ++e) { // unsigned_type is as long as a pointer, and like this, we avoid an icpc warning request_ptr* r = reinterpret_cast(static_cast(events[e].data)); r->get()->completed(canceled); delete r; // release auto_ptr reference num_free_events++; num_posted_requests--; // will never block } } // internal routines, run by the waiting thread void linuxaio_queue::wait_requests() { request_ptr req; io_event* events = new io_event[max_events]; for ( ; ; ) // as long as thread is running { // might block until next request is posted or message comes in int num_currently_posted_requests = num_posted_requests--; // terminate if termination has been requested if (wait_thread_state() == TERMINATING && num_currently_posted_requests == 0) break; // wait for at least one of them to finish long num_events = syscall(SYS_io_getevents, context, 1, max_events, events, NULL); if (num_events < 0) { STXXL_THROW_ERRNO(io_error, "linuxaio_queue::wait_requests" " io_getevents() nr_events=" << max_events); } num_posted_requests++; // compensate for the one eaten prematurely above handle_events(events, num_events, false); } delete[] events; } void* linuxaio_queue::post_async(void* arg) { (static_cast(arg))->post_requests(); self_type* pthis = static_cast(arg); pthis->post_thread_state.set_to(TERMINATED); #if STXXL_STD_THREADS && STXXL_MSVC >= 1700 // Workaround for deadlock bug in Visual C++ Runtime 2012 and 2013, see // request_queue_impl_worker.cpp. -tb ExitThread(NULL); #else return NULL; #endif } void* linuxaio_queue::wait_async(void* arg) { (static_cast(arg))->wait_requests(); self_type* pthis = static_cast(arg); pthis->wait_thread_state.set_to(TERMINATED); #if STXXL_STD_THREADS && STXXL_MSVC >= 1700 // Workaround for deadlock bug in Visual C++ Runtime 2012 and 2013, see // request_queue_impl_worker.cpp. -tb ExitThread(NULL); #else return NULL; #endif } STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_LINUXAIO_FILE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/fileperblock_file.cpp000644 001411 000144 00000012552 12411366426 020030 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/fileperblock_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008, 2009 Johannes Singler * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ufs_platform.h" STXXL_BEGIN_NAMESPACE template fileperblock_file::fileperblock_file( const std::string& filename_prefix, int mode, int queue_id, int allocator_id, unsigned int device_id) : file(device_id), disk_queued_file(queue_id, allocator_id), filename_prefix(filename_prefix), mode(mode), current_size(0), lock_file_created(false), lock_file(filename_prefix + "_fpb_lock", mode, queue_id) { } template fileperblock_file::~fileperblock_file() { if (lock_file_created) { if (::remove((filename_prefix + "_fpb_lock").c_str()) != 0) STXXL_ERRMSG("remove() error on path=" << filename_prefix << "_fpb_lock error=" << strerror(errno)); } } template std::string fileperblock_file::filename_for_block(offset_type offset) { std::ostringstream name; //enough for 1 billion blocks name << filename_prefix << "_fpb_" << std::setw(20) << std::setfill('0') << offset; return name.str(); } template void fileperblock_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { base_file_type base_file(filename_for_block(offset), mode, get_queue_id()); base_file.set_size(bytes); base_file.serve(buffer, 0, bytes, type); } template void fileperblock_file::lock() { if (!lock_file_created) { //create lock file and fill it with one page, an empty file cannot be locked const int page_size = STXXL_BLOCK_ALIGN; void* one_page = aligned_alloc(page_size); #if STXXL_WITH_VALGRIND memset(one_page, 0, page_size); #endif lock_file.set_size(page_size); request_ptr r = lock_file.awrite(one_page, 0, page_size); r->wait(); aligned_dealloc(one_page); lock_file_created = true; } lock_file.lock(); } template void fileperblock_file::discard(offset_type offset, offset_type length) { STXXL_UNUSED(length); #ifdef STXXL_FILEPERBLOCK_NO_DELETE if (::truncate(filename_for_block(offset).c_str(), 0) != 0) STXXL_ERRMSG("truncate() error on path=" << filename_for_block(offset) << " error=" << strerror(errno)); #else if (::remove(filename_for_block(offset).c_str()) != 0) STXXL_ERRMSG("remove() error on path=" << filename_for_block(offset) << " error=" << strerror(errno)); #endif STXXL_VERBOSE2("discard " << offset << " + " << length); } template void fileperblock_file::export_files(offset_type offset, offset_type length, std::string filename) { std::string original(filename_for_block(offset)); filename.insert(0, original.substr(0, original.find_last_of("/") + 1)); if (::remove(filename.c_str()) != 0) STXXL_ERRMSG("remove() error on path=" << filename << " error=" << strerror(errno)); if (::rename(original.c_str(), filename.c_str()) != 0) STXXL_ERRMSG("rename() error on path=" << filename << " to=" << original << " error=" << strerror(errno)); #if !STXXL_WINDOWS //TODO: implement on Windows if (::truncate(filename.c_str(), length) != 0) { STXXL_THROW_ERRNO(io_error, "Error doing truncate()"); } #else STXXL_UNUSED(length); #endif } template const char* fileperblock_file::io_type() const { return "fileperblock"; } //////////////////////////////////////////////////////////////////////////// template class fileperblock_file; #if STXXL_HAVE_MMAP_FILE template class fileperblock_file; #endif #if STXXL_HAVE_WINCALL_FILE template class fileperblock_file; #endif #if STXXL_HAVE_BOOSTFD_FILE template class fileperblock_file; #endif STXXL_END_NAMESPACE stxxl-1.4.1/lib/io/linuxaio_request.cpp000644 001411 000144 00000007626 12414452316 017773 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/linuxaio_request.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2011 Johannes Singler * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #if STXXL_HAVE_LINUXAIO_FILE #include #include #include #include #include STXXL_BEGIN_NAMESPACE void linuxaio_request::completed(bool posted, bool canceled) { STXXL_VERBOSE_LINUXAIO("linuxaio_request[" << this << "] completed(" << posted << "," << canceled << ")"); if (!canceled) { if (m_type == READ) stats::get_instance()->read_finished(); else stats::get_instance()->write_finished(); } else if (posted) { if (m_type == READ) stats::get_instance()->read_canceled(m_bytes); else stats::get_instance()->write_canceled(m_bytes); } request_with_state::completed(canceled); } void linuxaio_request::fill_control_block() { linuxaio_file* af = dynamic_cast(m_file); memset(&cb, 0, sizeof(cb)); // indirection, so the I/O system retains a counting_ptr reference cb.aio_data = reinterpret_cast<__u64>(new request_ptr(this)); cb.aio_fildes = af->file_des; cb.aio_lio_opcode = (m_type == READ) ? IOCB_CMD_PREAD : IOCB_CMD_PWRITE; cb.aio_reqprio = 0; cb.aio_buf = static_cast<__u64>((unsigned long)(m_buffer)); cb.aio_nbytes = m_bytes; cb.aio_offset = m_offset; } //! Submits an I/O request to the OS //! \returns false if submission fails bool linuxaio_request::post() { STXXL_VERBOSE_LINUXAIO("linuxaio_request[" << this << "] post()"); fill_control_block(); iocb* cb_pointer = &cb; // io_submit might considerable time, so we have to remember the current // time before the call. double now = timestamp(); linuxaio_queue* queue = dynamic_cast( disk_queues::get_instance()->get_queue(m_file->get_queue_id()) ); long success = syscall(SYS_io_submit, queue->get_io_context(), 1, &cb_pointer); if (success == 1) { if (m_type == READ) stats::get_instance()->read_started(m_bytes, now); else stats::get_instance()->write_started(m_bytes, now); } else if (success == -1 && errno != EAGAIN) STXXL_THROW_ERRNO(io_error, "linuxaio_request::post" " io_submit()"); return success == 1; } //! Cancel the request //! //! Routine is called by user, as part of the request interface. bool linuxaio_request::cancel() { STXXL_VERBOSE_LINUXAIO("linuxaio_request[" << this << "] cancel()"); if (!m_file) return false; request_ptr req(this); linuxaio_queue* queue = dynamic_cast( disk_queues::get_instance()->get_queue(m_file->get_queue_id()) ); return queue->cancel_request(req); } //! Cancel already posted request bool linuxaio_request::cancel_aio() { STXXL_VERBOSE_LINUXAIO("linuxaio_request[" << this << "] cancel_aio()"); if (!m_file) return false; io_event event; linuxaio_queue* queue = dynamic_cast( disk_queues::get_instance()->get_queue(m_file->get_queue_id()) ); long result = syscall(SYS_io_cancel, queue->get_io_context(), &cb, &event); if (result == 0) //successfully canceled queue->handle_events(&event, 1, true); return result == 0; } STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_LINUXAIO_FILE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/request_with_waiters.cpp000644 001411 000144 00000003452 12405375303 020645 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/request_with_waiters.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE bool request_with_waiters::add_waiter(onoff_switch* sw) { // this lock needs to be obtained before poll(), otherwise a race // condition might occur: the state might change and notify_waiters() // could be called between poll() and insert() resulting in waiter sw // never being notified scoped_mutex_lock lock(m_waiters_mutex); if (poll()) // request already finished { return true; } m_waiters.insert(sw); return false; } void request_with_waiters::delete_waiter(onoff_switch* sw) { scoped_mutex_lock lock(m_waiters_mutex); m_waiters.erase(sw); } void request_with_waiters::notify_waiters() { scoped_mutex_lock lock(m_waiters_mutex); std::for_each(m_waiters.begin(), m_waiters.end(), std::mem_fun(&onoff_switch::on) _STXXL_FORCE_SEQUENTIAL); } size_t request_with_waiters::num_waiters() { scoped_mutex_lock lock(m_waiters_mutex); return m_waiters.size(); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/request_queue_impl_1q.cpp000644 001411 000144 00000011115 12405152405 020671 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/request_queue_impl_1q.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2005 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #if STXXL_STD_THREADS && STXXL_MSVC >= 1700 #include #endif #ifndef STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION #define STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION 1 #endif STXXL_BEGIN_NAMESPACE struct file_offset_match : public std::binary_function { bool operator () ( const request_ptr& a, const request_ptr& b) const { // matching file and offset are enough to cause problems return (a->get_offset() == b->get_offset()) && (a->get_file() == b->get_file()); } }; request_queue_impl_1q::request_queue_impl_1q(int n) : m_thread_state(NOT_RUNNING), m_sem(0) { STXXL_UNUSED(n); start_thread(worker, static_cast(this), m_thread, m_thread_state); } void request_queue_impl_1q::add_request(request_ptr& req) { if (req.empty()) STXXL_THROW_INVALID_ARGUMENT("Empty request submitted to disk_queue."); if (m_thread_state() != RUNNING) STXXL_THROW_INVALID_ARGUMENT("Request submitted to not running queue."); if (!dynamic_cast(req.get())) STXXL_ERRMSG("Incompatible request submitted to running queue."); #if STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION { scoped_mutex_lock Lock(m_queue_mutex); if (std::find_if(m_queue.begin(), m_queue.end(), bind2nd(file_offset_match(), req) _STXXL_FORCE_SEQUENTIAL) != m_queue.end()) { STXXL_ERRMSG("request submitted for a BID with a pending request"); } } #endif scoped_mutex_lock Lock(m_queue_mutex); m_queue.push_back(req); m_sem++; } bool request_queue_impl_1q::cancel_request(request_ptr& req) { if (req.empty()) STXXL_THROW_INVALID_ARGUMENT("Empty request canceled disk_queue."); if (m_thread_state() != RUNNING) STXXL_THROW_INVALID_ARGUMENT("Request canceled to not running queue."); if (!dynamic_cast(req.get())) STXXL_ERRMSG("Incompatible request submitted to running queue."); bool was_still_in_queue = false; { scoped_mutex_lock Lock(m_queue_mutex); queue_type::iterator pos = std::find(m_queue.begin(), m_queue.end(), req _STXXL_FORCE_SEQUENTIAL); if (pos != m_queue.end()) { m_queue.erase(pos); was_still_in_queue = true; m_sem--; } } return was_still_in_queue; } request_queue_impl_1q::~request_queue_impl_1q() { stop_thread(m_thread, m_thread_state, m_sem); } void* request_queue_impl_1q::worker(void* arg) { self* pthis = static_cast(arg); for ( ; ; ) { pthis->m_sem--; { scoped_mutex_lock Lock(pthis->m_queue_mutex); if (!pthis->m_queue.empty()) { request_ptr req = pthis->m_queue.front(); pthis->m_queue.pop_front(); Lock.unlock(); //assert(req->nref() > 1); dynamic_cast(req.get())->serve(); } else { Lock.unlock(); pthis->m_sem++; } } // terminate if it has been requested and queues are empty if (pthis->m_thread_state() == TERMINATING) { if ((pthis->m_sem--) == 0) break; else pthis->m_sem++; } } pthis->m_thread_state.set_to(TERMINATED); #if STXXL_STD_THREADS && STXXL_MSVC >= 1700 // Workaround for deadlock bug in Visual C++ Runtime 2012 and 2013, see // request_queue_impl_worker.cpp. -tb ExitThread(NULL); #else return NULL; #endif } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/request_with_state.cpp000644 001411 000144 00000005222 12405375303 020304 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/request_with_state.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2005, 2008 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE request_with_state::~request_with_state() { STXXL_VERBOSE3_THIS("request_with_state::~(), ref_cnt: " << get_reference_count()); assert(m_state() == DONE || m_state() == READY2DIE); // if(m_state() != DONE && m_state()!= READY2DIE ) // STXXL_ERRMSG("WARNING: serious stxxl inconsistency: Request is being deleted while I/O not finished. "<< // "Please submit a bug report."); // m_state.wait_for (READY2DIE); // does not make sense ? } void request_with_state::wait(bool measure_time) { STXXL_VERBOSE3_THIS("request_with_state::wait()"); stats::scoped_wait_timer wait_timer(m_type == READ ? stats::WAIT_OP_READ : stats::WAIT_OP_WRITE, measure_time); m_state.wait_for(READY2DIE); check_errors(); } bool request_with_state::cancel() { STXXL_VERBOSE3_THIS("request_with_state::cancel() " << m_file << " " << m_buffer << " " << m_offset); if (m_file) { request_ptr rp(this); if (disk_queues::get_instance()->cancel_request(rp, m_file->get_queue_id())) { m_state.set_to(DONE); notify_waiters(); m_file->delete_request_ref(); m_file = 0; m_state.set_to(READY2DIE); return true; } } return false; } bool request_with_state::poll() { const request_state s = m_state(); check_errors(); return s == DONE || s == READY2DIE; } void request_with_state::completed(bool canceled) { STXXL_VERBOSE3_THIS("request_with_state::completed()"); m_state.set_to(DONE); if (!canceled) m_on_complete(this); notify_waiters(); m_file->delete_request_ref(); m_file = 0; m_state.set_to(READY2DIE); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/syscall_file.cpp000644 001411 000144 00000007504 12411366426 017042 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/syscall_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2010 Andreas Beckmann * Copyright (C) 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include "ufs_platform.h" STXXL_BEGIN_NAMESPACE void syscall_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { scoped_mutex_lock fd_lock(fd_mutex); char* cbuffer = static_cast(buffer); stats::scoped_read_write_timer read_write_timer(bytes, type == request::WRITE); while (bytes > 0) { off_t rc = ::lseek(file_des, offset, SEEK_SET); if (rc < 0) { STXXL_THROW_ERRNO (io_error, " this=" << this << " call=::lseek(fd,offset,SEEK_SET)" << " path=" << filename << " fd=" << file_des << " offset=" << offset << " buffer=" << cbuffer << " bytes=" << bytes << " type=" << ((type == request::READ) ? "READ" : "WRITE") << " rc=" << rc); } if (type == request::READ) { #if STXXL_MSVC assert(bytes <= std::numeric_limits::max()); if ((rc = ::read(file_des, cbuffer, (unsigned int)bytes)) <= 0) #else if ((rc = ::read(file_des, cbuffer, bytes)) <= 0) #endif { STXXL_THROW_ERRNO (io_error, " this=" << this << " call=::read(fd,buffer,bytes)" << " path=" << filename << " fd=" << file_des << " offset=" << offset << " buffer=" << buffer << " bytes=" << bytes << " type=" << "READ" << " rc=" << rc); } bytes = (size_type)(bytes - rc); offset += rc; cbuffer += rc; if (bytes > 0 && offset == this->_size()) { // read request extends past end-of-file // fill reminder with zeroes memset(cbuffer, 0, bytes); bytes = 0; } } else { #if STXXL_MSVC assert(bytes <= std::numeric_limits::max()); if ((rc = ::write(file_des, cbuffer, (unsigned int)bytes)) <= 0) #else if ((rc = ::write(file_des, cbuffer, bytes)) <= 0) #endif { STXXL_THROW_ERRNO (io_error, " this=" << this << " call=::write(fd,buffer,bytes)" << " path=" << filename << " fd=" << file_des << " offset=" << offset << " buffer=" << buffer << " bytes=" << bytes << " type=" << "WRITE" << " rc=" << rc); } bytes = (size_type)(bytes - rc); offset += rc; cbuffer += rc; } } } const char* syscall_file::io_type() const { return "syscall"; } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/create_file.cpp000644 001411 000144 00000016730 12405375303 016631 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/create_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008, 2010 Andreas Beckmann * Copyright (C) 2008, 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE file * create_file(const std::string& io_impl, const std::string& filename, int options, int physical_device_id, int disk_allocator_id) { // construct temporary disk_config structure disk_config cfg(filename, 0, io_impl); cfg.queue = physical_device_id; cfg.direct = (options& file::REQUIRE_DIRECT) ? disk_config::DIRECT_ON : (options& file::DIRECT) ? disk_config::DIRECT_TRY : disk_config::DIRECT_OFF; return create_file(cfg, options, disk_allocator_id); } file * create_file(disk_config& cfg, int mode, int disk_allocator_id) { // apply disk_config settings to open mode mode &= ~(file::DIRECT | file::REQUIRE_DIRECT); // clear DIRECT and REQUIRE_DIRECT switch (cfg.direct) { case disk_config::DIRECT_OFF: break; case disk_config::DIRECT_TRY: mode |= file::DIRECT; break; case disk_config::DIRECT_ON: mode |= file::DIRECT | file::REQUIRE_DIRECT; break; } // automatically enumerate disks as separate device ids if (cfg.device_id == file::DEFAULT_DEVICE_ID) { cfg.device_id = config::get_instance()->get_next_device_id(); } else { config::get_instance()->update_max_device_id(cfg.device_id); } // *** Select fileio Implementation if (cfg.io_impl == "syscall") { ufs_file_base* result = new syscall_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); // if marked as device but file is not -> throw! if (cfg.raw_device && !result->is_device()) { delete result; STXXL_THROW(io_error, "Disk " << cfg.path << " was expected to be " "a raw block device, but it is a normal file!"); } // if is raw_device -> get size and remove some flags. if (result->is_device()) { cfg.raw_device = true; cfg.size = result->size(); cfg.autogrow = cfg.delete_on_exit = cfg.unlink_on_open = false; } if (cfg.unlink_on_open) result->unlink(); return result; } else if (cfg.io_impl == "fileperblock_syscall") { fileperblock_file* result = new fileperblock_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } else if (cfg.io_impl == "memory") { mem_file* result = new mem_file(cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } #if STXXL_HAVE_LINUXAIO_FILE // linuxaio can have the desired queue length, specified as queue_length=? else if (cfg.io_impl == "linuxaio") { // linuxaio_queue is a singleton. cfg.queue = file::DEFAULT_LINUXAIO_QUEUE; ufs_file_base* result = new linuxaio_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id, cfg.queue_length); result->lock(); // if marked as device but file is not -> throw! if (cfg.raw_device && !result->is_device()) { delete result; STXXL_THROW(io_error, "Disk " << cfg.path << " was expected to be " "a raw block device, but it is a normal file!"); } // if is raw_device -> get size and remove some flags. if (result->is_device()) { cfg.raw_device = true; cfg.size = result->size(); cfg.autogrow = cfg.delete_on_exit = cfg.unlink_on_open = false; } if (cfg.unlink_on_open) result->unlink(); return result; } #endif #if STXXL_HAVE_MMAP_FILE else if (cfg.io_impl == "mmap") { ufs_file_base* result = new mmap_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); if (cfg.unlink_on_open) result->unlink(); return result; } else if (cfg.io_impl == "fileperblock_mmap") { fileperblock_file* result = new fileperblock_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } #endif #if STXXL_HAVE_SIMDISK_FILE else if (cfg.io_impl == "simdisk") { mode &= ~(file::DIRECT | file::REQUIRE_DIRECT); // clear the DIRECT flag, this file is supposed to be on tmpfs ufs_file_base* result = new sim_disk_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } #endif #if STXXL_HAVE_WINCALL_FILE else if (cfg.io_impl == "wincall") { wfs_file_base* result = new wincall_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } else if (cfg.io_impl == "fileperblock_wincall") { fileperblock_file* result = new fileperblock_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } #endif #if STXXL_HAVE_BOOSTFD_FILE else if (cfg.io_impl == "boostfd") { boostfd_file* result = new boostfd_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } else if (cfg.io_impl == "fileperblock_boostfd") { fileperblock_file* result = new fileperblock_file(cfg.path, mode, cfg.queue, disk_allocator_id, cfg.device_id); result->lock(); return result; } #endif #if STXXL_HAVE_WBTL_FILE else if (cfg.io_impl == "wbtl") { ufs_file_base* backend = new syscall_file(cfg.path, mode, -1, -1); // FIXME: ID wbtl_file* result = new stxxl::wbtl_file(backend, 16 * 1024 * 1024, 2, cfg.queue, disk_allocator_id); result->lock(); if (cfg.unlink_on_open) backend->unlink(); return result; } #endif STXXL_THROW(std::runtime_error, "Unsupported disk I/O implementation '" << cfg.io_impl << "'."); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/wincall_file.cpp000644 001411 000144 00000007757 12405375303 017030 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/wincall_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005-2006 Roman Dementiev * Copyright (C) 2008-2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #if STXXL_HAVE_WINCALL_FILE #include #include #ifndef NOMINMAX #define NOMINMAX #endif #include STXXL_BEGIN_NAMESPACE void wincall_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { scoped_mutex_lock fd_lock(fd_mutex); if (bytes > 32 * 1024 * 1024) { STXXL_ERRMSG("Using a block size larger than 32 MiB may not work with the " << io_type() << " filetype"); } HANDLE handle = file_des; LARGE_INTEGER desired_pos; desired_pos.QuadPart = offset; if (!SetFilePointerEx(handle, desired_pos, NULL, FILE_BEGIN)) { STXXL_THROW_WIN_LASTERROR(io_error, "SetFilePointerEx in wincall_request::serve()" << " offset=" << offset << " this=" << this << " buffer=" << buffer << " bytes=" << bytes << " type=" << ((type == request::READ) ? "READ" : "WRITE")); } else { stats::scoped_read_write_timer read_write_timer(bytes, type == request::WRITE); if (type == request::READ) { DWORD NumberOfBytesRead = 0; assert(bytes <= std::numeric_limits::max()); if (!ReadFile(handle, buffer, (DWORD)bytes, &NumberOfBytesRead, NULL)) { STXXL_THROW_WIN_LASTERROR(io_error, "ReadFile" << " this=" << this << " offset=" << offset << " buffer=" << buffer << " bytes=" << bytes << " type=" << ((type == request::READ) ? "READ" : "WRITE") << " NumberOfBytesRead= " << NumberOfBytesRead); } else if (NumberOfBytesRead != bytes) { STXXL_THROW_WIN_LASTERROR(io_error, " partial read: missing " << (bytes - NumberOfBytesRead) << " out of " << bytes << " bytes"); } } else { DWORD NumberOfBytesWritten = 0; assert(bytes <= std::numeric_limits::max()); if (!WriteFile(handle, buffer, (DWORD)bytes, &NumberOfBytesWritten, NULL)) { STXXL_THROW_WIN_LASTERROR(io_error, "WriteFile" << " this=" << this << " offset=" << offset << " buffer=" << buffer << " bytes=" << bytes << " type=" << ((type == request::READ) ? "READ" : "WRITE") << " NumberOfBytesWritten= " << NumberOfBytesWritten); } else if (NumberOfBytesWritten != bytes) { STXXL_THROW_WIN_LASTERROR(io_error, " partial write: missing " << (bytes - NumberOfBytesWritten) << " out of " << bytes << " bytes"); } } } } const char* wincall_file::io_type() const { return "wincall"; } STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_WINCALL_FILE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/disk_queued_file.cpp000644 001411 000144 00000003043 12405375303 017661 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/disk_queued_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE request_ptr disk_queued_file::aread( void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl) { request_ptr req(new serving_request(on_cmpl, this, buffer, pos, bytes, request::READ)); disk_queues::get_instance()->add_request(req, get_queue_id()); return req; } request_ptr disk_queued_file::awrite( void* buffer, offset_type pos, size_type bytes, const completion_handler& on_cmpl) { request_ptr req(new serving_request(on_cmpl, this, buffer, pos, bytes, request::WRITE)); disk_queues::get_instance()->add_request(req, get_queue_id()); return req; } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/ufs_file_base.cpp000644 001411 000144 00000020076 12411366426 017156 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/ufs_file_base.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2005, 2008 Roman Dementiev * Copyright (C) 2008 Ilja Andronov * Copyright (C) 2008-2010 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include "ufs_platform.h" STXXL_BEGIN_NAMESPACE const char* ufs_file_base::io_type() const { return "ufs_base"; } ufs_file_base::ufs_file_base( const std::string& filename, int mode) : file_des(-1), m_mode(mode), filename(filename) { int flags = 0; if (mode & RDONLY) { flags |= O_RDONLY; } if (mode & WRONLY) { flags |= O_WRONLY; } if (mode & RDWR) { flags |= O_RDWR; } if (mode & CREAT) { flags |= O_CREAT; } if (mode & TRUNC) { flags |= O_TRUNC; } if ((mode & DIRECT) || (mode & REQUIRE_DIRECT)) { #ifdef __APPLE__ // no additional open flags are required for Mac OS X #elif !STXXL_DIRECT_IO_OFF flags |= O_DIRECT; #else if (mode & REQUIRE_DIRECT) { STXXL_ERRMSG("Error: open()ing " << filename << " with DIRECT mode required, but the system does not support it."); file_des = -1; return; } else { STXXL_MSG("Warning: open()ing " << filename << " without DIRECT mode, as the system does not support it."); } #endif } if (mode & SYNC) { flags |= O_RSYNC; flags |= O_DSYNC; flags |= O_SYNC; } #if STXXL_WINDOWS flags |= O_BINARY; // the default in MS is TEXT mode #endif #if STXXL_WINDOWS || defined(__MINGW32__) const int perms = S_IREAD | S_IWRITE; #else const int perms = S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP; #endif if ((file_des = ::open(filename.c_str(), flags, perms)) >= 0) { _after_open(); return; } #if !STXXL_DIRECT_IO_OFF if ((mode & DIRECT) && !(mode & REQUIRE_DIRECT) && errno == EINVAL) { STXXL_MSG("open() error on path=" << filename << " flags=" << flags << ", retrying without O_DIRECT."); flags &= ~O_DIRECT; m_mode &= ~DIRECT; if ((file_des = ::open(filename.c_str(), flags, perms)) >= 0) { _after_open(); return; } } #endif STXXL_THROW_ERRNO(io_error, "open() rc=" << file_des << " path=" << filename << " flags=" << flags); } ufs_file_base::~ufs_file_base() { close(); } void ufs_file_base::_after_open() { // stat file type #if STXXL_WINDOWS || defined(__MINGW32__) struct _stat64 st; STXXL_THROW_ERRNO_NE_0(::_fstat64(file_des, &st), io_error, "_fstat64() path=" << filename << " fd=" << file_des); #else struct stat st; STXXL_THROW_ERRNO_NE_0(::fstat(file_des, &st), io_error, "fstat() path=" << filename << " fd=" << file_des); #endif m_is_device = S_ISBLK(st.st_mode) ? true : false; #ifdef __APPLE__ if (m_mode & REQUIRE_DIRECT) { STXXL_THROW_ERRNO_NE_0(fcntl(file_des, F_NOCACHE, 1), io_error, "fcntl() path=" << filename << " fd=" << file_des); STXXL_THROW_ERRNO_NE_0(fcntl(file_des, F_RDAHEAD, 0), io_error, "fcntl() path=" << filename << " fd=" << file_des); } else if (m_mode & DIRECT) { if (fcntl(file_des, F_NOCACHE, 1) != 0) { STXXL_MSG("fcntl(fd,F_NOCACHE,1) failed on path=" << filename << " fd=" << file_des << " : " << strerror(errno)); } if (fcntl(file_des, F_RDAHEAD, 0) != 0) { STXXL_MSG("fcntl(fd,F_RDAHEAD,0) failed on path=" << filename << " fd=" << file_des << " : " << strerror(errno)); } } #endif // successfully opened file descriptor if (!(m_mode & NO_LOCK)) lock(); } void ufs_file_base::close() { scoped_mutex_lock fd_lock(fd_mutex); if (file_des == -1) return; if (::close(file_des) < 0) STXXL_THROW_ERRNO(io_error, "close() fd=" << file_des); file_des = -1; } void ufs_file_base::lock() { #if STXXL_WINDOWS || defined(__MINGW32__) // not yet implemented #else scoped_mutex_lock fd_lock(fd_mutex); struct flock lock_struct; lock_struct.l_type = (short)(m_mode & RDONLY ? F_RDLCK : F_RDLCK | F_WRLCK); lock_struct.l_whence = SEEK_SET; lock_struct.l_start = 0; lock_struct.l_len = 0; // lock all bytes if ((::fcntl(file_des, F_SETLK, &lock_struct)) < 0) STXXL_THROW_ERRNO(io_error, "fcntl(,F_SETLK,) path=" << filename << " fd=" << file_des); #endif } file::offset_type ufs_file_base::_size() { // We use lseek SEEK_END to find the file size. This works for raw devices // (where stat() returns zero), and we need not reset the position because // serve() always lseek()s before read/write. off_t rc = ::lseek(file_des, 0, SEEK_END); if (rc < 0) STXXL_THROW_ERRNO(io_error, "lseek(fd,0,SEEK_END) path=" << filename << " fd=" << file_des); // return value is already the total size return rc; } file::offset_type ufs_file_base::size() { scoped_mutex_lock fd_lock(fd_mutex); return _size(); } void ufs_file_base::set_size(offset_type newsize) { scoped_mutex_lock fd_lock(fd_mutex); return _set_size(newsize); } void ufs_file_base::_set_size(offset_type newsize) { offset_type cur_size = _size(); if (!(m_mode & RDONLY) && !m_is_device) { #if STXXL_WINDOWS || defined(__MINGW32__) HANDLE hfile = (HANDLE) ::_get_osfhandle(file_des); STXXL_THROW_ERRNO_NE_0((hfile == INVALID_HANDLE_VALUE), io_error, "_get_osfhandle() path=" << filename << " fd=" << file_des); LARGE_INTEGER desired_pos; desired_pos.QuadPart = newsize; if (!SetFilePointerEx(hfile, desired_pos, NULL, FILE_BEGIN)) STXXL_THROW_WIN_LASTERROR(io_error, "SetFilePointerEx in ufs_file_base::set_size(..) oldsize=" << cur_size << " newsize=" << newsize << " "); if (!SetEndOfFile(hfile)) STXXL_THROW_WIN_LASTERROR(io_error, "SetEndOfFile oldsize=" << cur_size << " newsize=" << newsize << " "); #else STXXL_THROW_ERRNO_NE_0(::ftruncate(file_des, newsize), io_error, "ftruncate() path=" << filename << " fd=" << file_des); #endif } #if !STXXL_WINDOWS if (newsize > cur_size) STXXL_THROW_ERRNO_LT_0(::lseek(file_des, newsize - 1, SEEK_SET), io_error, "lseek() path=" << filename << " fd=" << file_des << " pos=" << newsize - 1); #endif } void ufs_file_base::close_remove() { close(); if (m_is_device) { STXXL_ERRMSG("remove() path=" << filename << " skipped as file is device node"); return; } if (::remove(filename.c_str()) != 0) STXXL_ERRMSG("remove() error on path=" << filename << " error=" << strerror(errno)); } void ufs_file_base::unlink() { if (m_is_device) { STXXL_ERRMSG("unlink() path=" << filename << " skipped as file is device node"); return; } if (::unlink(filename.c_str()) != 0) STXXL_THROW_ERRNO(io_error, "unlink() path=" << filename << " fd=" << file_des); } bool ufs_file_base::is_device() const { return m_is_device; } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/simdisk_file.cpp000644 001411 000144 00000017010 12411366426 017024 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/simdisk_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2003 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #if STXXL_HAVE_SIMDISK_FILE #include #include #include "ufs_platform.h" #include STXXL_BEGIN_NAMESPACE const double simdisk_geometry::s_average_speed = (15 * 1024 * 1024); void simdisk_geometry::add_zone(int& first_cyl, int last_cyl, int sec_per_track, int& first_sect) { double rate = nsurfaces * sec_per_track * bytes_per_sector / ((nsurfaces - 1) * head_switch_time + cyl_switch_time + nsurfaces * revolution_time); int sectors = (last_cyl - first_cyl + 1) * nsurfaces * sec_per_track; zones.insert(Zone(first_sect, sectors, rate)); first_sect += sectors; first_cyl = last_cyl + 1; } // returns delay in s double simdisk_geometry::get_delay(file::offset_type offset, file::size_type size) { #if 0 int first_sect = offset / bytes_per_sector; int last_sect = (offset + size) / bytes_per_sector; int sectors = size / bytes_per_sector; double delay = cmd_ovh + seek_time + rot_latency + double(bytes_per_sector) / double(interface_speed); std::set::iterator zone = zones.lower_bound(first_sect); //std::cout << __FUNCTION__ << " " << (*zone).first_sector << std::endl; while (1) { int from_this_zone = last_sect - ((*zone).first_sector + (*zone).sectors); if (from_this_zone <= 0) { delay += sectors * bytes_per_sector / ((*zone).sustained_data_rate); break; } else { delay += from_this_zone * bytes_per_sector / ((*zone).sustained_data_rate); zone++; stxxl_nassert(zone == zones.end()); sectors -= from_this_zone; } } return delay; #else STXXL_UNUSED(offset); return double(size) / s_average_speed; #endif } IC35L080AVVA07::IC35L080AVVA07() { std::cout << "Creating IBM 120GXP IC35L080AVVA07" << std::endl; nsurfaces = 4; bytes_per_sector = 512; cmd_ovh = 0.0002; // in s seek_time = 0.0082; // in s rot_latency = 0.00417; // in s head_switch_time = 0.0015; // in s cyl_switch_time = 0.002; // in s revolution_time = 0.0083; // in s interface_speed = 100000000; // in byte/s int first_sect = 0; int last_cyl = 0; add_zone(last_cyl, 1938, 928, first_sect); add_zone(last_cyl, 3756, 921, first_sect); add_zone(last_cyl, 5564, 896, first_sect); add_zone(last_cyl, 7687, 896, first_sect); add_zone(last_cyl, 9526, 888, first_sect); add_zone(last_cyl, 11334, 883, first_sect); add_zone(last_cyl, 13331, 864, first_sect); add_zone(last_cyl, 15128, 850, first_sect); add_zone(last_cyl, 16925, 840, first_sect); add_zone(last_cyl, 18922, 822, first_sect); add_zone(last_cyl, 20709, 806, first_sect); add_zone(last_cyl, 22601, 792, first_sect); add_zone(last_cyl, 24138, 787, first_sect); add_zone(last_cyl, 26024, 768, first_sect); add_zone(last_cyl, 27652, 752, first_sect); add_zone(last_cyl, 29501, 740, first_sect); add_zone(last_cyl, 31234, 725, first_sect); add_zone(last_cyl, 33009, 698, first_sect); add_zone(last_cyl, 34784, 691, first_sect); add_zone(last_cyl, 36609, 672, first_sect); add_zone(last_cyl, 38374, 648, first_sect); add_zone(last_cyl, 40139, 630, first_sect); add_zone(last_cyl, 41904, 614, first_sect); add_zone(last_cyl, 43519, 595, first_sect); add_zone(last_cyl, 45250, 576, first_sect); add_zone(last_cyl, 47004, 552, first_sect); add_zone(last_cyl, 48758, 533, first_sect); add_zone(last_cyl, 50491, 512, first_sect); add_zone(last_cyl, 52256, 493, first_sect); add_zone(last_cyl, 54010, 471, first_sect); add_zone(last_cyl, 55571, 448, first_sect); #if 0 set::iterator it = zones.begin(); int i = 0; for ( ; it != zones.end(); it++, i++) { //const int block_size = 128*3*1024* 4; // one cylinder std::cout << "Zone " << i << " first sector: " << (*it).first_sector; std::cout << " sectors: " << (*it).sectors << " sustained rate: "; std::cout << (*it).sustained_data_rate / 1024 / 1024 << " MiB/s" << std::endl; } std::cout << "Last sector : " << first_sect << std::endl; std::cout << "Approx. capacity: " << (first_sect / 1024 / 1024) * bytes_per_sector << " MiB" << std::endl; #endif std::cout << "Transfer 16 MiB from zone 0 : " << get_delay(0, 16 * 1024 * 1024) << " s" << std::endl; std::cout << "Transfer 16 MiB from zone 30: " << get_delay(file::offset_type(158204036) * file::offset_type(bytes_per_sector), 16 * 1024 * 1024) << " s" << std::endl; } //////////////////////////////////////////////////////////////////////////// void sim_disk_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { scoped_mutex_lock fd_lock(fd_mutex); double op_start = timestamp(); stats::scoped_read_write_timer read_write_timer(bytes, type == request::WRITE); void* mem = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_SHARED, file_des, offset); if (mem == MAP_FAILED) { STXXL_THROW_ERRNO (io_error, " mmap() failed." << " Page size: " << sysconf(_SC_PAGESIZE) << " offset modulo page size " << (offset % sysconf(_SC_PAGESIZE))); } else if (mem == 0) { STXXL_THROW_ERRNO(io_error, "mmap() returned NULL"); } else { if (type == request::READ) { memcpy(buffer, mem, bytes); } else { memcpy(mem, buffer, bytes); } STXXL_THROW_ERRNO_NE_0(munmap(mem, bytes), io_error, "munmap() failed"); } double delay = get_delay(offset, bytes); delay = delay - timestamp() + op_start; assert(delay > 0.0); int seconds_to_wait = static_cast(floor(delay)); if (seconds_to_wait) sleep(seconds_to_wait); usleep((useconds_t)((delay - seconds_to_wait) * 1000000.)); } const char* sim_disk_file::io_type() const { return "simdisk"; } //////////////////////////////////////////////////////////////////////////// void sim_disk_file::set_size(offset_type newsize) { scoped_mutex_lock fd_lock(fd_mutex); if (newsize > _size()) { STXXL_THROW_ERRNO_LT_0(::lseek(file_des, newsize - 1, SEEK_SET), io_error, "lseek() fd=" << file_des << " pos=" << newsize - 1); STXXL_THROW_ERRNO_LT_0(::write(file_des, "", 1), io_error, "write() fd=" << file_des << " size=1"); } } STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_SIMDISK_FILE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/serving_request.cpp000644 001411 000144 00000004021 12411366426 017605 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/serving_request.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE serving_request::serving_request( const completion_handler& on_cmpl, file* f, void* buf, offset_type off, size_type b, request_type t) : request_with_state(on_cmpl, f, buf, off, b, t) { #ifdef STXXL_CHECK_BLOCK_ALIGNING // Direct I/O requires file system block size alignment for file offsets, // memory buffer addresses, and transfer(buffer) size must be multiple // of the file system block size check_alignment(); #endif } void serving_request::serve() { check_nref(); STXXL_VERBOSE2_THIS( "serving_request::serve(): " << m_buffer << " @ [" << m_file << "|" << m_file->get_allocator_id() << "]0x" << std::hex << std::setfill('0') << std::setw(8) << m_offset << "/0x" << m_bytes << ((m_type == request::READ) ? " READ" : " WRITE")); try { m_file->serve(m_buffer, m_offset, m_bytes, m_type); } catch (const io_error& ex) { error_occured(ex.what()); } check_nref(true); completed(false); } const char* serving_request::io_type() const { return m_file->io_type(); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/wbtl_file.cpp000644 001411 000144 00000030634 12411366426 016340 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/wbtl_file.cpp * * a write-buffered-translation-layer pseudo file * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008-2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #if STXXL_HAVE_WBTL_FILE #include #include #include #include #include #ifndef STXXL_VERBOSE_WBTL #define STXXL_VERBOSE_WBTL STXXL_VERBOSE2 #endif STXXL_BEGIN_NAMESPACE wbtl_file::wbtl_file( file* backend_file, size_type write_buffer_size, int write_buffers, int queue_id, int allocator_id) : disk_queued_file(queue_id, allocator_id), storage(backend_file), sz(0), write_block_size(write_buffer_size), free_bytes(0), curbuf(1), curpos(write_block_size) { STXXL_UNUSED(write_buffers); assert(write_buffers == 2); // currently hardcoded write_buffer[0] = static_cast(stxxl::aligned_alloc(write_block_size)); write_buffer[1] = static_cast(stxxl::aligned_alloc(write_block_size)); buffer_address[0] = offset_type(-1); buffer_address[1] = offset_type(-1); } wbtl_file::~wbtl_file() { stxxl::aligned_dealloc(write_buffer[1]); stxxl::aligned_dealloc(write_buffer[0]); delete storage; storage = 0; } void wbtl_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { if (type == request::READ) { //stats::scoped_read_timer read_timer(size()); sread(buffer, offset, bytes); } else { //stats::scoped_write_timer write_timer(size()); swrite(buffer, offset, bytes); } } void wbtl_file::lock() { storage->lock(); } wbtl_file::offset_type wbtl_file::size() { return sz; } void wbtl_file::set_size(offset_type newsize) { scoped_mutex_lock mapping_lock(mapping_mutex); assert(sz <= newsize); // may not shrink if (sz < newsize) { _add_free_region(sz, newsize - sz); storage->set_size(newsize); sz = newsize; assert(sz == storage->size()); } } #define FMT_A_S(_addr_, _size_) "0x" << std::hex << std::setfill('0') << std::setw(8) << (_addr_) << "/0x" << std::setw(8) << (_size_) #define FMT_A_C(_addr_, _size_) "0x" << std::setw(8) << (_addr_) << "(" << std::dec << (_size_) << ")" #define FMT_A(_addr_) "0x" << std::setw(8) << (_addr_) // logical address void wbtl_file::discard(offset_type offset, offset_type size) { scoped_mutex_lock mapping_lock(mapping_mutex); sortseq::iterator physical = address_mapping.find(offset); STXXL_VERBOSE_WBTL("wbtl:discard l" << FMT_A_S(offset, size) << " @ p" << FMT_A(physical != address_mapping.end() ? physical->second : 0xffffffff)); if (physical == address_mapping.end()) { // could be OK if the block was never written ... //STXXL_ERRMSG("discard: mapping not found: " << FMT_A_S(offset, size) << " ==> " << "???"); } else { offset_type physical_offset = physical->second; address_mapping.erase(physical); _add_free_region(physical_offset, size); place_map::iterator reverse = reverse_mapping.find(physical_offset); if (reverse == reverse_mapping.end()) { STXXL_ERRMSG("discard: reverse mapping not found: " << FMT_A_S(offset, size) << " ==> " << "???"); } else { assert(offset == (reverse->second).first); reverse_mapping.erase(reverse); } storage->discard(physical_offset, size); } } // physical address void wbtl_file::_add_free_region(offset_type offset, offset_type size) { // mapping_lock has to be aquired by caller STXXL_VERBOSE_WBTL("wbtl:addfre p" << FMT_A_S(offset, size) << " F <= f" << FMT_A_C(free_bytes, free_space.size())); offset_type region_pos = offset; offset_type region_size = size; if (!free_space.empty()) { sortseq::iterator succ = free_space.upper_bound(region_pos); sortseq::iterator pred = succ; pred--; check_corruption(region_pos, region_size, pred, succ); if (succ == free_space.end()) { if (pred == free_space.end()) { //dump(); assert(pred != free_space.end()); } if ((*pred).first + (*pred).second == region_pos) { // coalesce with predecessor region_size += (*pred).second; region_pos = (*pred).first; free_space.erase(pred); } } else { if (free_space.size() > 1) { bool succ_is_not_the_first = (succ != free_space.begin()); if ((*succ).first == region_pos + region_size) { // coalesce with successor region_size += (*succ).second; free_space.erase(succ); } if (succ_is_not_the_first) { if (pred == free_space.end()) { //dump(); assert(pred != free_space.end()); } if ((*pred).first + (*pred).second == region_pos) { // coalesce with predecessor region_size += (*pred).second; region_pos = (*pred).first; free_space.erase(pred); } } } else { if ((*succ).first == region_pos + region_size) { // coalesce with successor region_size += (*succ).second; free_space.erase(succ); } } } } free_space[region_pos] = region_size; free_bytes += size; STXXL_VERBOSE_WBTL("wbtl:free p" << FMT_A_S(region_pos, region_size) << " F => f" << FMT_A_C(free_bytes, free_space.size())); } void wbtl_file::sread(void* buffer, offset_type offset, size_type bytes) { scoped_mutex_lock buffer_lock(buffer_mutex); int cached = -1; offset_type physical_offset; // map logical to physical address { scoped_mutex_lock mapping_lock(mapping_mutex); sortseq::iterator physical = address_mapping.find(offset); if (physical == address_mapping.end()) { STXXL_ERRMSG("wbtl_read: mapping not found: " << FMT_A_S(offset, bytes) << " ==> " << "???"); //STXXL_THROW_ERRNO(io_error, "wbtl_read of unmapped memory"); physical_offset = 0xffffffff; } else { physical_offset = physical->second; } } if (buffer_address[curbuf] <= physical_offset && physical_offset < buffer_address[curbuf] + write_block_size) { // block is in current write buffer assert(physical_offset + bytes <= buffer_address[curbuf] + write_block_size); memcpy(buffer, write_buffer[curbuf] + (physical_offset - buffer_address[curbuf]), bytes); stats::get_instance()->read_cached(bytes); cached = curbuf; } else if (buffer_address[1 - curbuf] <= physical_offset && physical_offset < buffer_address[1 - curbuf] + write_block_size) { // block is in previous write buffer assert(physical_offset + bytes <= buffer_address[1 - curbuf] + write_block_size); memcpy(buffer, write_buffer[1 - curbuf] + (physical_offset - buffer_address[1 - curbuf]), bytes); stats::get_instance()->read_cached(bytes); cached = curbuf; } else if (physical_offset == 0xffffffff) { // block was deleted or never written before char* uninitialized = (char*)malloc(sizeof(char)); memset(buffer, *uninitialized, bytes); free(uninitialized); } else { // block is not cached request_ptr req = storage->aread(buffer, physical_offset, bytes); req->wait(false); } STXXL_VERBOSE_WBTL("wbtl:sread l" << FMT_A_S(offset, bytes) << " @ p" << FMT_A(physical_offset) << " " << std::dec << cached); STXXL_UNUSED(cached); } void wbtl_file::swrite(void* buffer, offset_type offset, size_type bytes) { scoped_mutex_lock buffer_lock(buffer_mutex); // is the block already mapped? { scoped_mutex_lock mapping_lock(mapping_mutex); sortseq::iterator physical = address_mapping.find(offset); STXXL_VERBOSE_WBTL("wbtl:swrite l" << FMT_A_S(offset, bytes) << " @ <= p" << FMT_A_C(physical != address_mapping.end() ? physical->second : 0xffffffff, address_mapping.size())); if (physical != address_mapping.end()) { mapping_lock.unlock(); // FIXME: special case if we can replace it in the current writing block discard(offset, bytes); } } if (bytes > write_block_size - curpos) { // not enough space in the current write buffer if (buffer_address[curbuf] != offset_type(-1)) { STXXL_VERBOSE_WBTL("wbtl:w2disk p" << FMT_A_S(buffer_address[curbuf], write_block_size)); // mark remaining part as free if (curpos < write_block_size) _add_free_region(buffer_address[curbuf] + curpos, write_block_size - curpos); if (backend_request.get()) { backend_request->wait(false); } backend_request = storage->awrite(write_buffer[curbuf], buffer_address[curbuf], write_block_size); } curbuf = 1 - curbuf; buffer_address[curbuf] = get_next_write_block(); curpos = 0; } assert(bytes <= write_block_size - curpos); // write block into buffer memcpy(write_buffer[curbuf] + curpos, buffer, bytes); stats::get_instance()->write_cached(bytes); scoped_mutex_lock mapping_lock(mapping_mutex); address_mapping[offset] = buffer_address[curbuf] + curpos; reverse_mapping[buffer_address[curbuf] + curpos] = place(offset, bytes); STXXL_VERBOSE_WBTL("wbtl:swrite l" << FMT_A_S(offset, bytes) << " @ => p" << FMT_A_C(buffer_address[curbuf] + curpos, address_mapping.size())); curpos += bytes; } wbtl_file::offset_type wbtl_file::get_next_write_block() { // mapping_lock has to be aquired by caller sortseq::iterator space = std::find_if(free_space.begin(), free_space.end(), bind2nd(FirstFit(), write_block_size) _STXXL_FORCE_SEQUENTIAL); if (space != free_space.end()) { offset_type region_pos = (*space).first; offset_type region_size = (*space).second; free_space.erase(space); if (region_size > write_block_size) free_space[region_pos + write_block_size] = region_size - write_block_size; free_bytes -= write_block_size; STXXL_VERBOSE_WBTL("wbtl:nextwb p" << FMT_A_S(region_pos, write_block_size) << " F f" << FMT_A_C(free_bytes, free_space.size())); return region_pos; } STXXL_THROW_ERRNO(io_error, "OutOfSpace, probably fragmented"); } void wbtl_file::check_corruption(offset_type region_pos, offset_type region_size, sortseq::iterator pred, sortseq::iterator succ) { if (pred != free_space.end()) { if (pred->first <= region_pos && pred->first + pred->second > region_pos) { STXXL_THROW(bad_ext_alloc, "Error: double deallocation of external memory " << "System info: P " << pred->first << " " << pred->second << " " << region_pos); } } if (succ != free_space.end()) { if (region_pos <= succ->first && region_pos + region_size > succ->first) { STXXL_THROW(bad_ext_alloc, "Error: double deallocation of external memory " << "System info: S " << region_pos << " " << region_size << " " << succ->first); } } } const char* wbtl_file::io_type() const { return "wbtl"; } STXXL_END_NAMESPACE #endif // #if STXXL_HAVE_WBTL_FILE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/request_queue_impl_worker.cpp000644 001411 000144 00000005261 12405375303 021672 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/request_queue_impl_worker.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2005 Roman Dementiev * Copyright (C) 2008, 2009 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #include #include #if STXXL_BOOST_THREADS #include #endif #if STXXL_STD_THREADS && STXXL_MSVC >= 1700 #include #endif STXXL_BEGIN_NAMESPACE void request_queue_impl_worker::start_thread(void* (* worker)(void*), void* arg, thread_type& t, state& s) { assert(s() == NOT_RUNNING); #if STXXL_STD_THREADS t = new std::thread(worker, arg); #elif STXXL_BOOST_THREADS t = new boost::thread(boost::bind(worker, arg)); #else STXXL_CHECK_PTHREAD_CALL(pthread_create(&t, NULL, worker, arg)); #endif s.set_to(RUNNING); } void request_queue_impl_worker::stop_thread(thread_type& t, state& s, semaphore& sem) { assert(s() == RUNNING); s.set_to(TERMINATING); sem++; #if STXXL_STD_THREADS #if STXXL_MSVC >= 1700 // In the Visual C++ Runtime 2012 and 2013, there is a deadlock bug, which // occurs when threads are joined after main() exits. Apparently, Microsoft // thinks this is not a big issue. It has not been fixed in VC++RT 2013. // https://connect.microsoft.com/VisualStudio/feedback/details/747145 // // All STXXL threads are created by singletons, which are global variables // that are deleted after main() exits. The fix applied here it to use // std::thread::native_handle() and access the WINAPI to terminate the // thread directly (after it finished handling its i/o requests). WaitForSingleObject(t->native_handle(), INFINITE); CloseHandle(t->native_handle()); #else t->join(); delete t; #endif t = NULL; #elif STXXL_BOOST_THREADS t->join(); delete t; t = NULL; #else STXXL_CHECK_PTHREAD_CALL(pthread_join(t, NULL)); #endif assert(s() == TERMINATED); s.set_to(NOT_RUNNING); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/mmap_file.cpp000644 001411 000144 00000004336 12405375303 016317 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/mmap_file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002 Roman Dementiev * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #if STXXL_HAVE_MMAP_FILE #include #include #include "ufs_platform.h" #include STXXL_BEGIN_NAMESPACE void mmap_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { scoped_mutex_lock fd_lock(fd_mutex); //assert(offset + bytes <= _size()); stats::scoped_read_write_timer read_write_timer(bytes, type == request::WRITE); int prot = (type == request::READ) ? PROT_READ : PROT_WRITE; void* mem = mmap(NULL, bytes, prot, MAP_SHARED, file_des, offset); // void *mem = mmap (buffer, bytes, prot , MAP_SHARED|MAP_FIXED , file_des, offset); // STXXL_MSG("Mmaped to "< * Copyright (C) 2013-2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include STXXL_BEGIN_NAMESPACE void mem_file::serve(void* buffer, offset_type offset, size_type bytes, request::request_type type) { scoped_mutex_lock lock(m_mutex); if (type == request::READ) { stats::scoped_read_timer read_timer(bytes); memcpy(buffer, m_ptr + offset, bytes); } else { stats::scoped_write_timer write_timer(bytes); memcpy(m_ptr + offset, buffer, bytes); } } const char* mem_file::io_type() const { return "memory"; } mem_file::~mem_file() { free(m_ptr); m_ptr = NULL; } void mem_file::lock() { // nothing to do } file::offset_type mem_file::size() { return m_size; } void mem_file::set_size(offset_type newsize) { scoped_mutex_lock lock(m_mutex); assert(newsize <= std::numeric_limits::max()); m_ptr = (char*)realloc(m_ptr, (size_t)newsize); m_size = newsize; } void mem_file::discard(offset_type offset, offset_type size) { scoped_mutex_lock lock(m_mutex); #ifndef STXXL_MEMFILE_DONT_CLEAR_FREED_MEMORY // overwrite the freed region with uninitialized memory STXXL_VERBOSE("discard at " << offset << " len " << size); void* uninitialized = malloc(STXXL_BLOCK_ALIGN); while (size >= STXXL_BLOCK_ALIGN) { memcpy(m_ptr + offset, uninitialized, STXXL_BLOCK_ALIGN); offset += STXXL_BLOCK_ALIGN; size -= STXXL_BLOCK_ALIGN; } assert(size <= std::numeric_limits::max()); if (size > 0) memcpy(m_ptr + offset, uninitialized, (size_t)size); free(uninitialized); #else STXXL_UNUSED(offset); STXXL_UNUSED(size); #endif } STXXL_END_NAMESPACE stxxl-1.4.1/lib/io/file.cpp000644 001411 000144 00000001157 12405152405 015277 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/file.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include "ufs_platform.h" STXXL_BEGIN_NAMESPACE int file::unlink(const char* path) { return ::unlink(path); } STXXL_END_NAMESPACE stxxl-1.4.1/lib/io/request.cpp000644 001411 000144 00000005533 12410750556 016061 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/request.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include STXXL_BEGIN_NAMESPACE request::request( const completion_handler& on_compl, file* file, void* buffer, offset_type offset, size_type bytes, request_type type) : m_on_complete(on_compl), m_file(file), m_buffer(buffer), m_offset(offset), m_bytes(bytes), m_type(type) { STXXL_VERBOSE3_THIS("request::(...), ref_cnt=" << get_reference_count()); m_file->add_request_ref(); } request::~request() { STXXL_VERBOSE3_THIS("request::~request(), ref_cnt=" << get_reference_count()); } void request::check_alignment() const { if (m_offset % STXXL_BLOCK_ALIGN != 0) STXXL_ERRMSG("Offset is not aligned: modulo " << STXXL_BLOCK_ALIGN << " = " << m_offset % STXXL_BLOCK_ALIGN); if (m_bytes % STXXL_BLOCK_ALIGN != 0) STXXL_ERRMSG("Size is not a multiple of " << STXXL_BLOCK_ALIGN << ", = " << m_bytes % STXXL_BLOCK_ALIGN); if (unsigned_type(m_buffer) % STXXL_BLOCK_ALIGN != 0) STXXL_ERRMSG("Buffer is not aligned: modulo " << STXXL_BLOCK_ALIGN << " = " << unsigned_type(m_buffer) % STXXL_BLOCK_ALIGN << " (" << m_buffer << ")"); } void request::check_nref_failed(bool after) { STXXL_ERRMSG("WARNING: serious error, reference to the request is lost " << (after ? "after" : "before") << " serve()" << " nref=" << get_reference_count() << " this=" << this << " offset=" << m_offset << " buffer=" << m_buffer << " bytes=" << m_bytes << " type=" << ((m_type == READ) ? "READ" : "WRITE") << " file=" << m_file << " iotype=" << m_file->io_type() ); } const char* request::io_type() const { return m_file->io_type(); } std::ostream& request::print(std::ostream& out) const { out << "File object address: " << static_cast(m_file); out << " Buffer address: " << static_cast(m_buffer); out << " File offset: " << m_offset; out << " Transfer size: " << m_bytes << " bytes"; out << " Type of transfer: " << ((m_type == READ) ? "READ" : "WRITE"); return out; } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/io/ufs_platform.h000644 001411 000144 00000003651 12405152405 016527 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/ufs_platform.h * * Platform porting code local the I/O file implementations. This header is * not part of STXXL's template library interface and must only be used inside * libstxxl. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #ifndef STXXL_IO_UFS_PLATFORM_HEADER #define STXXL_IO_UFS_PLATFORM_HEADER #if STXXL_WINDOWS || defined(__MINGW32__) #ifndef NOMINMAX #define NOMINMAX #endif #include // this is not stxxl/bits/io/io.h ! #include #else #include #endif // these exist on Windows and Unixs #include #include #include // required for ::remove() #include // for systems that don't know anything about block devices. #ifndef S_ISBLK #define S_ISBLK(x) 0 #endif // for systems with missing flags #ifndef O_SYNC #define O_SYNC 0 #endif #ifndef O_RSYNC #define O_RSYNC 0 #endif #ifndef O_DSYNC #define O_DSYNC 0 #endif #if defined (__linux__) #if !defined(O_DIRECT) #error O_DIRECT is not defined while __linux__ is - PLEASE REPORT THIS BUG #endif // FIXME: In which conditions is this not defined? Why only i386 and alpha? Why not amd64? #if !defined (O_DIRECT) && (defined (__alpha__) || defined (__i386__)) #define O_DIRECT 040000 /* direct disk access */ #endif #endif #ifndef O_DIRECT #define O_DIRECT O_SYNC #endif // use 64-bit functions on Windows #if STXXL_WINDOWS #ifndef lseek #define lseek _lseeki64 #endif #ifndef off_t #define off_t int64 #endif #endif #endif // !STXXL_IO_UFS_PLATFORM_HEADER stxxl-1.4.1/lib/io/wfs_file_base.cpp000644 001411 000144 00000015101 12405375303 017146 0ustar00tbusers000000 000000 /*************************************************************************** * lib/io/wfs_file_base.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2005 Roman Dementiev * Copyright (C) 2008, 2010 Andreas Beckmann * Copyright (C) 2009, 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #if STXXL_WINDOWS #ifndef NOMINMAX #define NOMINMAX #endif #include STXXL_BEGIN_NAMESPACE const char* wfs_file_base::io_type() const { return "wfs_base"; } static HANDLE open_file_impl(const std::string& filename, int mode) { DWORD dwDesiredAccess = 0; DWORD dwShareMode = 0; DWORD dwCreationDisposition = 0; DWORD dwFlagsAndAttributes = 0; if (mode & file::RDONLY) { dwFlagsAndAttributes |= FILE_ATTRIBUTE_READONLY; dwDesiredAccess |= GENERIC_READ; } if (mode & file::WRONLY) { dwDesiredAccess |= GENERIC_WRITE; } if (mode & file::RDWR) { dwDesiredAccess |= (GENERIC_READ | GENERIC_WRITE); } if (mode & file::CREAT) { // ignored } if (mode & file::TRUNC) { dwCreationDisposition |= TRUNCATE_EXISTING; } else { dwCreationDisposition |= OPEN_ALWAYS; } if (mode & file::DIRECT) { #if !STXXL_DIRECT_IO_OFF dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING; // TODO: try also FILE_FLAG_WRITE_THROUGH option ? #else if (mode & file::REQUIRE_DIRECT) { STXXL_ERRMSG("Error: open()ing " << filename << " with DIRECT mode required, but the system does not support it."); return INVALID_HANDLE_VALUE; } else { STXXL_MSG("Warning: open()ing " << filename << " without DIRECT mode, as the system does not support it."); } #endif } if (mode & file::SYNC) { // ignored } HANDLE file_des = ::CreateFile(filename.c_str(), dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); if (file_des != INVALID_HANDLE_VALUE) return file_des; #if !STXXL_DIRECT_IO_OFF if ((mode& file::DIRECT) && !(mode & file::REQUIRE_DIRECT)) { STXXL_MSG("CreateFile() error on path=" << filename << " mode=" << mode << ", retrying without DIRECT mode."); dwFlagsAndAttributes &= ~FILE_FLAG_NO_BUFFERING; HANDLE file_des = ::CreateFile(filename.c_str(), dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); if (file_des != INVALID_HANDLE_VALUE) return file_des; } #endif STXXL_THROW_WIN_LASTERROR(io_error, "CreateFile() path=" << filename << " mode=" << mode); } wfs_file_base::wfs_file_base( const std::string& filename, int mode) : file_des(INVALID_HANDLE_VALUE), mode_(mode), filename(filename), locked(false) { file_des = open_file_impl(filename, mode); if (!(mode & NO_LOCK)) { lock(); } if (!(mode_ & RDONLY) && (mode & DIRECT)) { char buf[32768], * part; if (!GetFullPathName(filename.c_str(), sizeof(buf), buf, &part)) { STXXL_ERRMSG("wfs_file_base::wfs_file_base(): GetFullPathName() error for file " << filename); bytes_per_sector = 512; } else { part[0] = char(); DWORD bytes_per_sector_; if (!GetDiskFreeSpace(buf, NULL, &bytes_per_sector_, NULL, NULL)) { STXXL_ERRMSG("wfs_file_base::wfs_file_base(): GetDiskFreeSpace() error for path " << buf); bytes_per_sector = 512; } else bytes_per_sector = bytes_per_sector_; } } } wfs_file_base::~wfs_file_base() { close(); } void wfs_file_base::close() { scoped_mutex_lock fd_lock(fd_mutex); if (file_des == INVALID_HANDLE_VALUE) return; if (!CloseHandle(file_des)) STXXL_THROW_WIN_LASTERROR(io_error, "CloseHandle() of file fd=" << file_des); file_des = INVALID_HANDLE_VALUE; } void wfs_file_base::lock() { scoped_mutex_lock fd_lock(fd_mutex); if (locked) return; // already locked if (LockFile(file_des, 0, 0, 0xffffffff, 0xffffffff) == 0) STXXL_THROW_WIN_LASTERROR(io_error, "LockFile() fd=" << file_des); locked = true; } file::offset_type wfs_file_base::_size() { LARGE_INTEGER result; if (!GetFileSizeEx(file_des, &result)) STXXL_THROW_WIN_LASTERROR(io_error, "GetFileSizeEx() fd=" << file_des); return result.QuadPart; } file::offset_type wfs_file_base::size() { scoped_mutex_lock fd_lock(fd_mutex); return _size(); } void wfs_file_base::set_size(offset_type newsize) { scoped_mutex_lock fd_lock(fd_mutex); offset_type cur_size = _size(); if (!(mode_ & RDONLY)) { LARGE_INTEGER desired_pos; desired_pos.QuadPart = newsize; bool direct_with_bad_size = (mode_& file::DIRECT) && (newsize % bytes_per_sector); if (direct_with_bad_size) { if (!CloseHandle(file_des)) STXXL_THROW_WIN_LASTERROR(io_error, "closing file (call of ::CloseHandle() from set_size) "); file_des = INVALID_HANDLE_VALUE; file_des = open_file_impl(filename, WRONLY); } if (!SetFilePointerEx(file_des, desired_pos, NULL, FILE_BEGIN)) STXXL_THROW_WIN_LASTERROR(io_error, "SetFilePointerEx() in wfs_file_base::set_size(..) oldsize=" << cur_size << " newsize=" << newsize << " "); if (!SetEndOfFile(file_des)) STXXL_THROW_WIN_LASTERROR(io_error, "SetEndOfFile() oldsize=" << cur_size << " newsize=" << newsize << " "); if (direct_with_bad_size) { if (!CloseHandle(file_des)) STXXL_THROW_WIN_LASTERROR(io_error, "closing file (call of ::CloseHandle() from set_size) "); file_des = INVALID_HANDLE_VALUE; file_des = open_file_impl(filename, mode_ & ~TRUNC); } } } void wfs_file_base::close_remove() { close(); ::DeleteFile(filename.c_str()); } STXXL_END_NAMESPACE #endif // STXXL_WINDOWS // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/algo/async_schedule.cpp000644 001411 000144 00000014210 12411366426 017665 0ustar00tbusers000000 000000 /*************************************************************************** * lib/algo/async_schedule.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002, 2009 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ // Implements the "prudent prefetching" as described in // D. Hutchinson, P. Sanders, J. S. Vitter: Duality between prefetching // and queued writing on parallel disks, 2005 // DOI: 10.1137/S0097539703431573 #include #include #include #include #include #include #include #include #include #include #include #include #include #include STXXL_BEGIN_NAMESPACE namespace async_schedule_local { // only one type of event: WRITE COMPLETED struct sim_event { int_type timestamp; int_type iblock; inline sim_event(int_type t, int_type b) : timestamp(t), iblock(b) { } }; struct sim_event_cmp : public std::binary_function { inline bool operator () (const sim_event& a, const sim_event& b) const { return a.timestamp > b.timestamp; } }; typedef std::pair write_time_pair; struct write_time_cmp : public std::binary_function { inline bool operator () (const write_time_pair& a, const write_time_pair& b) const { return a.second > b.second; } }; static inline int_type get_disk(int_type i, const int_type* disks, int_type D) { int_type disk = disks[i]; if (disk == (int_type)file::DEFAULT_DEVICE_ID) disk = D; // remap to sentinel assert(0 <= disk && disk <= D); return disk; } int_type simulate_async_write( const int_type* disks, const int_type L, const int_type m_init, const int_type D, std::pair* o_time) { typedef std::priority_queue, sim_event_cmp> event_queue_type; typedef std::queue disk_queue_type; assert(L >= D); simple_vector disk_queues(D + 1); // + sentinel for remapping NO_ALLOCATOR event_queue_type event_queue; int_type m = m_init; int_type i = L - 1; int_type oldtime = 0; simple_vector disk_busy(D + 1); while (m && (i >= 0)) { int_type disk = get_disk(i, disks, D); disk_queues[disk].push(i); i--; m--; } for (int_type ii = 0; ii <= D; ii++) if (!disk_queues[ii].empty()) { int_type j = disk_queues[ii].front(); disk_queues[ii].pop(); event_queue.push(sim_event(1, j)); //STXXL_MSG("Block "<(cur.iblock, cur.timestamp); if (i >= 0) { int_type disk = get_disk(i, disks, D); if (disk_busy[disk]) { disk_queues[disk].push(i--); } else { if (!disk_queues[disk].empty()) { STXXL_VERBOSE1("c Block " << disk_queues[disk].front() << " scheduled for time " << cur.timestamp + 1); event_queue.push(sim_event(cur.timestamp + 1, disk_queues[disk].front())); disk_queues[disk].pop(); } else { STXXL_VERBOSE1("a Block " << i << " scheduled for time " << cur.timestamp + 1); event_queue.push(sim_event(cur.timestamp + 1, i--)); } disk_busy[disk] = true; } } // add next block to write int_type disk = get_disk(cur.iblock, disks, D); if (!disk_busy[disk] && !disk_queues[disk].empty()) { STXXL_VERBOSE1("b Block " << disk_queues[disk].front() << " scheduled for time " << cur.timestamp + 1); event_queue.push(sim_event(cur.timestamp + 1, disk_queues[disk].front())); disk_queues[disk].pop(); disk_busy[disk] = true; } } assert(i == -1); for (int_type i = 0; i <= D; i++) assert(disk_queues[i].empty()); return (oldtime - 1); } } // namespace async_schedule_local void compute_prefetch_schedule( const int_type* first, const int_type* last, int_type* out_first, int_type m, int_type D) { typedef std::pair pair_type; int_type L = last - first; if (L <= D) { for (int_type i = 0; i < L; ++i) out_first[i] = i; return; } pair_type* write_order = new pair_type[L]; int_type w_steps = async_schedule_local::simulate_async_write(first, L, m, D, write_order); STXXL_VERBOSE1("Write steps: " << w_steps); for (int_type i = 0; i < L; i++) STXXL_VERBOSE1(first[i] << " " << write_order[i].first << " " << write_order[i].second); std::stable_sort(write_order, write_order + L, async_schedule_local::write_time_cmp() _STXXL_FORCE_SEQUENTIAL); for (int_type i = 0; i < L; i++) { out_first[i] = write_order[i].first; //if(out_first[i] != i) STXXL_VERBOSE1(i << " " << out_first[i]); } delete[] write_order; STXXL_UNUSED(w_steps); } STXXL_END_NAMESPACE // vim: et:ts=4:sw=4 stxxl-1.4.1/lib/CMakeLists.txt000644 001411 000144 00000006744 12404624022 016012 0ustar00tbusers000000 000000 ############################################################################ # lib/CMakeLists.txt # # CMake file for libstxxl.a/so/lib # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ set(LIBSTXXL_SOURCES common/cmdline.cpp common/exithandler.cpp common/log.cpp common/rand.cpp common/seed.cpp common/utils.cpp common/verbose.cpp common/version.cpp io/boostfd_file.cpp io/create_file.cpp io/disk_queued_file.cpp io/file.cpp io/fileperblock_file.cpp io/iostats.cpp io/mem_file.cpp io/request.cpp io/request_queue_impl_1q.cpp io/request_queue_impl_qwqr.cpp io/request_queue_impl_worker.cpp io/request_with_state.cpp io/request_with_waiters.cpp io/serving_request.cpp io/syscall_file.cpp io/ufs_file_base.cpp io/wbtl_file.cpp io/wfs_file_base.cpp io/wincall_file.cpp mng/block_manager.cpp mng/config.cpp mng/disk_allocator.cpp algo/async_schedule.cpp ) if(NOT MSVC) # additional sources for non Visual Studio builds set(LIBSTXXL_SOURCES ${LIBSTXXL_SOURCES} io/mmap_file.cpp io/simdisk_file.cpp ) endif(NOT MSVC) if(STXXL_HAVE_LINUXAIO_FILE) # additional sources fo LinuxAIO fileio access method set(LIBSTXXL_SOURCES ${LIBSTXXL_SOURCES} io/linuxaio_file.cpp io/linuxaio_queue.cpp io/linuxaio_request.cpp ) endif() # tell top-level cmakelists which library we build set(STXXL_EXPORTED_LIBS stxxl) # we name debug library builds "stxxl_debug" and release builds "stxxl" string(TOLOWER "stxxl_${CMAKE_BUILD_TYPE}" STXXL_LIBNAME) if(STXXL_LIBNAME STREQUAL "stxxl_release" OR STXXL_LIBNAME STREQUAL "stxxl_") set(STXXL_LIBNAME "stxxl") endif() if(BUILD_SHARED_LIBS) # build stxxl SHARED library and link all programs again it add_library(stxxl SHARED ${LIBSTXXL_SOURCES}) set_target_properties(stxxl PROPERTIES OUTPUT_NAME "${STXXL_LIBNAME}") set_target_properties(stxxl PROPERTIES VERSION "${STXXL_VERSION_STRING}") install(TARGETS stxxl EXPORT stxxl-targets ARCHIVE DESTINATION ${INSTALL_LIB_DIR} LIBRARY DESTINATION ${INSTALL_LIB_DIR}) if(BUILD_STATIC_LIBS) # but also build STATIC library add_library(stxxl_static STATIC ${LIBSTXXL_SOURCES}) set_target_properties(stxxl_static PROPERTIES OUTPUT_NAME "${STXXL_LIBNAME}") set_target_properties(stxxl_static PROPERTIES VERSION "${STXXL_VERSION_STRING}") install(TARGETS stxxl_static EXPORT stxxl-targets ARCHIVE DESTINATION ${INSTALL_LIB_DIR}) # we build both shared and static, export both set(STXXL_EXPORTED_LIBS stxxl stxxl_static) endif() else() if(NOT BUILD_STATIC_LIBS) message(FATAL_ERROR "You must build either a static or shared STXXL library") endif() # build stxxl STATIC library and link all programs again it add_library(stxxl STATIC ${LIBSTXXL_SOURCES}) set_target_properties(stxxl PROPERTIES OUTPUT_NAME "${STXXL_LIBNAME}") set_target_properties(stxxl PROPERTIES VERSION "${STXXL_VERSION_STRING}") install(TARGETS stxxl EXPORT stxxl-targets ARCHIVE DESTINATION ${INSTALL_LIB_DIR} LIBRARY DESTINATION ${INSTALL_LIB_DIR}) endif() # export variables to top-level cmakelists set(STXXL_LIBNAME ${STXXL_LIBNAME} PARENT_SCOPE) set(STXXL_EXPORTED_LIBS ${STXXL_EXPORTED_LIBS} PARENT_SCOPE) stxxl-1.4.1/local/test1.cpp000644 001411 000144 00000003412 12405152405 015331 0ustar00tbusers000000 000000 /*************************************************************************** * local/test1.cpp * * This is an example file included in the local/ directory of STXXL. All .cpp * files in local/ are automatically compiled and linked with STXXL by CMake. * You can use this method for simple prototype applications. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include struct my_less_int : std::less { int min_value() const { return std::numeric_limits::min(); } int max_value() const { return std::numeric_limits::max(); } }; int main() { // create vector stxxl::VECTOR_GENERATOR::result vector; // fill vector with random integers { stxxl::scoped_print_timer timer("write random numbers", 100 * 1024 * 1024 * sizeof(int)); stxxl::random_number32 random; for (size_t i = 0; i < 100 * 1024 * 1024; ++i) { vector.push_back(random()); } } // sort vector using 16 MiB RAM { stxxl::scoped_print_timer timer("sorting random numbers", 100 * 1024 * 1024 * sizeof(int)); stxxl::sort(vector.begin(), vector.end(), my_less_int(), 16 * 1024 * 1024); } // output first and last items: std::cout << vector.size() << " items sorted ranging from " << vector.front() << " to " << vector.back() << std::endl; return 0; } stxxl-1.4.1/local/test2.cpp000644 001411 000144 00000004011 12411366426 015335 0ustar00tbusers000000 000000 /*************************************************************************** * local/test2.cpp * * This is another example file included in the local/ directory of STXXL. All * .cpp files in local/ are automatically compiled and linked with STXXL by * CMake. You can use this method for simple prototype applications. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2014 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include struct my_less : std::less { int64_t min_value() const { return std::numeric_limits::min(); } int64_t max_value() const { return std::numeric_limits::max(); } }; int main() { stxxl::scoped_print_timer timer("overall work", 600 * 1024 * 1024 * (int64_t)sizeof(int64_t)); // create sorter stxxl::sorter sorter(my_less(), 256 * 1024 * 1024); // fill sorter with random integers { stxxl::scoped_print_timer timer("presort+write random numbers", 600 * 1024 * 1024 * (int64_t)sizeof(int64_t)); stxxl::random_number32 random; for (size_t i = 0; i < 600 * 1024 * 1024; ++i) { sorter.push(random() * random()); } } sorter.sort(); // get data back in sorted order { stxxl::scoped_print_timer timer("read+merge random numbers", 600 * 1024 * 1024 * (int64_t)sizeof(int64_t)); int64_t first = *sorter, last = first, count = 1; ++sorter; while (!sorter.empty()) last = *sorter, ++sorter, ++count; // output first and last items: std::cout << count << " items sorted ranging from " << first << " to " << last << std::endl; } return 0; } stxxl-1.4.1/local/CMakeLists.txt000644 001411 000144 00000001454 12350112610 016322 0ustar00tbusers000000 000000 ############################################################################ # local/CMakeLists.txt # # CMake file which compiles and links all .cpp files in local/ with STXXL. # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ # find all .cpp files in current directory file(GLOB source_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp") # compile and link foreach(file ${source_files}) # remove .cpp string(REGEX REPLACE "\\.cpp$" "" file ${file}) # call macro stxxl_build_tool(${file}) endforeach() stxxl-1.4.1/CMakeLists.txt000644 001411 000144 00000061270 12424126603 015243 0ustar00tbusers000000 000000 ############################################################################ # CMakeLists.txt # # Base CMake file for building stxxl with different options. # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013-2014 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ set(CMAKE_LEGACY_CYGWIN_WIN32 0) # needed to silence warning on cygwin # require cmake 2.6.2 (but please use 2.8.x) cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) # the STXXL project project(stxxl) # for additional cmake scripts set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/misc/cmake) # prohibit in-source builds if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") message(FATAL_ERROR "In-source builds are not allowed, use a separate build directory.") endif() # default to Debug building for single-config generators if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message("Defaulting CMAKE_BUILD_TYPE to Debug") set(CMAKE_BUILD_TYPE "Debug") endif() # STXXL version string set(STXXL_VERSION_MAJOR "1") set(STXXL_VERSION_MINOR "4") set(STXXL_VERSION_PATCH "1") set(STXXL_VERSION_STRING "${STXXL_VERSION_MAJOR}.${STXXL_VERSION_MINOR}.${STXXL_VERSION_PATCH}") set(STXXL_VERSION_PHASE "prerelease/${CMAKE_BUILD_TYPE}") # read git directory (if it exists) and find git sha if(EXISTS ${PROJECT_SOURCE_DIR}/.git) find_package(Git) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} describe HEAD WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" OUTPUT_VARIABLE "STXXL_VERSION_GIT_REFSPEC" ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" OUTPUT_VARIABLE "STXXL_VERSION_GIT_SHA1" ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "Detected git refspec ${STXXL_VERSION_GIT_REFSPEC} sha ${STXXL_VERSION_GIT_SHA1}") endif() endif() ############################################################################### # compilation options # please document all build options in doc/install.doc:install_build_options option(BUILD_EXAMPLES "Build all stxxl examples" OFF) option(BUILD_TESTS "Build all stxxl test programs" OFF) if(BUILD_TESTS) set(BUILD_EXAMPLES ON) endif() option(BUILD_EXTRAS "Build all extra stxxl tests and tool programs" OFF) if(MSVC_VERSION LESS 1700) # require boost for Visual C++ versions older than VC11 = MSVS 2012 set(USE_BOOST ON) else() option(USE_BOOST "Use Boost libraries for threads,config,filesystem,random and date_time" OFF) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") option(USE_GNU_PARALLEL "Use GNU parallel extensions for multi-core parallelism" ON) endif() option(TRY_COMPILE_HEADERS "Test stxxl header files for self-sufficiency: try to compile them." OFF) if(NOT MSVC) option(USE_STD_THREADS "Force usage of C++ standard library thread support." OFF) endif() option(NO_CXX11 "Build without C++11 flags" OFF) option(USE_VALGRIND "Run tests with valgrind, pre-initialize some memory buffers." OFF) option(USE_GCOV "Compile and run tests with gcov for coverage analysis." OFF) # see tools/benchmarks about older TPIE benchmarks. option(USE_TPIE "Try to compile extra benchmarks from the 2007 S&PE paper with an old TPIE version." OFF) ### building shared and/or static libraries # by default we currently only build a static library, since we do not aim to # keep a stable binary interface. option(BUILD_STATIC_LIBS "Build static library version of libstxxl" ON) option(BUILD_SHARED_LIBS "Build shared library version of libstxxl" OFF) ### allow user to specify other installation paths set(INSTALL_BIN_DIR "bin" CACHE PATH "Installation directory for executables") set(INSTALL_LIB_DIR "lib" CACHE PATH "Installation directory for libraries") set(INSTALL_INCLUDE_DIR "include" CACHE PATH "Installation directory for header files") set(INSTALL_PKGCONFIG_DIR "lib/pkgconfig" CACHE PATH "Installation directory for pkg-config file") if(WIN32 AND NOT CYGWIN) set(DEF_INSTALL_CMAKE_DIR "CMake") else() set(DEF_INSTALL_CMAKE_DIR "lib/cmake/stxxl") endif() set(INSTALL_CMAKE_DIR "${DEF_INSTALL_CMAKE_DIR}" CACHE PATH "Installation directory for cmake files") ############################################################################### # enable use of "make test" enable_testing() include(CTest) set(CTEST_PROJECT_NAME "STXXL") if(USE_VALGRIND) set(STXXL_WITH_VALGRIND 1) set(VALGRIND_OPTS --leak-check=full --error-exitcode=1 --suppressions=${PROJECT_SOURCE_DIR}/misc/valgrind.supp) endif() ############################################################################### # enable use of "make package" # general package settings set(CPACK_PACKAGE_VENDOR "STXXL Maintainer Team") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Library of external memory algorithms") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE_1_0.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${STXXL_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${STXXL_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${STXXL_VERSION_PATCH}") # source packaging set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") set(CPACK_SOURCE_IGNORE_FILES "/\\\\..*$;/build;${CPACK_SOURCE_IGNORE_FILES}") # binary packaging set(CPACK_GENERATOR "TGZ") include(CPack) ############################################################################### # check platform if(WIN32 OR WIN64 OR MINGW) set(STXXL_WINDOWS "1") endif() if(MSVC) set(STXXL_MSVC ${MSVC_VERSION}) endif() ############################################################################### # enable warnings if(MSVC) # Force to always compile with W4 if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") endif() ### disable verbose warnings: # warning C4127: conditional expression is constant set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4127") # warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4290") # warning C4250: '...' : inherits '...' via dominance set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4250") # warning C4512: assignment operator could not be generated (contains const members) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4512") # warning C4355: 'this' : used in base member initializer list set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4355") # disable lots of warnings about "unsecure" C runtime function add_definitions(-D_CRT_SECURE_NO_WARNINGS) # disable lots of warnings about deprecated POSIX function names add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) # disable lots of warnings about "unsecure" STL functions add_definitions(-D_SCL_SECURE_NO_WARNINGS) else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -pedantic -Wno-long-long") # detect -Wextra include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-Wextra CXX_HAS_FLAGS_WEXTRA) if(CXX_HAS_FLAGS_WEXTRA) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") endif() endif() ############################################################################### # enable C++11 and more compiler features # more template depth for some tests and compilers include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-ftemplate-depth=1024 CXX_HAS_TEMPLATE_DEPTH) if(CXX_HAS_TEMPLATE_DEPTH) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth=1024") endif() # enable C++11 if(MSVC) # MSVC 11 or greater has C++11 automatically enabled elseif(CYGWIN) # C++11 is very restrictive on Cygwin elseif(NOT NO_CXX11) # try -std= flags to enable C++11 check_cxx_compiler_flag(-std=c++11 CXX_HAS_STD_CXX11) if(CXX_HAS_STD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") else() check_cxx_compiler_flag(-std=c++0x CXX_HAS_STD_CXX0X) if(CXX_HAS_STD_CXX0X) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") endif() endif() # on MacOSX with clang we need to use libc++ for C++11 headers if(APPLE) if (CMAKE_CXX_COMPILER MATCHES ".*clang[+][+]" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") check_cxx_compiler_flag(-stdlib=libc++ CXX_HAS_STDLIB_LIBCXX) if(CXX_HAS_STDLIB_LIBCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") else() message(SEND_ERROR "Compilation on MacOSX with clang requires libc++.") endif() endif() endif(APPLE) endif() # check C++ compiler for C++11 features include(CheckCXXSourceCompiles) check_cxx_source_compiles( "#include int main() { std::vector v(42); for (auto i : v) { ++i; } return 0; }" STXXL_HAVE_CXX11_RANGE_FOR_LOOP) ############################################################################### # enable gcov coverage analysis with gcc if(USE_GCOV) # find programs find_program(GENHTML genhtml) find_program(LCOV lcov) if(NOT LCOV OR NOT GENHTML) message(SEND_ERROR "Coverage analysis requires lcov and genhtml programs.") endif() # add coverage anaylsis compile and link flags set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") # add cached variable containing parameters for lcov/genhtml set(LCOV_FLAGS "" CACHE STRING "parameters for lcov") set(GENHTML_FLAGS --legend --no-branch-coverage CACHE STRING "parameters for genhtml") # custom target to run before tests add_custom_target(lcov-reset COMMAND ${LCOV} -q --directory ${CMAKE_BINARY_DIR} --zerocounters COMMENT "Resetting code coverage counters") # custom lcov target to run tests add_custom_target(lcov-runtests COMMAND ${CMAKE_CTEST_COMMAND} \${ARGS} DEPENDS ${ALL_TESTS} lcov-reset COMMENT "Running all unit tests") # get git version description execute_process(COMMAND git describe --tags WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE GITDESC OUTPUT_STRIP_TRAILING_WHITESPACE) # command sequence to gather, clean and generate HTML coverage report add_custom_target(lcov-html COMMAND ${LCOV} -q --directory . --capture --output-file lcov.info COMMAND ${LCOV} -q --remove lcov.info '/usr/*' ${LCOV_FLAGS} --output-file lcov-clean.info COMMAND ${GENHTML} -q -o coverage --title "STXXL ${GITDESC}" --prefix ${PROJECT_SOURCE_DIR} ${GENHTML_FLAGS} lcov-clean.info COMMENT "Capturing code coverage counters and create HTML coverage report" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) # top-level target to run tests and generate coverage report add_custom_target(test-coverage COMMENT "Generate HTML coverage report " DEPENDS lcov-runtests lcov-html) endif(USE_GCOV) ############################################################################### # find thread and random library # for testing for c++ system include files include(CheckIncludeFileCXX) check_include_file_cxx(pthread.h HAVE_PTHREAD_H) if(MINGW AND NOT HAVE_PTHREAD_H) set(USE_STD_THREADS ON) endif() if(MSVC OR USE_STD_THREADS) # check for std::mutex and std::threads avalability check_cxx_source_compiles( "#include int main() { std::mutex mutex; mutex.lock(); return 0; }" HAVE_STD_MUTEX) check_cxx_source_compiles( "#include int main() { std::thread t; return 0; }" HAVE_STD_THREAD) if(HAVE_STD_THREAD AND HAVE_STD_MUTEX) set(STXXL_STD_THREADS "1") else() set(USE_BOOST ON) message("No std::thread and std::mutex found, trying to use Boost classes instead") endif() # using also requires -pthread on gcc if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() # also check for check_include_file_cxx(random STXXL_STD_RANDOM) else() find_package(Threads REQUIRED) set(STXXL_POSIX_THREADS "1") endif() ############################################################################### # determine large file support # note: these flags need not be exported, all file operations must be # encapsulated within the library! include(TestLargeFiles) test_large_files(HAVE_LARGEFILES) if(HAVE_LARGEFILES) add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES) else() message(FATAL_ERROR "Large file support was not detectable.") endif() ############################################################################### # check for O_DIRECT flag if(CYGWIN) #-tb O_DIRECT messes up cygwin set(STXXL_DIRECT_IO_OFF 1) elseif(MSVC OR MINGW) # have FILE_FLAG_NO_BUFFERING on Windows set(STXXL_DIRECT_IO_OFF 0) elseif(APPLE) # we do not have to test anything, all is well. set(STXXL_DIRECT_IO_OFF 0) else() include(CheckCXXSourceCompiles) check_cxx_source_compiles(" #include #include int main() { return ((int)O_DIRECT) != 0; } " STXXL_HAVE_O_DIRECT) if(STXXL_HAVE_O_DIRECT) set(STXXL_DIRECT_IO_OFF 0) else() set(STXXL_DIRECT_IO_OFF 1) endif() endif() if(STXXL_DIRECT_IO_OFF) message(WARNING "The open() flag O_DIRECT was disabled!\n" "This means that STXXL cannot bypass the system cache on your system, which may degrade performance.") endif() ############################################################################### # check for mmap() function in sys/mman.h include(CheckSymbolExists) check_symbol_exists(mmap "sys/mman.h" STXXL_HAVE_MMAP_FILE) ############################################################################### # check for Linux aio syscalls include(CheckCXXSourceCompiles) check_cxx_source_compiles( "#include #include #include int main() { aio_context_t context; long r = syscall(SYS_io_setup, 5, &context); return (r == 0) ? 0 : -1; }" STXXL_HAVE_LINUXAIO_FILE) ############################################################################### # check for an atomic add-and-fetch intrinsic for counting_ptr include(CheckCXXSourceCompiles) check_cxx_source_compiles( "int main() { int x; __sync_add_and_fetch(&x, +1); return 0; }" STXXL_HAVE_SYNC_ADD_AND_FETCH) ############################################################################### # optional Boost libraries if(USE_BOOST) set(Boost_USE_MULTITHREADED ON) if(WIN32) set(Boost_USE_STATIC_LIBS ON) endif() # first try to find the version find_package(Boost 1.34.1 REQUIRED) if(Boost_VERSION GREATER 104699) #-tb Boost >= 1.47.0 found, we must also link with libboost_chrono for MSVC to work. find_package(Boost 1.47.0 REQUIRED COMPONENTS thread date_time chrono iostreams system filesystem) elseif(Boost_VERSION GREATER 103499) #-tb Boost >= 1.35.0 found, we must also link with libboost_system for MSVC to work. find_package(Boost 1.35.0 REQUIRED COMPONENTS thread date_time iostreams system filesystem) else() find_package(Boost 1.34.1 REQUIRED COMPONENTS thread date_time iostreams filesystem) endif() # force caching of variables to display them to the user set(BOOST_ROOT "${BOOST_ROOT}" CACHE PATH "Boost installation root." FORCE) if(Boost_FOUND) # globally add boost include directories include_directories(${Boost_INCLUDE_DIRS}) # set defines in set(STXXL_BOOST_CONFIG "1") set(STXXL_BOOST_FILESYSTEM "1") set(STXXL_BOOST_RANDOM "1") set(STXXL_BOOST_THREADS "1") set(STXXL_BOOST_TIMESTAMP "1") elseif(MSVC_VERSION LESS 1700) message(FATAL_ERROR "Boost libraries are required for MSVC < 2012.") else() message(FATAL_ERROR "Boost libraries not found. Try compilation without them.") endif() endif(USE_BOOST) ############################################################################### # optional GNU parallel STL mode if(USE_GNU_PARALLEL) include(FindOpenMP) if(NOT OPENMP_FOUND) message(FATAL_ERROR "OpenMP not found. Try compilation without GNU parallel mode.") else() check_include_file_cxx(parallel/algorithm HAVE_PARALLEL_ALGORITHM_H) if(NOT HAVE_PARALLEL_ALGORITHM_H) message(FATAL_ERROR "GNU parallel mode header not found. Try compilation without parallel mode.") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") set(STXXL_PARALLEL_MODE_EXPLICIT "1") endif() endif() endif(USE_GNU_PARALLEL) ############################################################################### # check if one thread and random library is available if(STXXL_STD_THREADS) message(STATUS "Using std::thread and other C++11 library functions.") set(STXXL_BOOST_THREADS 0) set(STXXL_POSIX_THREADS 0) elseif(STXXL_BOOST_THREADS) message(STATUS "Using boost::thread library functions.") set(STXXL_STD_THREADS 0) set(STXXL_POSIX_THREADS 0) elseif(STXXL_POSIX_THREADS) message(STATUS "Using POSIX pthread library functions.") set(STXXL_STD_THREADS 0) set(STXXL_BOOST_THREADS 0) else() message(SEND_ERROR "Could not detect a thread library. Check the compilation documentation.") endif() ############################################################################### # test for additional includes and features used by some stxxl_tool components include(CheckSymbolExists) check_symbol_exists(mallinfo "malloc.h" STXXL_HAVE_MALLINFO_PROTO) check_symbol_exists(mlock "sys/mman.h" STXXL_HAVE_MLOCK_PROTO) ############################################################################### # configure environment for building # create config.h with define switches _inside binary dir_! configure_file(${PROJECT_SOURCE_DIR}/include/stxxl/bits/config.h.in ${PROJECT_BINARY_DIR}/include/stxxl/bits/config.h) # CXX_FLAGS required for compilation set(STXXL_CXX_FLAGS ${OpenMP_CXX_FLAGS}) # globally adds top-level include directories set(STXXL_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include/ ${PROJECT_BINARY_DIR}/include/) include_directories(${STXXL_INCLUDE_DIRS}) # for targets using stxxl library set(STXXL_EXTRA_LIBRARIES ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) set(STXXL_LIBRARIES stxxl ${STXXL_EXTRA_LIBRARIES}) # export STXXL_INCLUDE_DIRS and STXXL_LIBRARIES to global CACHE set(STXXL_CXX_FLAGS "${STXXL_CXX_FLAGS}" CACHE STRING "Compiler flags for STXXL") set(STXXL_INCLUDE_DIRS "${STXXL_INCLUDE_DIRS}" CACHE STRING "Include paths for STXXL") set(STXXL_LIBRARIES "${STXXL_LIBRARIES}" CACHE STRING "Libraries to link for STXXL") # build libstxxl in /lib add_subdirectory(lib) ############################################################################### # macros for building stxxl programs and tests # macro for building stxxl programs macro(stxxl_build_tool PROGNAME) add_executable(${PROGNAME} ${PROGNAME}.cpp ${ARGN}) target_link_libraries(${PROGNAME} ${STXXL_LIBRARIES}) endmacro(stxxl_build_tool) # macro for building stxxl examples macro(stxxl_build_example TESTNAME) if(BUILD_EXAMPLES) stxxl_build_tool(${TESTNAME} ${ARGN}) endif(BUILD_EXAMPLES) endmacro(stxxl_build_example) # macro for building stxxl tests macro(stxxl_build_test TESTNAME) if(BUILD_TESTS) stxxl_build_tool(${TESTNAME} ${ARGN}) endif(BUILD_TESTS) endmacro(stxxl_build_test) # macro for registering stxxl tests macro(stxxl_test TESTNAME) if(BUILD_TESTS) set(TESTFULLNAME ${TESTNAME} ${ARGN}) string(REPLACE ";" "_" TESTFULLNAME "${TESTFULLNAME}") # stringify list if(USE_VALGRIND) # prepend valgrind call add_test(${TESTFULLNAME} /usr/bin/valgrind ${VALGRIND_OPTS} ./${TESTNAME} ${ARGN}) set_tests_properties(${TESTFULLNAME} PROPERTIES TIMEOUT 7200) else() add_test(${TESTFULLNAME} ${TESTNAME} ${ARGN}) set_tests_properties(${TESTFULLNAME} PROPERTIES TIMEOUT 3600) endif() endif(BUILD_TESTS) endmacro(stxxl_test) # macro for building stxxl extra program macro(stxxl_build_extra_tool) if(BUILD_EXTRAS) stxxl_build_tool(${ARGN}) endif(BUILD_EXTRAS) endmacro(stxxl_build_extra_tool) # macro for registering extra stxxl tests macro(stxxl_extra_test) if(BUILD_EXTRAS) stxxl_test(${ARGN}) endif(BUILD_EXTRAS) endmacro(stxxl_extra_test) # macro for setting additional defines for targets macro(add_define PROGNAME) if(TARGET ${PROGNAME}) set_property(TARGET ${PROGNAME} APPEND PROPERTY COMPILE_DEFINITIONS ${ARGN}) else() if(BUILD_TESTS) message("Ignoring add_define(${PROGNAME} ${ARGN}) for unknown target ${PROGNAME}") endif(BUILD_TESTS) endif() endmacro(add_define TESTNAME) ############################################################################### # cmake script TRY_COMPILE all stxxl header files if(TRY_COMPILE_HEADERS) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES ${STXXL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) file(GLOB_RECURSE header_files FOLLOW_SYMLINKS "include/*") list(REMOVE_ITEM header_files "${PROJECT_SOURCE_DIR}/include/stxxl/bits/config.h.in") list(SORT header_files) foreach(file ${header_files}) string(REPLACE "/" "_" compilename "${file}") # replace / to _ to fix warnings string(REPLACE "." "_" compilename "${compilename}") check_cxx_source_compiles( "#include \"${file}\" int main() { return 0; }" IsSelfContained${compilename}) if(NOT IsSelfContained${compilename}) message(SEND_ERROR "Compilation FAILED for ${file}\n\nCompiler output:\n${OUTPUT}") endif() endforeach() endif(TRY_COMPILE_HEADERS) ############################################################################### # figure out STXXL_TMPDIR for tests if(BUILD_TESTS) if(DEFINED ENV{STXXL_TMPDIR}) set(STXXL_TMPDIR "$ENV{STXXL_TMPDIR}") elseif(NOT DEFINED STXXL_TMPDIR) set(STXXL_TMPDIR ".") endif() message(STATUS "Using STXXL_TMPDIR ${STXXL_TMPDIR} for tests") endif(BUILD_TESTS) ############################################################################### # install header files install(FILES include/stxxl.h DESTINATION ${INSTALL_INCLUDE_DIR}) install(DIRECTORY include/stxxl DESTINATION ${INSTALL_INCLUDE_DIR}) # also copy the config file with build options! install(FILES ${PROJECT_BINARY_DIR}/include/stxxl/bits/config.h DESTINATION ${INSTALL_INCLUDE_DIR}/stxxl/bits/) ############################################################################### # prepare pkg-config file configure_file(misc/cmake/stxxl.pc "${PROJECT_BINARY_DIR}/${STXXL_LIBNAME}.pc" @ONLY) # copy the stxxl.pc file into lib/pkgconfig if(INSTALL_PKGCONFIG_DIR) install(FILES ${PROJECT_BINARY_DIR}/${STXXL_LIBNAME}.pc DESTINATION ${INSTALL_PKGCONFIG_DIR}) endif() ############################################################################### # export targets to cmake project config file if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.6) # register package for use from the global CMake-registry export(PACKAGE stxxl) endif() # add stxxl library targets to the build tree export set export(TARGETS ${STXXL_EXPORTED_LIBS} FILE "${PROJECT_BINARY_DIR}/stxxl-targets.cmake") # calculate absolute install paths foreach(dir LIB BIN INCLUDE CMAKE) set(ABS_INSTALL_${dir}_DIR "${INSTALL_${dir}_DIR}") if(NOT IS_ABSOLUTE "${ABS_INSTALL_${dir}_DIR}") set(ABS_INSTALL_${dir}_DIR "${CMAKE_INSTALL_PREFIX}/${ABS_INSTALL_${dir}_DIR}") endif() endforeach() # calculate final installed include path relative to cmake install path file(RELATIVE_PATH REL_INCLUDE_DIR "${ABS_INSTALL_CMAKE_DIR}" "${ABS_INSTALL_INCLUDE_DIR}") # create common stxxl-config.cmake file configure_file(misc/cmake/stxxl-version.cmake.in "${PROJECT_BINARY_DIR}/stxxl-version.cmake" @ONLY) # create stxxl-version.cmake file for the build tree set(CONF_INCLUDE_DIRS "${STXXL_INCLUDE_DIRS}") configure_file(misc/cmake/stxxl-config.cmake.in "${PROJECT_BINARY_DIR}/stxxl-config.cmake" @ONLY) # create stxxl-version.cmake file for the install tree set(CONF_INCLUDE_DIRS "\${STXXL_CMAKE_DIR}/${REL_INCLUDE_DIR}") configure_file(misc/cmake/stxxl-config.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/stxxl-config.cmake" @ONLY) # install the stxxl-config.cmake and stxxl-version.cmake install(FILES "${PROJECT_BINARY_DIR}/stxxl-version.cmake" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/stxxl-config.cmake" DESTINATION "${INSTALL_CMAKE_DIR}") # Install the export set for use with the install-tree install(EXPORT stxxl-targets DESTINATION "${INSTALL_CMAKE_DIR}") ############################################################################### # build tests and tools add_subdirectory(tools) add_subdirectory(examples) add_subdirectory(tests) add_subdirectory(local) stxxl-1.4.1/tools/benchmark_pqueue.cpp000644 001411 000144 00000025107 12411366426 017671 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmark_pqueue.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ static const char* description = "Benchmark the priority queue implementation using a sequence of " "operations. The PQ contains pairs of 32- or 64-bit integers, or a " "24 byte struct. The operation sequence is either a simple fill/delete " "cycle or fill/intermixed inserts/deletes. Because the memory parameters " "of the PQ must be set a compile-time, the benchmark provides only " "three PQ sizes: for 256 MiB, 1 GiB and 8 GiB of RAM, with the maximum " "number of items set accordingly."; #include #include #include #include #include #include #include using stxxl::uint32; using stxxl::uint64; using stxxl::internal_size_type; #define MiB (1024 * 1024) #define PRINTMOD (16 * MiB) // *** Integer Pair Types typedef stxxl::tuple uint32_pair_type; typedef stxxl::tuple uint64_pair_type; // *** Larger Structure Type #define MY_TYPE_SIZE 24 struct my_type : public uint32_pair_type { typedef uint32 key_type; char data[MY_TYPE_SIZE - sizeof(uint32_pair_type)]; my_type() { } my_type(const key_type& k1, const key_type& k2) : uint32_pair_type(k1, k2) { #if STXXL_WITH_VALGRIND memset(data, 0, sizeof(data)); #endif } static my_type max_value() { return my_type(std::numeric_limits::max(), std::numeric_limits::max()); } }; template struct my_cmp : public std::binary_function { bool operator () (const ValueType& a, const ValueType& b) const { // PQ is a max priority queue, thus compare greater return a.first > b.first; } ValueType min_value() const { return ValueType::max_value(); } }; static inline void progress(const char* text, uint64 i, uint64 nelements) { if ((i % PRINTMOD) == 0) STXXL_MSG(text << " " << i << " (" << std::setprecision(5) << ((double)i * 100.0 / (double)nelements) << " %)"); } template void run_pqueue_insert_delete(uint64 nelements, internal_size_type mem_for_pools) { typedef typename PQType::value_type ValueType; // construct priority queue PQType pq(mem_for_pools / 2, mem_for_pools / 2); pq.dump_sizes(); STXXL_MSG("Internal memory consumption of the priority queue: " << pq.mem_cons() << " B"); stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); { stxxl::scoped_print_timer timer("Filling PQ", nelements * sizeof(ValueType)); for (stxxl::uint64 i = 0; i < nelements; i++) { progress("Inserting element", i, nelements); pq.push(ValueType((int)(nelements - i), 0)); } } STXXL_CHECK(pq.size() == nelements); STXXL_MSG("Internal memory consumption of the priority queue: " << pq.mem_cons() << " B"); pq.dump_sizes(); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; stats_begin = *stxxl::stats::get_instance(); { stxxl::scoped_print_timer timer("Reading PQ", nelements * sizeof(ValueType)); for (stxxl::uint64 i = 0; i < nelements; ++i) { STXXL_CHECK(!pq.empty()); STXXL_CHECK(pq.top().first == i + 1); pq.pop(); progress("Popped element", i, nelements); } } STXXL_MSG("Internal memory consumption of the priority queue: " << pq.mem_cons() << " B"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; } template void run_pqueue_insert_intermixed(uint64 nelements, internal_size_type mem_for_pools) { typedef typename PQType::value_type ValueType; // construct priority queue PQType pq(mem_for_pools / 2, mem_for_pools / 2); pq.dump_sizes(); STXXL_MSG("Internal memory consumption of the priority queue: " << pq.mem_cons() << " B"); stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); { stxxl::scoped_print_timer timer("Filling PQ", nelements * sizeof(ValueType)); for (stxxl::uint64 i = 0; i < nelements; i++) { progress("Inserting element", i, nelements); pq.push(ValueType((int)(nelements - i), 0)); } } STXXL_CHECK(pq.size() == nelements); STXXL_MSG("Internal memory consumption of the priority queue: " << pq.mem_cons() << " B"); pq.dump_sizes(); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; stats_begin = *stxxl::stats::get_instance(); stxxl::random_number32 rand; { stxxl::scoped_print_timer timer("Intermixed Insert/Delete", nelements * sizeof(ValueType)); for (stxxl::uint64 i = 0; i < nelements; ++i) { int o = rand() % 3; if (o == 0) { pq.push(ValueType((int)(nelements - i), 0)); } else { STXXL_CHECK(!pq.empty()); pq.pop(); } progress("Intermixed element", i, nelements); } } STXXL_MSG("Internal memory consumption of the priority queue: " << pq.mem_cons() << " B"); pq.dump_sizes(); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; } template int do_benchmark_pqueue(uint64 volume, int opseq) { const internal_size_type mem_for_queue = mib_for_queue * MiB; const internal_size_type mem_for_pools = mib_for_pools * MiB; typedef typename stxxl::PRIORITY_QUEUE_GENERATOR< ValueType, my_cmp, mem_for_queue, maxvolume* MiB / sizeof(ValueType)> gen; typedef typename gen::result pq_type; STXXL_MSG("Given PQ parameters: " << mib_for_queue << " MiB for queue, " << mib_for_pools << " MiB for pools, " << maxvolume << " GiB maximum volume."); STXXL_MSG("Selected PQ parameters:"); STXXL_MSG("element size: " << sizeof(ValueType)); STXXL_MSG("block size: " << pq_type::BlockSize); STXXL_MSG("insertion buffer size (N): " << pq_type::N << " items (" << pq_type::N * sizeof(ValueType) << " B)"); STXXL_MSG("delete buffer size: " << pq_type::delete_buffer_size); STXXL_MSG("maximal arity for internal mergers (AI): " << pq_type::IntKMAX); STXXL_MSG("maximal arity for external mergers (AE): " << pq_type::ExtKMAX); STXXL_MSG("internal groups: " << pq_type::num_int_groups); STXXL_MSG("external groups: " << pq_type::num_ext_groups); STXXL_MSG("X : " << gen::X); if (volume == 0) volume = 2 * (mem_for_queue + mem_for_pools); stxxl::uint64 nelements = volume / sizeof(ValueType); STXXL_MSG("Number of elements: " << nelements); if (opseq == 0) { run_pqueue_insert_delete(nelements, mem_for_pools); run_pqueue_insert_intermixed(nelements, mem_for_pools); } else if (opseq == 1) run_pqueue_insert_delete(nelements, mem_for_pools); else if (opseq == 2) run_pqueue_insert_intermixed(nelements, mem_for_pools); else STXXL_ERRMSG("Invalid operation sequence."); return 1; } template int do_benchmark_pqueue_config(unsigned pqconfig, uint64 size, unsigned opseq) { if (pqconfig == 0) { do_benchmark_pqueue_config(1, size, opseq); do_benchmark_pqueue_config(2, size, opseq); do_benchmark_pqueue_config(3, size, opseq); return 1; } else if (pqconfig == 1) return do_benchmark_pqueue(size, opseq); else if (pqconfig == 2) return do_benchmark_pqueue(size, opseq); #if __x86_64__ || __LP64__ || (__WORDSIZE == 64) else if (pqconfig == 3) return do_benchmark_pqueue(size, opseq); #endif else return 0; } int do_benchmark_pqueue_type(unsigned type, unsigned pqconfig, uint64 size, unsigned opseq) { if (type == 0) { do_benchmark_pqueue_type(1, pqconfig, size, opseq); do_benchmark_pqueue_type(2, pqconfig, size, opseq); do_benchmark_pqueue_type(3, pqconfig, size, opseq); return 1; } else if (type == 1) return do_benchmark_pqueue_config(pqconfig, size, opseq); else if (type == 2) return do_benchmark_pqueue_config(pqconfig, size, opseq); else if (type == 3) return do_benchmark_pqueue_config(pqconfig, size, opseq); else return 0; } int benchmark_pqueue(int argc, char* argv[]) { // parse command line stxxl::cmdline_parser cp; cp.set_description(description); uint64 size = 0; cp.add_opt_param_bytes("size", "Amount of data to insert (e.g. 1GiB)", size); unsigned type = 2; cp.add_uint('t', "type", "Value type of tested priority queue:\n 1 = pair of uint32,\n 2 = pair of uint64 (default),\n 3 = 24 byte struct\n 0 = all of the above", type); unsigned pqconfig = 2; cp.add_uint('p', "pq", "Priority queue configuration to test:\n" "1 = small (256 MiB RAM, 4 GiB elements)\n" "2 = medium (1 GiB RAM, 16 GiB elements) (default)\n" #if __x86_64__ || __LP64__ || (__WORDSIZE == 64) "3 = big (8 GiB RAM, 64 GiB elements)\n" #endif "0 = all of the above", pqconfig); unsigned opseq = 1; cp.add_uint('o', "opseq", "Operation sequence to perform:\n 1 = insert all, delete all (default)\n 2 = insert all, intermixed insert/delete\n 0 = all of the above", opseq); if (!cp.process(argc, argv)) return -1; stxxl::config::get_instance(); if (!do_benchmark_pqueue_type(type, pqconfig, size, opseq)) { STXXL_ERRMSG("Invalid (type,pqconfig) combination."); } return 0; } stxxl-1.4.1/tools/extras/iobench_scatter_in_place.cpp000644 001411 000144 00000017346 12411366426 022655 0ustar00tbusers000000 000000 /*************************************************************************** * tools/extras/iobench_scatter_in_place.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include using stxxl::request_ptr; using stxxl::file; using stxxl::timer; using stxxl::uint64; using stxxl::unsigned_type; #ifndef BLOCK_ALIGN #define BLOCK_ALIGN 4096 #endif #define MB (1024 * 1024) #define GB (1024 * 1024 * 1024) void usage(const char* argv0) { std::cout << "Usage: " << argv0 << " num_blocks blocks_per_round block_size file" << std::endl; std::cout << " 'block_size' in bytes" << std::endl; std::cout << " 'file' is split into 'num_blocks' files of size 'block_size'," << std::endl; std::cout << " reading chunks of 'blocks_per_round' blocks starting from end-of-file" << std::endl; std::cout << " and truncating the input file after each chunk was read," << std::endl; std::cout << " before writing the chunk to new files" << std::endl; exit(-1); } // returns throughput in MiB/s inline double throughput(stxxl::uint64 bytes, double seconds) { if (seconds == 0.0) return 0.0; return (double)bytes / (1024 * 1024) / seconds; } int main(int argc, char* argv[]) { if (argc < 5) usage(argv[0]); unsigned_type num_blocks = (unsigned_type)stxxl::atouint64(argv[1]); unsigned_type blocks_per_round = (unsigned_type)stxxl::atouint64(argv[2]); unsigned_type block_size = (unsigned_type)stxxl::atouint64(argv[3]); const char* filebase = argv[4]; unsigned_type num_rounds = stxxl::div_ceil(num_blocks, blocks_per_round); std::cout << "# Splitting '" << filebase << "' into " << num_rounds * blocks_per_round << " blocks of size " << block_size << ", reading chunks of " << blocks_per_round << " blocks" << std::endl; char* buffer = (char*)stxxl::aligned_alloc(block_size * blocks_per_round); double totaltimeread = 0, totaltimewrite = 0; stxxl::int64 totalsizeread = 0, totalsizewrite = 0; double totaltimereadchunk = 0.0, totaltimewritechunk = 0.0; stxxl::int64 totalsizereadchunk = 0, totalsizewritechunk = 0; typedef stxxl::syscall_file file_type; file_type input_file(filebase, file::RDWR | file::DIRECT, 0); timer t_total(true); try { for (stxxl::unsigned_type r = num_rounds; r-- > 0; ) { // read a chunk of blocks_per_round blocks timer t_read(true); for (stxxl::unsigned_type i = blocks_per_round; i-- > 0; ) { const uint64 offset = (r * blocks_per_round + i) * block_size; timer t_op(true); // read a block { input_file.aread(buffer + i * block_size, offset, block_size)->wait(); } t_op.stop(); totalsizeread += block_size; totaltimeread += t_op.seconds(); if (blocks_per_round > 1) { std::cout << "Offset " << std::setw(8) << offset / MB << " MiB: " << std::fixed; std::cout << std::setw(8) << std::setprecision(3) << throughput(block_size, t_op.seconds()) << " MiB/s read"; std::cout << std::endl; } } // truncate input_file.set_size(r * blocks_per_round * block_size); t_read.stop(); totalsizereadchunk += blocks_per_round * block_size; totaltimereadchunk += t_read.seconds(); // write the chunk timer t_write(true); for (stxxl::unsigned_type i = blocks_per_round; i-- > 0; ) { const uint64 offset = (r * blocks_per_round + i) * block_size; timer t_op(true); // write a block { char cfn[4096]; // PATH_MAX snprintf(cfn, sizeof(cfn), "%s_%012llX", filebase, offset); file_type chunk_file(cfn, file::CREAT | file::RDWR | file::DIRECT, 0); chunk_file.awrite(buffer + i * block_size, 0, block_size)->wait(); } t_op.stop(); totalsizewrite += block_size; totaltimewrite += t_op.seconds(); if (blocks_per_round > 1) { std::cout << "Offset " << std::setw(8) << offset / MB << " MiB: " << std::fixed; std::cout << std::setw(8) << std::setprecision(3) << "" << " "; std::cout << std::setw(8) << std::setprecision(3) << throughput(block_size, t_op.seconds()) << " MiB/s write"; std::cout << std::endl; } } t_write.stop(); totalsizewritechunk += blocks_per_round * block_size; totaltimewritechunk += t_write.seconds(); const uint64 offset = r * blocks_per_round * block_size; std::cout << "Input offset " << std::setw(8) << offset / MB << " MiB: " << std::fixed; std::cout << std::setw(8) << std::setprecision(3) << throughput(block_size * blocks_per_round, t_read.seconds()) << " MiB/s read, "; std::cout << std::setw(8) << std::setprecision(3) << throughput(block_size * blocks_per_round, t_write.seconds()) << " MiB/s write"; std::cout << std::endl; } } catch (const std::exception& ex) { std::cout << std::endl; STXXL_ERRMSG(ex.what()); } t_total.stop(); const int ndisks = 1; std::cout << "=============================================================================================" << std::endl; std::cout << "# Average over " << std::setw(8) << stxxl::STXXL_MAX(totalsizewrite, totalsizeread) / MB << " MiB: "; std::cout << std::setw(8) << std::setprecision(3) << (throughput(totalsizeread, totaltimeread)) << " MiB/s read, "; std::cout << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite, totaltimewrite)) << " MiB/s write" << std::endl; if (totaltimeread != 0.0) std::cout << "# Read time " << std::setw(8) << std::setprecision(3) << totaltimeread << " s" << std::endl; if (totaltimereadchunk != 0.0) std::cout << "# ChRd/trnk ti " << std::setw(8) << std::setprecision(3) << totaltimereadchunk << " s" << std::endl; if (totaltimewrite != 0.0) std::cout << "# Write time " << std::setw(8) << std::setprecision(3) << totaltimewrite << " s" << std::endl; if (totaltimewritechunk != 0.0) std::cout << "# ChWrite time " << std::setw(8) << std::setprecision(3) << totaltimewritechunk << " s" << std::endl; std::cout << "# Non-I/O time " << std::setw(8) << std::setprecision(3) << (t_total.seconds() - totaltimewrite - totaltimeread) << " s, average throughput " << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds() - totaltimewrite - totaltimeread) * ndisks) << " MiB/s" << std::endl; std::cout << "# Total time " << std::setw(8) << std::setprecision(3) << t_total.seconds() << " s, average throughput " << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds()) * ndisks) << " MiB/s" << std::endl; stxxl::aligned_dealloc(buffer); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tools/extras/benchmark_disk_and_flash.cpp000644 001411 000144 00000011362 12411366426 022622 0ustar00tbusers000000 000000 /*************************************************************************** * tools/extras/benchmark_disk_and_flash.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2008 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include using stxxl::request_ptr; using stxxl::file; using stxxl::timestamp; #ifdef BLOCK_ALIGN #undef BLOCK_ALIGN #endif #define BLOCK_ALIGN 4096 #define KB (1024) #define MB (1024 * 1024) #define GB (1024 * 1024 * 1024) void run(char* buffer, file** disks, stxxl::int64 offset, stxxl::int64 length, unsigned hdd_blocks, unsigned hdd_bytes, unsigned ssd_blocks, unsigned ssd_bytes, unsigned repeats) { unsigned i, j; double begin = timestamp(), end, elapsed; request_ptr* reqs = new request_ptr[stxxl::STXXL_MAX(hdd_blocks + ssd_blocks, 1U)]; struct diskinfo { unsigned id; unsigned bytes; unsigned n; }; diskinfo info[2]; // HDD info[0].id = 0; info[0].bytes = hdd_bytes; info[0].n = hdd_blocks; // SSD info[1].id = 1; info[1].bytes = ssd_bytes; info[1].n = ssd_blocks; begin = timestamp(); double volume = 0; for (unsigned repeat = 0; repeat < repeats; ++repeat) { int r = 0; char* buf = buffer; for (i = 0; i < 2; i++) { for (j = 0; j < info[i].n; j++) { unsigned bytes = info[i].bytes; stxxl::int64 position = (bytes * (rand() & 0xffff)) % length; reqs[r++] = disks[info[i].id]->aread(buf, offset + position, bytes); buf += bytes; volume += (double)bytes; } } wait_all(reqs, r); } end = timestamp(); elapsed = end - begin; std::cout << "B_d = " << info[0].bytes << " B_f = " << info[1].bytes << " n_d = " << info[0].n << " n_f = " << info[1].n; //<< std::endl; std::cout << " Transferred " << (volume / MB) << " MiB in " << elapsed << " seconds @ " << (volume / MB / elapsed) << " MiB/s" << std::endl; delete[] reqs; } void usage(const char* argv0) { std::cout << "Usage: " << argv0 << " offset length diskfile flashfile" << std::endl; std::cout << " starting 'offset' and 'length' are given in GiB" << std::endl; std::cout << " length == 0 implies till end of space (please ignore the write error)" << std::endl; exit(-1); } int main(int argc, char* argv[]) { if (argc < 4) usage(argv[0]); stxxl::int64 offset = stxxl::int64(GB) * stxxl::int64(atoi(argv[1])); stxxl::int64 length = stxxl::int64(GB) * stxxl::int64(atoi(argv[2])); int first_disk_arg = 3; std::vector disks_arr; if (!(first_disk_arg < argc)) usage(argv[0]); for (int ii = first_disk_arg; ii < argc; ii++) { std::cout << "# Add disk: " << argv[ii] << std::endl; disks_arr.push_back(argv[ii]); } const size_t ndisks = disks_arr.size(); stxxl::unsigned_type buffer_size = 1024 * MB; const stxxl::int64 buffer_size_int = buffer_size / sizeof(int); unsigned i; file** disks = new file*[ndisks]; unsigned* buffer = (unsigned*)stxxl::aligned_alloc(buffer_size); for (i = 0; i < buffer_size_int; i++) buffer[i] = i; for (i = 0; i < ndisks; i++) { disks[i] = new stxxl::syscall_file(disks_arr[i], file::CREAT | file::RDWR | file::DIRECT, i); } try { run((char*)buffer, disks, offset, length, 1, 2 * MB, 23, 128 * 1024, 100); run((char*)buffer, disks, offset, length, 1, 2 * MB, 42, 128 * 1024, 100); for (unsigned hdd_bytes = 4 * KB; hdd_bytes < 256 * MB; hdd_bytes <<= 1) { for (unsigned ssd_bytes = 128 * KB; ssd_bytes == 128 * KB; ssd_bytes <<= 1) { for (unsigned hdd_blocks = 1; hdd_blocks == 1; ++hdd_blocks) { for (unsigned ssd_blocks = 0; ssd_blocks <= (stxxl::STXXL_MAX(16U, 2 * hdd_bytes * hdd_blocks / ssd_bytes)); ++ssd_blocks) { run((char*)buffer, disks, offset, length, hdd_blocks, hdd_bytes, ssd_blocks, ssd_bytes, 100); } } } } } catch (const std::exception& ex) { std::cout << std::endl; STXXL_ERRMSG(ex.what()); } for (i = 0; i < ndisks; i++) delete disks[i]; delete[] disks; stxxl::aligned_dealloc(buffer); return 0; } // vim: et:ts=4:sw=4 stxxl-1.4.1/tools/extras/CMakeLists.txt000644 001411 000144 00000001072 12411366426 017710 0ustar00tbusers000000 000000 ############################################################################ # tools/extras/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(benchmark_disk_and_flash) stxxl_build_test(iobench_scatter_in_place) stxxl_build_test(pq_param) stxxl-1.4.1/tools/extras/pq_param.cpp000644 001411 000144 00000004335 12405152406 017453 0ustar00tbusers000000 000000 /*************************************************************************** * tools/extras/pq_param.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include typedef stxxl::int64 int64; int64 D(int64 m, int64 k, int64 MaxS, int64 E, int64 B) { return (m * m / 4 - (MaxS * E / (k - m)) / B); } using std::cout; using std::endl; int main() { int64 IntM = 128 * 1024 * 1024; int64 E = 4; int64 B = 8 * 1024 * 1024; //int64 Bstep = 128 * 1024; int64 MaxS = (int64(128) * int64(1024 * 1024 * 1024)) / E; for ( ; B > 4096; B = B / 2) { int64 m = 1; int64 k = IntM / B; for ( ; m < k; ++m) { int64 c = (k - m); //if( D(m,k,MaxS,E,B)>= 0 && c > 10) if ((k - m) * m * m * B / (E * 4) >= MaxS) { cout << (k - m) * (m) * (m * B / (E * 4)) << endl; cout << MaxS << endl; cout << "D: " << D(m, k, MaxS, E, B) << endl; cout << "B: " << B << endl; cout << "c: " << c << endl; cout << "k: " << k << endl; int64 Ae = m / 2; int64 Ae1 = Ae + int64(sqrt((double)D(m, k, MaxS, E, B))); int64 Ae2 = Ae - int64(sqrt((double)D(m, k, MaxS, E, B))); int64 x = c * B / E; int64 N = x / 4096; cout << "Ae : " << Ae << endl; cout << "Ae1: " << Ae1 << endl; cout << "Ae2: " << Ae2 << endl; cout << "minAe :" << (MaxS / (x * Ae)) << endl; cout << "x : " << x << endl; cout << "N : " << N << endl; int64 Cons = x * E + B * (m / 2) + MaxS * B / (x * (m / 2)); cout << "COns : " << Cons << endl; return 0; } } } cout << "No valid parameter found" << endl; } stxxl-1.4.1/tools/mallinfo.cpp000644 001411 000144 00000005754 12411366426 016162 0ustar00tbusers000000 000000 /*************************************************************************** * tools/mallinfo.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #if STXXL_HAVE_MALLINFO_PROTO #include #include #include #include #include #include void print_malloc_stats() { struct mallinfo info = mallinfo(); STXXL_MSG("MALLOC statistics BEGIN"); STXXL_MSG("==============================================================="); STXXL_MSG("non-mmapped space allocated from system (bytes): " << info.arena); STXXL_MSG("number of free chunks : " << info.ordblks); STXXL_MSG("number of fastbin blocks : " << info.smblks); STXXL_MSG("number of chunks allocated via mmap() : " << info.hblks); STXXL_MSG("total number of bytes allocated via mmap() : " << info.hblkhd); STXXL_MSG("maximum total allocated space (bytes) : " << info.usmblks); STXXL_MSG("space available in freed fastbin blocks (bytes): " << info.fsmblks); STXXL_MSG("number of bytes allocated and in use : " << info.uordblks); STXXL_MSG("number of bytes allocated but not in use : " << info.fordblks); STXXL_MSG("top-most, releasable (via malloc_trim) space : " << info.keepcost); STXXL_MSG("================================================================"); } int do_mallinfo(int argc, char* argv[]) { // parse command line stxxl::cmdline_parser cp; cp.set_description("Allocate some memory and mlock() it to consume physical memory. " "Needs to run as root to block more than 64 KiB in default settings." ); stxxl::internal_size_type M; cp.add_param_bytes("size", "Amount of memory to allocate (e.g. 1GiB)", M); if (!cp.process(argc, argv)) return -1; sbrk(128 * 1024 * 1024); std::cout << "Nothing allocated" << std::endl; print_malloc_stats(); char* ptr = new char[M]; std::cout << "Allocated " << M << " bytes" << std::endl; print_malloc_stats(); memset(ptr, 42, M); std::cout << "Filled " << M << " bytes" << std::endl; print_malloc_stats(); delete[] ptr; std::cout << "Deallocated " << std::endl; print_malloc_stats(); return 0; } #else // !STXXL_HAVE_MALLINFO_PROTO int do_mallinfo(int, char*[]) { STXXL_MSG("Sorry, mallinfo() statistics are not supported on this platform."); return -1; } #endif // STXXL_HAVE_MALLINFO_PROTO stxxl-1.4.1/tools/stxxl_tool.cpp000644 001411 000144 00000012067 12411366426 016573 0ustar00tbusers000000 000000 /*************************************************************************** * tools/stxxl_tool.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2007, 2009-2011 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include int stxxl_info(int, char**) { stxxl::config::get_instance(); stxxl::block_manager::get_instance(); stxxl::stats::get_instance(); stxxl::disk_queues::get_instance(); #if STXXL_PARALLEL STXXL_MSG("STXXL_PARALLEL, max threads = " << omp_get_max_threads()); #endif STXXL_MSG("sizeof(unsigned int) = " << sizeof(unsigned int)); STXXL_MSG("sizeof(unsigned_type) = " << sizeof(stxxl::unsigned_type)); STXXL_MSG("sizeof(uint64) = " << sizeof(stxxl::uint64)); STXXL_MSG("sizeof(long) = " << sizeof(long)); STXXL_MSG("sizeof(size_t) = " << sizeof(size_t)); STXXL_MSG("sizeof(off_t) = " << sizeof(off_t)); STXXL_MSG("sizeof(void*) = " << sizeof(void*)); #if defined(STXXL_HAVE_LINUXAIO_FILE) STXXL_MSG("STXXL_HAVE_LINUXAIO_FILE = " << STXXL_HAVE_LINUXAIO_FILE); #endif return 0; } extern int create_files(int argc, char* argv[]); extern int benchmark_disks(int argc, char* argv[]); extern int benchmark_files(int argc, char* argv[]); extern int benchmark_sort(int argc, char* argv[]); extern int benchmark_disks_random(int argc, char* argv[]); extern int benchmark_pqueue(int argc, char* argv[]); extern int do_mlock(int argc, char* argv[]); extern int do_mallinfo(int argc, char* argv[]); struct SubTool { const char* name; int (* func)(int argc, char* argv[]); bool shortline; const char* description; }; struct SubTool subtools[] = { { "info", &stxxl_info, false, "Print out information about the build system and which optional " "modules where compiled into STXXL." }, { "create_files", &create_files, false, "Precreate large files to keep file system allocation time out to measurements." }, { "benchmark_disks", &benchmark_disks, false, "This program will benchmark the disks configured by the standard " ".stxxl disk configuration files mechanism." }, { "benchmark_files", &benchmark_files, false, "Benchmark different file access methods, e.g. syscall or mmap_files." }, { "benchmark_sort", &benchmark_sort, false, "Run benchmark tests of different sorting methods in STXXL" }, { "benchmark_disks_random", &benchmark_disks_random, false, "Benchmark random block access time to .stxxl configured disks." }, { "benchmark_pqueue", &benchmark_pqueue, false, "Benchmark priority queue implementation using sequence of operations." }, { "mlock", &do_mlock, true, "Lock physical memory." }, { "mallinfo", &do_mallinfo, true, "Show mallinfo statistics." }, { NULL, NULL, false, NULL } }; int main_usage(const char* arg0) { STXXL_MSG(stxxl::get_version_string_long()); std::cout << "Usage: " << arg0 << " ..." << std::endl << "Available subtools: " << std::endl; int shortlen = 0; for (unsigned int i = 0; subtools[i].name; ++i) { if (!subtools[i].shortline) continue; shortlen = std::max(shortlen, (int)strlen(subtools[i].name)); } for (unsigned int i = 0; subtools[i].name; ++i) { if (subtools[i].shortline) continue; std::cout << " " << subtools[i].name << std::endl; stxxl::cmdline_parser::output_wrap(std::cout, subtools[i].description, 80, 6, 6); std::cout << std::endl; } for (unsigned int i = 0; subtools[i].name; ++i) { if (!subtools[i].shortline) continue; std::cout << " " << std::left << std::setw(shortlen + 2) << subtools[i].name << subtools[i].description << std::endl; } std::cout << std::endl; return 0; } int main(int argc, char** argv) { char progsub[256]; if (stxxl::check_library_version() != 0) STXXL_ERRMSG("version mismatch between headers and library"); if (argc > 1) { for (unsigned int i = 0; subtools[i].name; ++i) { if (strcmp(subtools[i].name, argv[1]) == 0) { // replace argv[1] with call string of subtool. snprintf(progsub, sizeof(progsub), "%s %s", argv[0], argv[1]); argv[1] = progsub; return subtools[i].func(argc - 1, argv + 1); } } std::cout << "Unknown subtool '" << argv[1] << "'" << std::endl; } return main_usage(argv[0]); } stxxl-1.4.1/tools/benchmark_sort.cpp000644 001411 000144 00000014457 12411366426 017362 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmark_sort.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /* * This program will benchmark the different sorting methods provided by STXXL * using three different data types: first a pair of 32-bit uints, "then a pair * 64-bit uint and then a larger structure of 64 bytes. */ #include #include #include #include #include #include #include using stxxl::timestamp; using stxxl::uint32; using stxxl::uint64; using stxxl::unsigned_type; #define MB (1024 * 1024) // pair of uint32 = 8 bytes typedef stxxl::tuple pair32_type; // pair of uint64 = 16 bytes typedef stxxl::tuple pair64_type; // larger struct of 64 bytes struct struct64_type : public pair64_type { char junk[16 + 32]; struct64_type() { } struct64_type(const pair64_type& pt) : pair64_type(pt) { } }; // construct a simple sorting benchmark for the value type template class BenchmarkSort { typedef ValueType value_type; struct value_less { bool operator () (const value_type& a, const value_type& b) const { return a.first < b.first; } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; struct value_key_second { typedef typename value_type::second_type key_type; key_type operator () (const value_type& p) const { return p.second; } static value_type min_value() { return value_type::min_value(); } static value_type max_value() { return value_type::max_value(); } }; struct random_stream { typedef ValueType value_type; RandomGenerator m_rng; value_type m_value; stxxl::uint64 m_counter; random_stream(stxxl::uint64 size) : m_counter(size) { m_value.first = m_rng(); m_value.second = m_rng(); } const value_type& operator * () const { return m_value; } random_stream& operator ++ () { assert(m_counter > 0); --m_counter; m_value.first = m_rng(); m_value.second = m_rng(); return *this; } bool empty() const { return (m_counter == 0); } }; static void output_result(double elapsed, uint64 vec_size) { std::cout << "finished in " << elapsed << " seconds @ " << ((double)vec_size * sizeof(value_type) / MB / elapsed) << " MiB/s" << std::endl; } public: BenchmarkSort(const char* desc, uint64 length, unsigned_type memsize) { // construct vector typedef typename stxxl::VECTOR_GENERATOR::result vector_type; uint64 vec_size = stxxl::div_ceil(length, sizeof(ValueType)); vector_type vec(vec_size); // construct random stream std::cout << "#!!! running sorting test with " << desc << " = " << sizeof(ValueType) << " bytes." << std::endl; { std::cout << "# materialize random_stream into vector of size " << vec.size() << std::endl; double ts1 = timestamp(); random_stream rs(vec_size); stxxl::stream::materialize(rs, vec.begin(), vec.end()); double elapsed = timestamp() - ts1; output_result(elapsed, vec_size); } { std::cout << "# stxxl::sort vector of size " << vec.size() << std::endl; double ts1 = timestamp(); stxxl::sort(vec.begin(), vec.end(), value_less(), memsize); double elapsed = timestamp() - ts1; output_result(elapsed, vec_size); } { std::cout << "# stxxl::ksort vector of size " << vec.size() << std::endl; double ts1 = timestamp(); stxxl::ksort(vec.begin(), vec.end(), value_key_second(), memsize); double elapsed = timestamp() - ts1; output_result(elapsed, vec_size); } vec.clear(); { std::cout << "# stxxl::stream::sort of size " << vec_size << std::endl; double ts1 = timestamp(); typedef stxxl::stream::sort random_stream_sort_type; random_stream stream(vec_size); random_stream_sort_type stream_sort(stream, value_less(), memsize); stxxl::stream::discard(stream_sort); double elapsed = timestamp() - ts1; output_result(elapsed, vec_size); } std::cout << std::endl; } }; // run sorting benchmark for the three types defined above. int benchmark_sort(int argc, char* argv[]) { // parse command line stxxl::cmdline_parser cp; cp.set_description("This program will benchmark the different sorting methods provided " "by STXXL using three different data types: first a pair of 32-bit uints, " "then a pair 64-bit uint and then a larger structure of 64 bytes." ); cp.set_author("Timo Bingmann "); uint64 length = 0; cp.add_param_bytes("size", "Amount of data to sort (e.g. 1GiB)", length); unsigned_type memsize = 256 * MB; cp.add_bytes('M', "ram", "Amount of RAM to use when sorting, default: 256 MiB", memsize); if (!cp.process(argc, argv)) return -1; BenchmarkSort ("pair of uint32", length, (unsigned_type)memsize); BenchmarkSort ("pair of uint64", length, (unsigned_type)memsize); BenchmarkSort ("struct of 64 bytes", length, (unsigned_type)memsize); return 0; } stxxl-1.4.1/tools/mlock.cpp000644 001411 000144 00000004014 12411366426 015452 0ustar00tbusers000000 000000 /*************************************************************************** * tools/mlock.cpp * * Allocate some memory and mlock() it to consume physical memory. * Needs to run as root to block more than 64 KiB in default settings. * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #if STXXL_HAVE_MLOCK_PROTO #include #include #include #include #include #include #include int do_mlock(int argc, char* argv[]) { // parse command line stxxl::cmdline_parser cp; cp.set_description("Allocate some memory and mlock() it to consume physical memory. " "Needs to run as root to block more than 64 KiB in default settings." ); cp.set_author("Andreas Beckmann "); stxxl::unsigned_type M; cp.add_param_bytes("size", "Amount of memory to allocate (e.g. 4GiB)", M); if (!cp.process(argc, argv)) return -1; // allocate and fill char* c = (char*)malloc(M); memset(c, 42, M); if (mlock(c, M) == 0) { std::cout << "mlock(" << (void*)c << ", " << M << ") successful, press Ctrl-C to exit." << std::endl; while (1) sleep(86400); } else { std::cerr << "mlock(" << (void*)c << ", " << M << ") failed: " << strerror(errno) << std::endl; return 1; } } #else // !STXXL_HAVE_MLOCK_PROTO int do_mlock(int, char*[]) { STXXL_MSG("Sorry, mlock() is not supported on this platform."); return -1; } #endif // STXXL_HAVE_MLOCK_PROTO // vim: et:ts=4:sw=4 stxxl-1.4.1/tools/create_files.cpp000644 001411 000144 00000017060 12411366426 016777 0ustar00tbusers000000 000000 /*************************************************************************** * tools/create_files.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2007 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include #if !STXXL_WINDOWS #include #endif using stxxl::request_ptr; using stxxl::file; using stxxl::timestamp; using stxxl::unsigned_type; #ifdef BLOCK_ALIGN #undef BLOCK_ALIGN #endif #define BLOCK_ALIGN 4096 #define NOREAD //#define DO_ONLY_READ #define POLL_DELAY 1000 #define RAW_ACCESS //#define WATCH_TIMES #define CHECK_AFTER_READ 0 #ifdef WATCH_TIMES void watch_times(request_ptr reqs[], unsigned n, double* out) { bool* finished = new bool[n]; unsigned count = 0; unsigned i = 0; for (i = 0; i < n; i++) finished[i] = false; while (count != n) { usleep(POLL_DELAY); i = 0; for (i = 0; i < n; i++) { if (!finished[i]) if (reqs[i]->poll()) { finished[i] = true; out[i] = timestamp(); count++; } } } delete[] finished; } void out_stat(double start, double end, double* times, unsigned n, const std::vector& names) { for (unsigned i = 0; i < n; i++) { std::cout << i << " " << names[i] << " took " << 100. * (times[i] - start) / (end - start) << " %" << std::endl; } } #endif #define MB (1024 * 1024) int create_files(int argc, char* argv[]) { std::vector disks_arr; stxxl::uint64 offset = 0, length; stxxl::cmdline_parser cp; cp.add_param_bytes("filesize", "Number of bytes to write to files.", length); cp.add_param_stringlist("filename", "Paths to files to write.", disks_arr); if (!cp.process(argc, argv)) return -1; stxxl::uint64 endpos = offset + length; for (size_t i = 0; i < disks_arr.size(); ++i) { unlink(disks_arr[i].c_str()); std::cout << "# Add disk: " << disks_arr[i] << std::endl; } const size_t ndisks = disks_arr.size(); #if STXXL_WINDOWS unsigned_type buffer_size = 64 * MB; #else unsigned_type buffer_size = 256 * MB; #endif const unsigned_type buffer_size_int = buffer_size / sizeof(int); unsigned chunks = 2; const unsigned_type chunk_size = buffer_size / chunks; const unsigned_type chunk_size_int = chunk_size / sizeof(int); unsigned i = 0, j = 0; int* buffer = (int*)stxxl::aligned_alloc(buffer_size * ndisks); file** disks = new file*[ndisks]; request_ptr* reqs = new request_ptr[ndisks * chunks]; #ifdef WATCH_TIMES double* r_finish_times = new double[ndisks]; double* w_finish_times = new double[ndisks]; #endif for (i = 0; i < ndisks * buffer_size_int; i++) buffer[i] = i; for (i = 0; i < ndisks; i++) { #if STXXL_WINDOWS #ifdef RAW_ACCESS disks[i] = new stxxl::wincall_file(disks_arr[i], file::CREAT | file::RDWR | file::DIRECT, i); #else disks[i] = new stxxl::wincall_file(disks_arr[i], file::CREAT | file::RDWR, i); #endif #else #ifdef RAW_ACCESS disks[i] = new stxxl::syscall_file(disks_arr[i], file::CREAT | file::RDWR | file::DIRECT, i); #else disks[i] = new stxxl::syscall_file(disks_arr[i], file::CREAT | file::RDWR, i); #endif #endif } while (offset < endpos) { const unsigned_type current_block_size = length ? (unsigned_type)std::min(buffer_size, endpos - offset) : buffer_size; const unsigned_type current_chunk_size = current_block_size / chunks; std::cout << "Disk offset " << std::setw(7) << offset / MB << " MiB: " << std::fixed; double begin = timestamp(), end; #ifndef DO_ONLY_READ for (i = 0; i < ndisks; i++) { for (j = 0; j < chunks; j++) reqs[i * chunks + j] = disks[i]->awrite(buffer + buffer_size_int * i + j * chunk_size_int, offset + j * current_chunk_size, current_chunk_size); } #ifdef WATCH_TIMES watch_times(reqs, ndisks, w_finish_times); #else wait_all(reqs, ndisks * chunks); #endif end = timestamp(); #if 0 std::cout << "WRITE\nDisks: " << ndisks << " \nElapsed time: " << end - begin << " \nThroughput: " << int(double(buffer_size * ndisks) / MB / (end - begin)) << " MiB/s \nPer one disk:" << int((buffer_size) / MB / (end - begin)) << " MiB/s" << std::endl; #endif #ifdef WATCH_TIMES out_stat(begin, end, w_finish_times, ndisks, disks_arr); #endif std::cout << std::setw(7) << int(double(current_block_size) / MB / (end - begin)) << " MiB/s,"; #endif #ifndef NOREAD begin = timestamp(); for (i = 0; i < ndisks; i++) { for (j = 0; j < chunks; j++) reqs[i * chunks + j] = disks[i]->aread(buffer + buffer_size_int * i + j * chunk_size_int, offset + j * current_chunk_size, current_chunk_size); } #ifdef WATCH_TIMES watch_times(reqs, ndisks, r_finish_times); #else wait_all(reqs, ndisks * chunks); #endif end = timestamp(); #if 0 std::cout << "READ\nDisks: " << ndisks << " \nElapsed time: " << end - begin << " \nThroughput: " << int(double(buffer_size * ndisks) / MB / (end - begin)) << " MiB/s \nPer one disk:" << int(double(buffer_size) / MB / (end - begin)) << " MiB/s" << std::endl; #endif std::cout << int(double(current_block_size) / MB / (end - begin)) << " MiB/s" << std::endl; #ifdef WATCH_TIMES out_stat(begin, end, r_finish_times, ndisks, disks_arr); #endif if (CHECK_AFTER_READ) { for (int i = 0; unsigned(i) < ndisks * buffer_size_int; i++) { if (buffer[i] != i) { int ibuf = i / buffer_size_int; int pos = i % buffer_size_int; std::cout << "Error on disk " << ibuf << " position " << std::hex << std::setw(8) << offset + pos * sizeof(int) << " got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << i << std::dec << std::endl; i = (ibuf + 1) * buffer_size_int; // jump to next } } } #else std::cout << std::endl; #endif offset += current_block_size; } #ifdef WATCH_TIMES delete[] r_finish_times; delete[] w_finish_times; #endif delete[] reqs; for (i = 0; i < ndisks; i++) delete disks[i]; delete[] disks; stxxl::aligned_dealloc(buffer); return 0; } stxxl-1.4.1/tools/benchmark_disks_random.cpp000644 001411 000144 00000022055 12411366426 021041 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmark_disks_random.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Johannes Singler * Copyright (C) 2009 Andreas Beckmann * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /* example gnuplot command for the output of this program: (x-axis: offset in GiB, y-axis: bandwidth in MiB/s) plot \ "disk.log" using ($2/1024):($7) w l title "read", \ "disk.log" using ($2/1024):($4) w l title "write" */ #include #include #include #include #include #include using stxxl::request_ptr; using stxxl::timestamp; #define KiB (1024) #define MiB (1024 * 1024) struct print_number { int n; print_number(int n) : n(n) { } void operator () (stxxl::request_ptr) { //std::cout << n << " " << std::flush; } }; template void run_test(stxxl::int64 span, stxxl::int64 worksize, bool do_init, bool do_read, bool do_write) { const unsigned raw_block_size = BlockSize; typedef stxxl::typed_block block_type; typedef stxxl::BID BID_type; stxxl::unsigned_type num_blocks = (stxxl::unsigned_type)stxxl::div_ceil(worksize, raw_block_size); stxxl::unsigned_type num_blocks_in_span = (stxxl::unsigned_type)stxxl::div_ceil(span, raw_block_size); num_blocks = stxxl::STXXL_MIN(num_blocks, num_blocks_in_span); if (num_blocks == 0) num_blocks = num_blocks_in_span; worksize = num_blocks * raw_block_size; block_type* buffer = new block_type; request_ptr* reqs = new request_ptr[num_blocks_in_span]; std::vector blocks; //touch data, so it is actually allocated for (unsigned i = 0; i < block_type::size; ++i) (*buffer)[i] = i; try { AllocStrategy alloc; blocks.resize(num_blocks_in_span); stxxl::block_manager::get_instance()->new_blocks(alloc, blocks.begin(), blocks.end()); std::cout << "# Span size: " << stxxl::add_IEC_binary_multiplier(span, "B") << " (" << num_blocks_in_span << " blocks of " << stxxl::add_IEC_binary_multiplier(raw_block_size, "B") << ")" << std::endl; std::cout << "# Work size: " << stxxl::add_IEC_binary_multiplier(worksize, "B") << " (" << num_blocks << " blocks of " << stxxl::add_IEC_binary_multiplier(raw_block_size, "B") << ")" << std::endl; double begin, end, elapsed; if (do_init) { begin = timestamp(); std::cout << "First fill up space by writing sequentially..." << std::endl; for (unsigned j = 0; j < num_blocks_in_span; j++) reqs[j] = buffer->write(blocks[j]); wait_all(reqs, num_blocks_in_span); end = timestamp(); elapsed = end - begin; std::cout << "Written " << std::setw(12) << num_blocks_in_span << " blocks in " << std::fixed << std::setw(9) << std::setprecision(2) << elapsed << " seconds: " << std::setw(9) << std::setprecision(1) << (double(num_blocks_in_span) / elapsed) << " blocks/s " << std::setw(7) << std::setprecision(1) << (double(num_blocks_in_span * raw_block_size) / MiB / elapsed) << " MiB/s write " << std::endl; } std::cout << "Random block access..." << std::endl; srand((unsigned int)time(NULL)); std::random_shuffle(blocks.begin(), blocks.end()); begin = timestamp(); if (do_read) { for (unsigned j = 0; j < num_blocks; j++) reqs[j] = buffer->read(blocks[j], print_number(j)); wait_all(reqs, num_blocks); end = timestamp(); elapsed = end - begin; std::cout << "Read " << num_blocks << " blocks in " << std::fixed << std::setw(5) << std::setprecision(2) << elapsed << " seconds: " << std::setw(5) << std::setprecision(1) << (double(num_blocks) / elapsed) << " blocks/s " << std::setw(5) << std::setprecision(1) << (double(num_blocks * raw_block_size) / MiB / elapsed) << " MiB/s read" << std::endl; } std::random_shuffle(blocks.begin(), blocks.end()); begin = timestamp(); if (do_write) { for (unsigned j = 0; j < num_blocks; j++) reqs[j] = buffer->write(blocks[j], print_number(j)); wait_all(reqs, num_blocks); end = timestamp(); elapsed = end - begin; std::cout << "Written " << num_blocks << " blocks in " << std::fixed << std::setw(5) << std::setprecision(2) << elapsed << " seconds: " << std::setw(5) << std::setprecision(1) << (double(num_blocks) / elapsed) << " blocks/s " << std::setw(5) << std::setprecision(1) << (double(num_blocks * raw_block_size) / MiB / elapsed) << " MiB/s write " << std::endl; } } catch (const std::exception& ex) { std::cout << std::endl; STXXL_ERRMSG(ex.what()); } delete[] reqs; delete buffer; stxxl::block_manager::get_instance()->delete_blocks(blocks.begin(), blocks.end()); } template int benchmark_disks_random_alloc(stxxl::uint64 span, stxxl::uint64 block_size, stxxl::uint64 worksize, const std::string& optirw) { bool do_init = (optirw.find('i') != std::string::npos); bool do_read = (optirw.find('r') != std::string::npos); bool do_write = (optirw.find('w') != std::string::npos); #define run(bs) run_test(span, worksize, do_init, do_read, do_write) if (block_size == 4 * KiB) run(4 * KiB); else if (block_size == 8 * KiB) run(8 * KiB); else if (block_size == 16 * KiB) run(16 * KiB); else if (block_size == 32 * KiB) run(32 * KiB); else if (block_size == 64 * KiB) run(64 * KiB); else if (block_size == 128 * KiB) run(128 * KiB); else if (block_size == 256 * KiB) run(256 * KiB); else if (block_size == 512 * KiB) run(512 * KiB); else if (block_size == 1 * MiB) run(1 * MiB); else if (block_size == 2 * MiB) run(2 * MiB); else if (block_size == 4 * MiB) run(4 * MiB); else if (block_size == 8 * MiB) run(8 * MiB); else if (block_size == 16 * MiB) run(16 * MiB); else if (block_size == 32 * MiB) run(32 * MiB); else if (block_size == 64 * MiB) run(64 * MiB); else if (block_size == 128 * MiB) run(128 * MiB); else std::cerr << "Unsupported block_size " << block_size << "." << std::endl << "Available are only powers of two from 4 KiB to 128 MiB. You must use 'ki' instead of 'k'." << std::endl; #undef run return 0; } int benchmark_disks_random(int argc, char* argv[]) { // parse command line stxxl::cmdline_parser cp; stxxl::uint64 span, block_size = 8 * MiB, worksize = 0; std::string optirw = "irw", allocstr; cp.add_param_bytes("span", "Span of external memory to write/read to (e.g. 10GiB).", span); cp.add_opt_param_bytes("block_size", "Size of blocks to randomly write/read (default: 8MiB).", block_size); cp.add_opt_param_bytes("size", "Amount of data to operate on (e.g. 2GiB), default: whole span.", worksize); cp.add_opt_param_string("i|r|w", "Operations: [i]nitialize, [r]ead, and/or [w]rite (default: all).", optirw); cp.add_opt_param_string("alloc", "Block allocation strategy: RC, SR, FR, striping (default: RC).", allocstr); cp.set_description( "This program will benchmark _random_ block access on the disks " "configured by the standard .stxxl disk configuration files mechanism. " "Available block sizes are power of two from 4 KiB to 128 MiB. " "A set of three operations can be performed: sequential initialization, " "random reading and random writing." ); if (!cp.process(argc, argv)) return -1; #define run_alloc(alloc) benchmark_disks_random_alloc(span, block_size, worksize, optirw) if (allocstr.size()) { if (allocstr == "RC") return run_alloc(stxxl::RC); if (allocstr == "SR") return run_alloc(stxxl::SR); if (allocstr == "FR") return run_alloc(stxxl::FR); if (allocstr == "striping") return run_alloc(stxxl::striping); std::cout << "Unknown allocation strategy '" << allocstr << "'" << std::endl; cp.print_usage(); return -1; } return run_alloc(STXXL_DEFAULT_ALLOC_STRATEGY); #undef run_alloc } // vim: et:ts=4:sw=4 stxxl-1.4.1/tools/CMakeLists.txt000644 001411 000144 00000001400 12350112610 016357 0ustar00tbusers000000 000000 ############################################################################ # tools/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_tool(stxxl_tool create_files.cpp benchmark_disks.cpp benchmark_files.cpp benchmark_sort.cpp benchmark_disks_random.cpp benchmark_pqueue.cpp mlock.cpp mallinfo.cpp ) install(TARGETS stxxl_tool RUNTIME DESTINATION ${INSTALL_BIN_DIR}) add_subdirectory(benchmarks) add_subdirectory(extras) stxxl-1.4.1/tools/benchmark_files.cpp000644 001411 000144 00000040126 12411366426 017465 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmark_files.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2002-2003 Roman Dementiev * Copyright (C) 2007-2011 Andreas Beckmann * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /* example gnuplot command for the output of this program: (x-axis: file offset in GiB, y-axis: bandwidth in MiB/s) plot \ "file.log" using ($3/1024):($14) w l title "read", \ "file.log" using ($3/1024):($7) w l title "write" */ #include #include #include #include #include #include #include using stxxl::request_ptr; using stxxl::file; using stxxl::timestamp; using stxxl::unsigned_type; using stxxl::uint64; #ifdef BLOCK_ALIGN #undef BLOCK_ALIGN #endif #define BLOCK_ALIGN 4096 #define POLL_DELAY 1000 #if STXXL_WINDOWS const char* default_file_type = "wincall"; #else const char* default_file_type = "syscall"; #endif #ifdef WATCH_TIMES void watch_times(request_ptr reqs[], unsigned n, double* out) { bool* finished = new bool[n]; unsigned count = 0; for (unsigned i = 0; i < n; i++) finished[i] = false; while (count != n) { usleep(POLL_DELAY); unsigned i = 0; for (i = 0; i < n; i++) { if (!finished[i]) if (reqs[i]->poll()) { finished[i] = true; out[i] = timestamp(); count++; } } } delete[] finished; } void out_stat(double start, double end, double* times, unsigned n, const std::vector& names) { for (unsigned i = 0; i < n; i++) { std::cout << i << " " << names[i] << " took " << 100. * (times[i] - start) / (end - start) << " %" << std::endl; } } #endif #define MB (1024 * 1024) // returns throughput in MiB/s static inline double throughput(stxxl::int64 bytes, double seconds) { if (seconds == 0.0) return 0.0; return (double)bytes / (1024 * 1024) / seconds; } int benchmark_files(int argc, char* argv[]) { uint64 offset = 0, length = 0; bool no_direct_io = false; bool sync_io = false; bool resize_after_open = false; std::string file_type = default_file_type; unsigned_type block_size = 0; unsigned int batch_size = 1; std::string opstr = "wv"; unsigned pattern = 0; std::vector files_arr; stxxl::cmdline_parser cp; cp.add_param_bytes("length", "Length to write in file.", length); cp.add_param_stringlist("filename", "File path to run benchmark on.", files_arr); cp.add_bytes('o', "offset", "Starting offset to write in file.", offset); cp.add_flag(0, "no-direct", "open files without O_DIRECT", no_direct_io); cp.add_flag(0, "sync", "open files with O_SYNC|O_DSYNC|O_RSYNC", sync_io); cp.add_flag(0, "resize", "resize the file size after opening, needed e.g. for creating mmap files", resize_after_open); cp.add_bytes(0, "block_size", "block size for operations (default 8 MiB)", block_size); cp.add_uint(0, "batch_size", "increase (default 1) to submit several I/Os at once and report average rate", batch_size); cp.add_string('f', "file-type", "Method to open file (syscall|mmap|wincall|boostfd|...) default: " + file_type, file_type); cp.add_string('p', "operations", "[w]rite pattern, [r]ead without verification, read and [v]erify pattern (default: 'wv')", opstr); cp.add_uint(0, "pattern", "32-bit pattern to write (default: block index)", pattern); cp.set_description("Open a file using one of STXXL's file abstractions and perform write/read/verify tests on the file. " "Block sizes and batch size can be adjusted via command line. " "If length == 0 , then operation will continue till end of space (please ignore the write error). " "Memory consumption: block_size * batch_size * num_files"); if (!cp.process(argc, argv)) return -1; uint64 endpos = offset + length; if (block_size == 0) block_size = 8 * MB; if (batch_size == 0) batch_size = 1; bool do_read = false, do_write = false, do_verify = false; // deprecated, use --no-direct instead if (opstr.find("nd") != std::string::npos || opstr.find("ND") != std::string::npos) { no_direct_io = true; } if (opstr.find('r') != std::string::npos || opstr.find('R') != std::string::npos) { do_read = true; } if (opstr.find('v') != std::string::npos || opstr.find('V') != std::string::npos) { do_verify = true; } if (opstr.find('w') != std::string::npos || opstr.find('W') != std::string::npos) { do_write = true; } const char* myself = strrchr(argv[0], '/'); if (!myself || !*(++myself)) myself = argv[0]; std::cout << "# " << myself << " " << stxxl::get_version_string_long(); #if STXXL_DIRECT_IO_OFF std::cout << " STXXL_DIRECT_IO_OFF"; #endif std::cout << std::endl; for (size_t ii = 0; ii < files_arr.size(); ii++) { std::cout << "# Add file: " << files_arr[ii] << std::endl; } const size_t nfiles = files_arr.size(); bool verify_failed = false; const unsigned_type step_size = block_size * batch_size; const unsigned_type block_size_int = block_size / sizeof(int); const uint64 step_size_int = step_size / sizeof(int); unsigned* buffer = (unsigned*)stxxl::aligned_alloc(step_size * nfiles); file** files = new file*[nfiles]; request_ptr* reqs = new request_ptr[nfiles * batch_size]; #ifdef WATCH_TIMES double* r_finish_times = new double[nfiles]; double* w_finish_times = new double[nfiles]; #endif double totaltimeread = 0, totaltimewrite = 0; stxxl::int64 totalsizeread = 0, totalsizewrite = 0; // fill buffer with pattern for (unsigned i = 0; i < nfiles * step_size_int; i++) buffer[i] = (pattern ? pattern : i); // open files for (unsigned i = 0; i < nfiles; i++) { int openmode = file::CREAT | file::RDWR; if (!no_direct_io) { openmode |= file::DIRECT; } if (sync_io) { openmode |= file::SYNC; } files[i] = stxxl::create_file(file_type, files_arr[i], openmode, i); if (resize_after_open) files[i]->set_size(endpos); } std::cout << "# Step size: " << step_size << " bytes per file (" << batch_size << " block" << (batch_size == 1 ? "" : "s") << " of " << block_size << " bytes)" << " file_type=" << file_type << " O_DIRECT=" << (no_direct_io ? "no" : "yes") << " O_SYNC=" << (sync_io ? "yes" : "no") << std::endl; stxxl::timer t_total(true); try { while (offset + uint64(step_size) <= endpos || length == 0) { const uint64 current_step_size = (length == 0) ? stxxl::int64(step_size) : std::min(step_size, endpos - offset); const uint64 current_step_size_int = current_step_size / sizeof(int); const unsigned_type current_num_blocks = (unsigned_type)stxxl::div_ceil(current_step_size, block_size); std::cout << "File offset " << std::setw(8) << offset / MB << " MiB: " << std::fixed; double begin = timestamp(), end = begin, elapsed; if (do_write) { // write block number (512 byte blocks) into each block at position 42 * sizeof(unsigned) for (uint64 j = 42, b = offset >> 9; j < current_step_size_int; j += 512 / sizeof(unsigned), ++b) { for (unsigned i = 0; i < nfiles; i++) buffer[current_step_size_int * i + j] = (unsigned int)b; } for (unsigned i = 0; i < nfiles; i++) { for (unsigned_type j = 0; j < current_num_blocks; j++) reqs[i * current_num_blocks + j] = files[i]->awrite(buffer + current_step_size_int * i + j * block_size_int, offset + j * block_size, (unsigned_type)block_size); } #ifdef WATCH_TIMES watch_times(reqs, nfiles, w_finish_times); #else wait_all(reqs, nfiles * current_num_blocks); #endif end = timestamp(); elapsed = end - begin; totalsizewrite += current_step_size; totaltimewrite += elapsed; } else { elapsed = 0.0; } #if 0 std::cout << "# WRITE\nFiles: " << nfiles << " \nElapsed time: " << end - begin << " \nThroughput: " << int(double(current_step_size * nfiles) / MB / (end - begin)) << " MiB/s \nPer one file:" << int(double(current_step_size) / MB / (end - begin)) << " MiB/s" << std::endl; #endif #ifdef WATCH_TIMES out_stat(begin, end, w_finish_times, nfiles, files_arr); #endif std::cout << std::setw(2) << nfiles << " * " << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed)) << " = " << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed) * (double)nfiles) << " MiB/s write,"; begin = end = timestamp(); if (do_read || do_verify) { for (unsigned i = 0; i < nfiles; i++) { for (unsigned j = 0; j < current_num_blocks; j++) reqs[i * current_num_blocks + j] = files[i]->aread(buffer + current_step_size_int * i + j * block_size_int, offset + j * block_size, (unsigned_type)block_size); } #ifdef WATCH_TIMES watch_times(reqs, nfiles, r_finish_times); #else wait_all(reqs, nfiles * current_num_blocks); #endif end = timestamp(); elapsed = end - begin; totalsizeread += current_step_size; totaltimeread += elapsed; } else { elapsed = 0.0; } #if 0 std::cout << "# READ\nFiles: " << nfiles << " \nElapsed time: " << end - begin << " \nThroughput: " << int(double(current_step_size * nfiles) / MB / (end - begin)) << " MiB/s \nPer one file:" << int(double(current_step_size) / MB / (end - begin)) << " MiB/s" << std::endl; #endif std::cout << std::setw(2) << nfiles << " * " << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed)) << " = " << std::setw(8) << std::setprecision(3) << (throughput(current_step_size, elapsed) * (double)nfiles) << " MiB/s read"; #ifdef WATCH_TIMES out_stat(begin, end, r_finish_times, nfiles, files_arr); #endif if (do_verify) { for (unsigned d = 0; d < nfiles; ++d) { for (unsigned s = 0; s < (current_step_size >> 9); ++s) { uint64 i = d * current_step_size_int + s * (512 / sizeof(unsigned)) + 42; uint64 b = (offset >> 9) + s; if (buffer[i] != b) { verify_failed = true; std::cout << "Error on file " << d << " sector " << std::hex << std::setw(8) << b << " got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << b << std::dec << std::endl; } buffer[i] = (pattern ? pattern : (unsigned int)i); } } for (uint64 i = 0; i < nfiles * current_step_size_int; i++) { if (buffer[i] != (pattern ? pattern : i)) { stxxl::int64 ibuf = i / current_step_size_int; uint64 pos = i % current_step_size_int; std::cout << std::endl << "Error on file " << ibuf << " position " << std::hex << std::setw(8) << offset + pos * sizeof(int) << " got: " << std::hex << std::setw(8) << buffer[i] << " wanted: " << std::hex << std::setw(8) << i << std::dec << std::endl; i = (ibuf + 1) * current_step_size_int; // jump to next verify_failed = true; } } } std::cout << std::endl; offset += current_step_size; } } catch (const std::exception& ex) { std::cout << std::endl; STXXL_ERRMSG(ex.what()); } t_total.stop(); std::cout << "=============================================================================================" << std::endl; // the following line of output is parsed by misc/filebench-avgplot.sh std::cout << "# Average over " << std::setw(8) << stxxl::STXXL_MAX(totalsizewrite, totalsizeread) / MB << " MiB: "; std::cout << std::setw(2) << nfiles << " * " << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite, totaltimewrite)) << " = " << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite, totaltimewrite) * (double)nfiles) << " MiB/s write,"; std::cout << std::setw(2) << nfiles << " * " << std::setw(8) << std::setprecision(3) << (throughput(totalsizeread, totaltimeread)) << " = " << std::setw(8) << std::setprecision(3) << (throughput(totalsizeread, totaltimeread) * (double)nfiles) << " MiB/s read" << std::endl; if (totaltimewrite != 0.0) std::cout << "# Write time " << std::setw(8) << std::setprecision(3) << totaltimewrite << " s" << std::endl; if (totaltimeread != 0.0) std::cout << "# Read time " << std::setw(8) << std::setprecision(3) << totaltimeread << " s" << std::endl; std::cout << "# Non-I/O time " << std::setw(8) << std::setprecision(3) << (t_total.seconds() - totaltimewrite - totaltimeread) << " s, average throughput " << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds() - totaltimewrite - totaltimeread) * (double)nfiles) << " MiB/s" << std::endl; std::cout << "# Total time " << std::setw(8) << std::setprecision(3) << t_total.seconds() << " s, average throughput " << std::setw(8) << std::setprecision(3) << (throughput(totalsizewrite + totalsizeread, t_total.seconds()) * (double)nfiles) << " MiB/s" << std::endl; if (do_verify) { std::cout << "# Verify: " << (verify_failed ? "FAILED." : "all okay.") << std::endl; } #ifdef WATCH_TIMES delete[] r_finish_times; delete[] w_finish_times; #endif delete[] reqs; for (unsigned i = 0; i < nfiles; i++) delete files[i]; delete[] files; stxxl::aligned_dealloc(buffer); return (verify_failed ? 1 : 0); } // vim: et:ts=4:sw=4 stxxl-1.4.1/tools/benchmarks/pq_benchmark.cpp000644 001411 000144 00000014744 12405375303 021124 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmarks/pq_benchmark.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/pq_benchmark.cpp //! This is a benchmark mentioned in the paper //! R. Dementiev, L. Kettner, P. Sanders "STXXL: standard template library for XXL data sets" //! Software: Practice and Experience //! Volume 38, Issue 6, Pages 589-637, May 2008 //! DOI: 10.1002/spe.844 #include #include #include #include #define TOTAL_PQ_MEM_SIZE (768 * 1024 * 1024) #define PQ_MEM_SIZE (512 * 1024 * 1024) #define PREFETCH_POOL_SIZE ((TOTAL_PQ_MEM_SIZE - PQ_MEM_SIZE) / 2) #define WRITE_POOL_SIZE (PREFETCH_POOL_SIZE) #define MAX_ELEMENTS (2000 * 1024 * 1024) struct my_record { int key; int data; my_record() : key(0), data(0) { } my_record(int k, int d) : key(k), data(d) { } }; std::ostream& operator << (std::ostream& o, const my_record& obj) { o << obj.key << " " << obj.data; return o; } bool operator == (const my_record& a, const my_record& b) { return a.key == b.key; } bool operator != (const my_record& a, const my_record& b) { return a.key != b.key; } bool operator < (const my_record& a, const my_record& b) { return a.key < b.key; } bool operator > (const my_record& a, const my_record& b) { return a.key > b.key; } struct comp_type : std::binary_function { bool operator () (const my_record& a, const my_record& b) const { return a > b; } static my_record min_value() { return my_record(std::numeric_limits::max(), 0); } }; typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pq_type; typedef pq_type::block_type block_type; #define BLOCK_SIZE block_type::raw_size #if 1 unsigned ran32State = 0xdeadbeef; inline int myrand() { return ((int)((ran32State = 1664525 * ran32State + 1013904223) >> 1)) - 1; } #else // a longer pseudo random sequence long long unsigned ran32State = 0xdeadbeef; inline long long unsigned myrand() { return (ran32State = (ran32State * 0x5DEECE66DULL + 0xBULL) & 0xFFFFFFFFFFFFULL); } #endif void run_stxxl_insert_all_delete_all(stxxl::uint64 ops) { pq_type PQ(PREFETCH_POOL_SIZE, WRITE_POOL_SIZE); stxxl::uint64 i; my_record cur; stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); stxxl::timer Timer; Timer.start(); for (i = 0; i < ops; ++i) { cur.key = myrand(); PQ.push(cur); } Timer.stop(); STXXL_MSG("Records in PQ: " << PQ.size()); if (i != PQ.size()) { STXXL_MSG("Size does not match"); abort(); } STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; //////////////////////////////////////////////// stats_begin = *stxxl::stats::get_instance(); Timer.reset(); Timer.start(); for (i = 0; i < ops; ++i) { PQ.pop(); } Timer.stop(); STXXL_MSG("Records in PQ: " << PQ.size()); if (!PQ.empty()) { STXXL_MSG("PQ must be empty"); abort(); } STXXL_MSG("Deletions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; } void run_stxxl_intermixed(stxxl::uint64 ops) { pq_type PQ(PREFETCH_POOL_SIZE, WRITE_POOL_SIZE); stxxl::uint64 i; my_record cur; stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); stxxl::timer Timer; Timer.start(); for (i = 0; i < ops; ++i) { cur.key = myrand(); PQ.push(cur); } Timer.stop(); STXXL_MSG("Records in PQ: " << PQ.size()); if (i != PQ.size()) { STXXL_MSG("Size does not match"); abort(); } STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; //////////////////////////////////////////////// stats_begin = *stxxl::stats::get_instance(); Timer.reset(); Timer.start(); for (i = 0; i < ops; ++i) { int o = myrand() % 3; if (o == 0) { cur.key = myrand(); PQ.push(cur); } else { assert(!PQ.empty()); PQ.pop(); } } Timer.stop(); STXXL_MSG("Records in PQ: " << PQ.size()); STXXL_MSG("Deletions/Insertion elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; } int main(int argc, char* argv[]) { STXXL_MSG("stxxl::pq lock size: " << BLOCK_SIZE << " bytes"); #if STXXL_DIRECT_IO_OFF STXXL_MSG("STXXL_DIRECT_IO_OFF is defined"); #else STXXL_MSG("STXXL_DIRECT_IO_OFF is NOT defined"); #endif if (argc < 3) { STXXL_MSG("Usage: " << argv[0] << " version #ops"); STXXL_MSG("\t version = 1: insert-all-delete-all stxxl pq"); STXXL_MSG("\t version = 2: intermixed insert/delete stxxl pq"); return -1; } int version = atoi(argv[1]); stxxl::uint64 ops = stxxl::atouint64(argv[2]); STXXL_MSG("Running version : " << version); STXXL_MSG("Operations to perform: " << ops); switch (version) { case 1: run_stxxl_insert_all_delete_all(ops); break; case 2: run_stxxl_intermixed(ops); break; default: STXXL_MSG("Unsupported version " << version); } } stxxl-1.4.1/tools/benchmarks/monotonic_pq.cpp000644 001411 000144 00000031715 12411366426 021177 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmarks/monotonic_pq.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2003 Roman Dementiev * Copyright (C) 2007, 2009 Johannes Singler * Copyright (C) 2008, 2009 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL 1 #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL 1 #define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER 1 #define TINY_PQ 0 #define MANUAL_PQ 0 #define SIDE_PQ 1 // compare with second, in-memory PQ (needs a lot of memory) #include #include #include const stxxl::unsigned_type mega = 1024 * 1024; #define RECORD_SIZE 16 #define LOAD 0 typedef stxxl::uint64 my_key_type; #define MAGIC 123 struct my_type { typedef my_key_type key_type; key_type key; #if LOAD key_type load; char data[RECORD_SIZE - 2 * sizeof(key_type)]; #else char data[RECORD_SIZE - sizeof(key_type)]; #endif my_type() { } my_type(key_type k) : key(k) { } #if LOAD my_type(key_type k, key_type l) : key(k), load(l) { } #endif void operator = (const key_type& k) { key = k; } #if LOAD void operator = (const my_type& mt) { key = mt.key; load = mt.load; } bool operator == (const my_type& mt) { return (key == mt.key) && (load = mt.load); } #else void operator = (const my_type& mt) { key = mt.key; } bool operator == (const my_type& mt) { return key == mt.key; } #endif }; std::ostream& operator << (std::ostream& o, const my_type& obj) { o << obj.key; #if LOAD o << "/" << obj.load; #endif return o; } //STXXL priority queue is a _maximum_ PQ. "Greater" comparator makes this a "minimum" PQ again. struct my_cmp /*: public std::binary_function*/ // greater { typedef my_type first_argument_type; typedef my_type second_argument_type; typedef bool result_type; bool operator () (const my_type& a, const my_type& b) const { return a.key > b.key; } my_type min_value() const { #if LOAD return my_type(std::numeric_limits::max(), MAGIC); #else return my_type(std::numeric_limits::max()); #endif } my_type max_value() const { #if LOAD return my_type(std::numeric_limits::min(), MAGIC); #else return my_type(std::numeric_limits::min()); #endif } }; int main(int argc, char* argv[]) { if (argc < 3) { std::cout << "Usage: " << argv[0] << " [n in MiB]" #if defined(STXXL_PARALLEL) << " [p threads]" #endif << std::endl; return -1; } STXXL_MSG("----------------------------------------"); stxxl::config::get_instance(); std::string Flags = std::string("") #if STXXL_CHECK_ORDER_IN_SORTS + " STXXL_CHECK_ORDER_IN_SORTS" #endif #ifdef NDEBUG + " NDEBUG" #endif #if TINY_PQ + " TINY_PQ" #endif #if MANUAL_PQ + " MANUAL_PQ" #endif #if SIDE_PQ + " SIDE_PQ" #endif #if STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + " STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL" #endif #if STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL + " STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL" #endif #if STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER + " STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER" #endif ; STXXL_MSG("Flags:" << Flags); unsigned long megabytes = atoi(argv[1]); #if defined(STXXL_PARALLEL_MODE) int num_threads = atoi(argv[2]); STXXL_MSG("Threads: " << num_threads); omp_set_num_threads(num_threads); __gnu_parallel::_Settings parallel_settings(__gnu_parallel::_Settings::get()); parallel_settings.sort_algorithm = __gnu_parallel::QS_BALANCED; parallel_settings.sort_splitting = __gnu_parallel::SAMPLING; parallel_settings.sort_minimal_n = 1000; parallel_settings.sort_mwms_oversampling = 10; parallel_settings.merge_splitting = __gnu_parallel::SAMPLING; parallel_settings.merge_minimal_n = 1000; parallel_settings.merge_oversampling = 10; parallel_settings.multiway_merge_algorithm = __gnu_parallel::LOSER_TREE; parallel_settings.multiway_merge_splitting = __gnu_parallel::EXACT; parallel_settings.multiway_merge_oversampling = 10; parallel_settings.multiway_merge_minimal_n = 1000; parallel_settings.multiway_merge_minimal_k = 2; __gnu_parallel::_Settings::set(parallel_settings); #endif const stxxl::unsigned_type mem_for_queue = 512 * mega; const stxxl::unsigned_type mem_for_pools = 512 * mega; #if TINY_PQ stxxl::STXXL_UNUSED(mem_for_queue); const unsigned BufferSize1 = 32; // equalize procedure call overheads etc. const unsigned N = (1 << 9) / sizeof(my_type); // minimal sequence length const unsigned IntKMAX = 8; // maximal arity for internal mergersq const unsigned IntLevels = 2; // number of internal levels const unsigned BlockSize = (4 * mega); const unsigned ExtKMAX = 8; // maximal arity for external mergers const unsigned ExtLevels = 2; // number of external levels typedef stxxl::priority_queue< stxxl::priority_queue_config< my_type, my_cmp, BufferSize1, N, IntKMAX, IntLevels, BlockSize, ExtKMAX, ExtLevels > > pq_type; #elif MANUAL_PQ stxxl::STXXL_UNUSED(mem_for_queue); const unsigned BufferSize1 = 32; // equalize procedure call overheads etc. const unsigned N = (1 << 20) / sizeof(my_type); // minimal sequence length const unsigned IntKMAX = 16; // maximal arity for internal mergersq const unsigned IntLevels = 2; // number of internal levels const unsigned BlockSize = (4 * mega); const unsigned ExtKMAX = 32; // maximal arity for external mergers const unsigned ExtLevels = 2; // number of external levels typedef stxxl::priority_queue< stxxl::priority_queue_config< my_type, my_cmp, BufferSize1, N, IntKMAX, IntLevels, BlockSize, ExtKMAX, ExtLevels > > pq_type; #else const stxxl::uint64 volume = stxxl::uint64(200000) * mega; // in bytes typedef stxxl::PRIORITY_QUEUE_GENERATOR gen; typedef gen::result pq_type; // BufferSize1 = Config::BufferSize1, // N = Config::N, // IntKMAX = Config::IntKMAX, // IntLevels = Config::IntLevels, // ExtLevels = Config::ExtLevels, // Levels = Config::IntLevels + Config::ExtLevels, // BlockSize = Config::BlockSize, // ExtKMAX = Config::ExtKMAX /* STXXL_MSG ( "Blocks fitting into internal memory m: "<, my_cmp> side_pq; #endif my_type side_pq_least; STXXL_MSG("op-sequence(monotonic pq): ( push, pop, push ) * n"); for (i = 0; i < nelements; ++i) { if ((i % mega) == 0) STXXL_MSG( std::fixed << std::setprecision(2) << std::setw(5) << (100.0 * (double)i / (double)nelements) << "% " << "Inserting element " << i << " top() == " << least.key << " @ " << std::setprecision(3) << Timer.seconds() << " s" << std::setprecision(6) << std::resetiosflags(std::ios_base::floatfield)); //monotone priority queue r = least.key + rand() % modulo; sum_input += r; p.push(my_type(r)); #if SIDE_PQ side_pq.push(my_type(r)); #endif least = p.top(); sum_output += least.key; p.pop(); #if SIDE_PQ side_pq_least = side_pq.top(); side_pq.pop(); if (!(side_pq_least == least)) STXXL_MSG("Wrong result at " << i << " " << side_pq_least.key << " != " << least.key); #endif if (cmp(last_least, least)) { STXXL_MSG("Wrong order at " << i << " " << last_least.key << " > " << least.key); } else last_least = least; r = least.key + rand() % modulo; sum_input += r; p.push(my_type(r)); #if SIDE_PQ side_pq.push(my_type(r)); #endif } Timer.stop(); STXXL_MSG("Time spent for filling: " << Timer.seconds() << " s"); STXXL_MSG("Internal memory consumption of the priority queue: " << p.mem_cons() << " B"); stxxl::stats_data sd_middle(*stxxl::stats::get_instance()); std::cout << sd_middle - sd_start; Timer.reset(); Timer.start(); STXXL_MSG("op-sequence(monotonic pq): ( pop, push, pop ) * n"); for (i = 0; i < (nelements); ++i) { assert(!p.empty()); least = p.top(); sum_output += least.key; p.pop(); #if SIDE_PQ side_pq_least = side_pq.top(); side_pq.pop(); if (!(side_pq_least == least)) { STXXL_VERBOSE1("" << side_pq_least << " != " << least); } #endif if (cmp(last_least, least)) { STXXL_MSG("Wrong result at " << i << " " << last_least.key << " > " << least.key); } else last_least = least; r = least.key + rand() % modulo; sum_input += r; p.push(my_type(r)); #if SIDE_PQ side_pq.push(my_type(r)); #endif least = p.top(); sum_output += least.key; p.pop(); #if SIDE_PQ side_pq_least = side_pq.top(); side_pq.pop(); if (!(side_pq_least == least)) { STXXL_VERBOSE1("" << side_pq_least << " != " << least); } #endif if (cmp(last_least, least)) { STXXL_MSG("Wrong result at " << i << " " << last_least.key << " > " << least.key); } else last_least = least; if ((i % mega) == 0) STXXL_MSG( std::fixed << std::setprecision(2) << std::setw(5) << (100.0 * (double)i / (double)nelements) << "% " << "Popped element " << i << " == " << least.key << " @ " << std::setprecision(3) << Timer.seconds() << " s" << std::setprecision(6) << std::resetiosflags(std::ios_base::floatfield)); } STXXL_MSG("Last element " << i << " popped"); Timer.stop(); if (sum_input != sum_output) STXXL_MSG("WRONG sum! " << sum_input << " - " << sum_output << " = " << (sum_output - sum_input) << " / " << (sum_input - sum_output)); STXXL_MSG("Time spent for removing elements: " << Timer.seconds() << " s"); STXXL_MSG("Internal memory consumption of the priority queue: " << p.mem_cons() << " B"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - sd_middle; std::cout << *stxxl::stats::get_instance(); assert(sum_input == sum_output); } stxxl-1.4.1/tools/benchmarks/tpie_stack_benchmark.cpp000644 001411 000144 00000011235 12405375303 022622 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmarks/tpie_stack_benchmark.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/tpie_stack_benchmark.cpp //! This is a benchmark mentioned in the paper //! R. Dementiev, L. Kettner, P. Sanders "STXXL: standard template library for XXL data sets" //! Software: Practice and Experience //! Volume 38, Issue 6, Pages 589-637, May 2008 //! DOI: 10.1002/spe.844 #include "app_config.h" #include #include // Get the AMI_stack definition. #include // Utilities for ASCII output. #include #include #include #include #define MEM_2_RESERVE (768 * 1024 * 1024) #define BLOCK_SIZE (2 * 1024 * 1024) #ifndef DISKS #define DISKS 1 #endif template struct my_record_ { char data[RECORD_SIZE]; my_record_() { } }; template void run_stack(stxxl::int64 volume) { typedef AMI_stack stack_type; MM_manager.set_memory_limit(BLOCK_SIZE * DISKS * 8); STXXL_MSG("Record size: " << sizeof(my_record) << " bytes"); stack_type Stack; stxxl::int64 ops = volume / sizeof(my_record); stxxl::int64 i; my_record cur; stxxl::timer Timer; Timer.start(); for (i = 0; i < ops; ++i) { Stack.push(cur); } Timer.stop(); STXXL_MSG("Records in Stack: " << Stack.stream_len()); if (i != Stack.stream_len()) { STXXL_MSG("Size does not match"); abort(); } STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(volume) / (1024. * 1024. * Timer.mseconds() / 1000.)) << " MiB/s"); //////////////////////////////////////////////// Timer.reset(); Timer.start(); my_record* out; for (i = 0; i < ops; ++i) { Stack.pop(&out); } Timer.stop(); STXXL_MSG("Records in Stack: " << Stack.stream_len()); if (Stack.stream_len() != 0) { STXXL_MSG("Stack must be empty"); abort(); } STXXL_MSG("Deletions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(volume) / (1024. * 1024. * Timer.mseconds() / 1000.)) << " MiB/s"); } int main(int argc, char* argv[]) { using std::cout; using std::endl; #ifdef BTE_COLLECTION_IMP_MMAP cout << "BTE_COLLECTION_IMP_MMAP is defined" << endl; #endif #ifdef BTE_COLLECTION_IMP_UFS cout << "BTE_COLLECTION_IMP_UFS is defined" << endl; #endif #ifdef BTE_STREAM_IMP_UFS cout << "BTE_STREAM_IMP_UFS is defined" << endl; cout << "BTE_STREAM_UFS_BLOCK_FACTOR is " << BTE_STREAM_UFS_BLOCK_FACTOR << endl; cout << "Actual block size is " << (TPIE_OS_BLOCKSIZE() * BTE_STREAM_UFS_BLOCK_FACTOR / 1024) << " KiB" << endl; #endif #ifdef BTE_STREAM_IMP_MMAP cout << "BTE_STREAM_IMP_MMAP is defined" << endl; cout << "BTE_STREAM_MMAP_BLOCK_FACTOR is " << BTE_STREAM_MMAP_BLOCK_FACTOR << endl; cout << "Actual block size is " << (TPIE_OS_BLOCKSIZE() * BTE_STREAM_MMAP_BLOCK_FACTOR / 1024) << " KiB" << endl; #endif #ifdef BTE_STREAM_IMP_STDIO cout << "BTE_STREAM_IMP_STDIO is defined" << endl; #endif cout << "TPIE_OS_BLOCKSIZE() is " << TPIE_OS_BLOCKSIZE() << endl; if (argc < 3) { STXXL_MSG("Usage: " << argv[0] << " version #volume"); STXXL_MSG("\t version = 1: TPIE stack with 4 byte records"); STXXL_MSG("\t version = 2: TPIE stack with 32 byte records"); return -1; } int version = atoi(argv[1]); stxxl::uint64 volume = stxxl::atouint64(argv[2]); STXXL_MSG("Allocating array with size " << MEM_2_RESERVE << " bytes to prevent file buffering."); //int * array = new int[MEM_2_RESERVE/sizeof(int)]; int* array = (int*)malloc(MEM_2_RESERVE); std::fill(array, array + (MEM_2_RESERVE / sizeof(int)), 0); STXXL_MSG("Running version: " << version); STXXL_MSG("Data volume : " << volume << " bytes"); switch (version) { case 1: run_stack >(volume); break; case 2: run_stack >(volume); break; default: STXXL_MSG("Unsupported version " << version); } //delete [] array; free(array); } stxxl-1.4.1/tools/benchmarks/stack_benchmark.cpp000644 001411 000144 00000014211 12405375303 021576 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmarks/stack_benchmark.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/stack_benchmark.cpp //! This is a benchmark mentioned in the paper //! R. Dementiev, L. Kettner, P. Sanders "STXXL: standard template library for XXL data sets" //! Software: Practice and Experience //! Volume 38, Issue 6, Pages 589-637, May 2008 //! DOI: 10.1002/spe.844 #include #include #include #define MEM_2_RESERVE (768 * 1024 * 1024) #ifndef BLOCK_SIZE #define BLOCK_SIZE (2 * 1024 * 1024) #endif #ifndef DISKS #define DISKS 4 #endif template struct my_record_ { char data[RECORD_SIZE]; my_record_() { memset(data, 0, sizeof(data)); } }; template inline std::ostream& operator << (std::ostream& o, const my_record_&) { o << "."; return o; } template void benchmark_insert(stack_type& Stack, stxxl::int64 volume) { typedef typename stack_type::value_type value_type; STXXL_MSG("Record size: " << sizeof(value_type) << " bytes"); stxxl::int64 ops = volume / sizeof(value_type); value_type cur; // test whether top() returns an lvalue Stack.push(cur); Stack.top() = cur; Stack.pop(); stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); stxxl::timer Timer; Timer.start(); for (stxxl::int64 i = 0; i < ops; ++i) { Stack.push(cur); } Timer.stop(); STXXL_MSG("Records in Stack: " << Stack.size()); if (ops != stxxl::int64(Stack.size())) { STXXL_MSG("Size does not match"); abort(); } STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(volume) / (1024. * 1024. * Timer.mseconds() / 1000.)) << " MiB/s"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; } template void benchmark_delete(stack_type& Stack, stxxl::int64 volume) { typedef typename stack_type::value_type value_type; stxxl::int64 ops = volume / sizeof(value_type); value_type cur; stxxl::stats_data stats_begin(*stxxl::stats::get_instance()); stxxl::timer Timer; Timer.start(); for (stxxl::int64 i = 0; i < ops; ++i) { Stack.pop(); } Timer.stop(); STXXL_MSG("Records in Stack: " << Stack.size()); if (!Stack.empty()) { STXXL_MSG("Stack must be empty"); abort(); } STXXL_MSG("Deletions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(volume) / (1024. * 1024. * Timer.mseconds() / 1000.)) << " MiB/s"); std::cout << stxxl::stats_data(*stxxl::stats::get_instance()) - stats_begin; } template void run_stxxl_growshrink2_stack(stxxl::int64 volume) { typedef typename stxxl::STACK_GENERATOR::result stack_type; typedef typename stack_type::block_type block_type; stxxl::read_write_pool pool(DISKS * 4, DISKS * 4); stack_type Stack(pool); benchmark_insert(Stack, volume); Stack.set_prefetch_aggr(DISKS * 8); benchmark_delete(Stack, volume); } template void run_stxxl_normal_stack(stxxl::int64 volume) { typedef typename stxxl::STACK_GENERATOR::result stack_type; stack_type Stack; benchmark_insert(Stack, volume); benchmark_delete(Stack, volume); } template void run_stl_stack(stxxl::int64 volume) { typedef std::stack stack_type; stack_type Stack; benchmark_insert(Stack, volume); benchmark_delete(Stack, volume); } int main(int argc, char* argv[]) { STXXL_MSG("stxxl::pq block size: " << BLOCK_SIZE << " bytes"); #if STXXL_DIRECT_IO_OFF STXXL_MSG("STXXL_DIRECT_IO_OFF is defined"); #else STXXL_MSG("STXXL_DIRECT_IO_OFF is NOT defined"); #endif if (argc < 3) { STXXL_MSG("Usage: " << argv[0] << " variant volume"); STXXL_MSG("\t variant = 1: grow-shrink-stack2 with 4 byte records"); STXXL_MSG("\t variant = 2: grow-shrink-stack2 with 32 byte records"); STXXL_MSG("\t variant = 3: normal-stack with 4 byte records"); STXXL_MSG("\t variant = 4: normal-stack with 32 byte records"); STXXL_MSG("\t variant = 5: std::stack with 4 byte records"); STXXL_MSG("\t variant = 6: std::stack with 32 byte records"); STXXL_MSG("\t volume: in bytes"); return -1; } int variant = atoi(argv[1]); stxxl::uint64 volume = stxxl::atouint64(argv[2]); STXXL_MSG("Allocating array with size " << MEM_2_RESERVE << " bytes to prevent file buffering."); int* array = new int[MEM_2_RESERVE / sizeof(int)]; std::fill(array, array + (MEM_2_RESERVE / sizeof(int)), 0); STXXL_MSG("Running variant: " << variant); STXXL_MSG("Data volume : " << volume << " bytes"); switch (variant) { case 1: run_stxxl_growshrink2_stack >(volume); break; case 2: run_stxxl_growshrink2_stack >(volume); break; case 3: run_stxxl_normal_stack >(volume); break; case 4: run_stxxl_normal_stack >(volume); break; case 5: run_stl_stack >(volume); break; case 6: run_stl_stack >(volume); break; default: STXXL_MSG("Unsupported variant " << variant); } delete[] array; } stxxl-1.4.1/tools/benchmarks/benchmark_naive_matrix.cpp000644 001411 000144 00000005410 12405375303 023160 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmarks/benchmark_naive_matrix.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010 Johannes Singler * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/test_matrix.cpp //! This is an example of how to represent a 2D matrix by using an \c stxxl::vector. //! DISCLAIMER: The approach is very basic and the matrix multiplication is NOT I/O-EFFICIENT. #include #include template class matrix2d { stxxl::vector v; stxxl::unsigned_type width, height; public: matrix2d(stxxl::unsigned_type width, stxxl::unsigned_type height) : width(width), height(height) { v.resize(width * height); } stxxl::unsigned_type get_width() const { return width; } stxxl::unsigned_type get_height() const { return height; } T & element(stxxl::unsigned_type x, stxxl::unsigned_type y) { //row-major return v[y * width + x]; } const T & const_element(stxxl::unsigned_type x, stxxl::unsigned_type y) const { //row-major return v[y * width + x]; } }; template void matrix_multiply(const matrix2d& a, const matrix2d& b, matrix2d& c) { assert(a.get_width() == b.get_height()); assert(b.get_height() == c.get_height()); assert(b.get_width() == c.get_width()); for (stxxl::unsigned_type y = 0; y < c.get_height(); ++y) for (stxxl::unsigned_type x = 0; x < c.get_width(); ++x) { c.element(x, y) = 0; for (stxxl::unsigned_type t = 0; t < a.get_width(); ++t) c.element(x, y) += a.const_element(t, y) * b.const_element(x, t); } } int main() { const stxxl::unsigned_type width = 416, height = 416; try { matrix2d a(width, height), b(width, height), c(width, height); for (stxxl::unsigned_type y = 0; y < height; ++y) for (stxxl::unsigned_type x = 0; x < width; ++x) { a.element(x, y) = 1.0; b.element(x, y) = 1.0; c.element(x, y) = 1.0; } matrix_multiply(a, b, c); std::cout << c.const_element(0, 0) << std::endl; std::cout << c.const_element(width - 1, height - 1) << std::endl; } catch (const std::exception& ex) { STXXL_MSG("Caught exception: " << ex.what()); } catch (...) { STXXL_MSG("Caught unknown exception."); } return 0; } stxxl-1.4.1/tools/benchmarks/matrix_benchmark.cpp000644 001411 000144 00000015243 12411366426 022006 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmarks/matrix_benchmark.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2010-2011 Raoul Steffen * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ #include #include #include #include #include #include using stxxl::uint64; using stxxl::unsigned_type; using stxxl::int_type; using stxxl::internal_size_type; int main(int argc, char** argv) { #ifndef STXXL_MATRIX_BLOCK_ORDER const int block_order = 1568; // must be a multiple of 32, assuming at least 4 bytes element size #else const int block_order = STXXL_MATRIX_BLOCK_ORDER; // must be a multiple of 32, assuming at least 4 bytes element size #endif int rank = 10000; internal_size_type internal_memory = 256 * 1024 * 1024; int mult_algo_num = 5; int sched_algo_num = 2; stxxl::cmdline_parser cp; cp.add_int('r', "rank", "N", "rank of the matrices, default: 10000", rank); cp.add_bytes('m', "memory", "L", "internal memory to use, default: 256 MiB", internal_memory); cp.add_int('a', "mult-algo", "N", "use multiplication-algorithm number N\n available are:\n 0: naive_multiply_and_add\n 1: recursive_multiply_and_add\n 2: strassen_winograd_multiply_and_add\n 3: multi_level_strassen_winograd_multiply_and_add\n 4: strassen_winograd_multiply (block-interleaved pre- and postadditions)\n 5: strassen_winograd_multiply_and_add_interleaved (block-interleaved preadditions)\n 6: multi_level_strassen_winograd_multiply_and_add_block_grained\n -1: internal multiplication\n -2: pure BLAS\n default: 5", mult_algo_num); cp.add_int('s', "scheduling-algo", "N", "use scheduling-algorithm number N\n available are:\n 0: online LRU\n 1: offline LFD\n 2: offline LRU prefetching\n default: 2", sched_algo_num); cp.set_description("stxxl matrix test"); cp.set_author("Raoul Steffen "); if (!cp.process(argc, argv)) return -1; STXXL_MSG("multiplying two full double matrices of rank " << rank << ", block order " << block_order << " using " << internal_memory / 1024 / 1024 << "MiB internal memory, multiplication-algo " << mult_algo_num << ", scheduling-algo " << sched_algo_num); typedef double value_type; stxxl::stats_data stats_before, stats_after; stxxl::matrix_operation_statistic_data matrix_stats_before, matrix_stats_after; if (mult_algo_num == -2) { const int_type size = rank * rank; value_type* A = new value_type[size]; value_type* B = new value_type[size]; value_type* C = new value_type[size]; // write A and B for (int_type i = 0; i < size; ++i) A[i] = B[i] = 1; // evict A and B by accessing lots of memory int_type int_mem_size = 50 * 2 ^ 30 / sizeof(int_type); assert(int_mem_size > 0); int_type* D = new int_type[int_mem_size]; for (int_type i = 0; i < int_mem_size; ++i) D[i] = 1; delete[] D; #if STXXL_BLAS stats_before = *stxxl::stats::get_instance(); gemm_wrapper(rank, rank, rank, value_type(1), false, A, false, B, value_type(0), false, C); stats_after = *stxxl::stats::get_instance(); #else STXXL_ERRMSG("internal multiplication is only available for testing with blas"); #endif delete[] A; delete[] B; delete[] C; } else { typedef stxxl::block_scheduler > bst; typedef stxxl::matrix mt; typedef mt::row_major_iterator mitt; typedef mt::const_row_major_iterator cmitt; bst* b_s = new bst(internal_memory); // the block_scheduler may use internal_memory byte for caching bst& bs = *b_s; mt* a = new mt(bs, rank, rank), * b = new mt(bs, rank, rank), * c = new mt(bs, rank, rank); STXXL_MSG("writing input matrices"); for (mitt mit = a->begin(); mit != a->end(); ++mit) *mit = 1; for (mitt mit = b->begin(); mit != b->end(); ++mit) *mit = 1; bs.flush(); STXXL_MSG("start of multiplication"); matrix_stats_before.set(); stats_before = *stxxl::stats::get_instance(); if (mult_algo_num >= 0) *c = a->multiply(*b, mult_algo_num, sched_algo_num); else *c = a->multiply_internal(*b, sched_algo_num); bs.flush(); stats_after = *stxxl::stats::get_instance(); matrix_stats_after.set(); STXXL_MSG("end of multiplication"); matrix_stats_after = matrix_stats_after - matrix_stats_before; STXXL_MSG(matrix_stats_after); stats_after = stats_after - stats_before; STXXL_MSG(stats_after); { int_type num_err = 0; for (cmitt mit = c->cbegin(); mit != c->cend(); ++mit) num_err += (*mit != rank); if (num_err) STXXL_ERRMSG("c had " << num_err << " errors"); } delete a; delete b; delete c; delete b_s; } STXXL_MSG("end of test"); std::cout << "@"; std::cout << " ra " << rank << " bo " << block_order << " im " << internal_memory / 1024 / 1024 << " ma " << mult_algo_num << " sa " << sched_algo_num; std::cout << " mu " << matrix_stats_after.block_multiplication_calls << " mus " << matrix_stats_after.block_multiplications_saved_through_zero << " ad " << matrix_stats_after.block_addition_calls << " ads " << matrix_stats_after.block_additions_saved_through_zero; std::cout << " t " << stats_after.get_elapsed_time() << " r " << stats_after.get_reads() << " w " << stats_after.get_writes() << " rt " << stats_after.get_read_time() << " rtp " << stats_after.get_pread_time() << " wt " << stats_after.get_write_time() << " wtp " << stats_after.get_pwrite_time() << " rw " << stats_after.get_wait_read_time() << " ww " << stats_after.get_wait_write_time() << " iotp " << stats_after.get_pio_time(); std::cout << std::endl; return 0; } stxxl-1.4.1/tools/benchmarks/berkeley_db_benchmark.cpp000644 001411 000144 00000107400 12411366426 022746 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmarks/berkeley_db_benchmark.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2006 Roman Dementiev * Copyright (C) 2009, 2010 Andreas Beckmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ //! \example containers/berkeley_db_benchmark.cpp //! This is a benchmark mentioned in the paper //! R. Dementiev, L. Kettner, P. Sanders "STXXL: standard template library for XXL data sets" //! Software: Practice and Experience //! Volume 38, Issue 6, Pages 589-637, May 2008 //! DOI: 10.1002/spe.844 #include #include #include #include ///// BDB header //////////// #include ///////// TPIE headers ////////// #include "app_config.h" #include #include //////////////////////////// #define BDB_BULK_SCAN #define KEY_SIZE 8 #define DATA_SIZE 32 #define NODE_BLOCK_SIZE (32 * 1024) #define LEAF_BLOCK_SIZE (32 * 1024) #define LEAF_BLOCK_SIZE (32 * 1024) #define TOTAL_CACHE_SIZE (750 * 1024 * 1024) //#define TOTAL_CACHE_SIZE (150 * 1024 * 1024) //#define NODE_CACHE_SIZE (1 * (TOTAL_CACHE_SIZE / 40)) //#define LEAF_CACHE_SIZE (39 * (TOTAL_CACHE_SIZE / 40)) #define NODE_CACHE_SIZE (64 * 1024 * 1024) #define LEAF_CACHE_SIZE (TOTAL_CACHE_SIZE - NODE_CACHE_SIZE) #define SORTER_MEM (TOTAL_CACHE_SIZE - 1024 * 1024 * 2 * 4) #define SCAN_LIMIT(x) (x) //#define BDB_FILE "/data3/bdb_file" #define BDB_FILE "/var/tmp/bdb_file" // BDB settings u_int32_t pagesize = LEAF_BLOCK_SIZE; u_int bulkbufsize = 4 * 1024 * 1024; u_int logbufsize = 8 * 1024 * 1024; u_int cachesize = (TOTAL_CACHE_SIZE < 500 * 1024 * 1024) ? (4 * (TOTAL_CACHE_SIZE / 5)) : (TOTAL_CACHE_SIZE - 100 * 1024 * 1024); u_int datasize = DATA_SIZE; u_int keysize = KEY_SIZE; u_int numitems = 0; const char* letters = "abcdefghijklmnopqrstuvwxuz"; struct my_key { char keybuf[KEY_SIZE]; }; std::ostream& operator << (std::ostream& o, const my_key& obj) { for (int i = 0; i < KEY_SIZE; ++i) o << obj.keybuf[i]; return o; } bool operator == (const my_key& a, const my_key& b) { return strncmp(a.keybuf, b.keybuf, KEY_SIZE) == 0; } bool operator != (const my_key& a, const my_key& b) { return strncmp(a.keybuf, b.keybuf, KEY_SIZE) != 0; } bool operator < (const my_key& a, const my_key& b) { return strncmp(a.keybuf, b.keybuf, KEY_SIZE) < 0; } bool operator > (const my_key& a, const my_key& b) { return strncmp(a.keybuf, b.keybuf, KEY_SIZE) > 0; } bool operator <= (const my_key& a, const my_key& b) { return strncmp(a.keybuf, b.keybuf, KEY_SIZE) <= 0; } bool operator >= (const my_key& a, const my_key& b) { return strncmp(a.keybuf, b.keybuf, KEY_SIZE) >= 0; } struct my_data { char databuf[DATA_SIZE]; }; std::ostream& operator << (std::ostream& o, const my_data& obj) { o << "DATA(size=" << sizeof(obj) << ") "; return o; } my_key min_key, max_key; struct comp_type : std::binary_function { bool operator () (const my_key& a, const my_key& b) const { return strncmp(a.keybuf, b.keybuf, KEY_SIZE) < 0; } static my_key max_value() { return max_key; } static my_key min_value() { return min_key; } }; /// TPIE declarations // Key type. typedef my_key bkey_t; // Element type for the btree. struct el_t { bkey_t key_; my_data data_; el_t(bkey_t k) : key_(k) { } el_t() { } }; struct key_from_el { bkey_t operator () (const el_t& v) const { return v.key_; } }; // Temporary distinction btw UN*X and WIN, since there are some // problems with the MMAP collection implementation. #ifdef _WIN32 typedef AMI_btree, key_from_el, BTE_COLLECTION_UFS> u_btree_t; #else typedef AMI_btree, key_from_el> u_btree_t; #endif void init() { memset(max_key.keybuf, std::numeric_limits::max(), KEY_SIZE); memset(min_key.keybuf, std::numeric_limits::min(), KEY_SIZE); } typedef stxxl::map map_type; #define REAL_NODE_BLOCK_SIZE map_type::node_block_type::raw_size #define REAL_LEAF_BLOCK_SIZE map_type::leaf_block_type::raw_size #define REAL_NODE_MELEMENTS map_type::node_block_type::size #define REAL_LEAF_MELEMENTS map_type::leaf_block_type::size typedef stxxl::VECTOR_GENERATOR, 1, 1>::result vector_type; //typedef stxxl::vector,1,stxxl::lru_pager<1>,512*1024> vector_type; //#define KEYPOS (i % KEY_SIZE) //#define VALUE (myrand() % 26) #if 0 unsigned ran32State = 0xdeadbeef; inline unsigned myrand() { return (ran32State = 1664525 * ran32State + 1013904223); } inline void rand_key(stxxl::int64 pos, my_key& Key) { for (int i = 0; i < KEY_SIZE; ++i) Key.keybuf[i] = letters[myrand() % 26]; } #else // a longer pseudo random sequence long long unsigned ran32State = 0xdeadbeef; inline long long unsigned myrand() { return (ran32State = (ran32State * 0x5DEECE66DULL + 0xBULL) & 0xFFFFFFFFFFFFULL); } inline void rand_key(stxxl::int64 /*pos*/, my_key& Key) { long long unsigned r = myrand(); for (int i = 0; i < KEY_SIZE; ++i) { Key.keybuf[i] = letters[r % 26]; r >>= 5; } } #endif void run_bdb_btree(stxxl::int64 ops) { const char* filename = BDB_FILE; my_key key1_storage; my_data data1_storage; unlink(filename); memset(key1_storage.keybuf, 'a', KEY_SIZE); memset(data1_storage.databuf, 'b', DATA_SIZE); Db db(NULL, 0); // Instantiate the Db object try { db.set_errfile(stderr); db.set_pagesize(pagesize); db.set_cachesize(0, cachesize, 1); // Open the database db.open(NULL, // Transaction pointer filename, // Database file name NULL, // Optional logical database name DB_BTREE, // Database access method DB_CREATE, // Open flags 0); // File mode (using defaults) // here we start with the tests Dbt key1(key1_storage.keybuf, KEY_SIZE); Dbt data1(data1_storage.databuf, DATA_SIZE); stxxl::timer Timer; stxxl::int64 n_inserts = ops, n_locates = ops, n_range_queries = ops, n_deletes = ops; stxxl::int64 i; //comp_type cmp_; ran32State = 0xdeadbeef; DB_BTREE_STAT* dbstat; db.stat(NULL, &dbstat, 0); STXXL_MSG("Records in map: " << dbstat->bt_ndata); db.get_env()->memp_stat(NULL, NULL, DB_STAT_CLEAR); Timer.start(); for (i = 0; i < n_inserts; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); db.put(NULL, &key1, &data1, DB_NOOVERWRITE); } Timer.stop(); db.stat(NULL, &dbstat, 0); STXXL_MSG("Records in map: " << dbstat->bt_ndata); STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_inserts) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); db.get_env()->memp_stat_print(DB_STAT_CLEAR); ///////////////////////////////////////// Timer.reset(); Timer.start(); Dbc* cursorp; db.cursor(NULL, &cursorp, 0); for (i = 0; i < n_locates; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); Dbt keyx(key1_storage.keybuf, KEY_SIZE); Dbt datax(data1_storage.databuf, DATA_SIZE); cursorp->get(&keyx, &datax, DB_SET_RANGE); } Timer.stop(); STXXL_MSG("Locates elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); db.get_env()->memp_stat_print(DB_STAT_CLEAR); //////////////////////////////////// Timer.reset(); Timer.start(); stxxl::int64 n_scanned = 0; for (i = 0; i < n_range_queries; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); my_key last_key = key1_storage; //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); if (last_key < key1_storage) std::swap(last_key, key1_storage); Dbt keyx(key1_storage.keybuf, KEY_SIZE); Dbt datax(data1_storage.databuf, DATA_SIZE); if (cursorp->get(&keyx, &datax, DB_SET_RANGE) == DB_NOTFOUND) continue; while (*((my_key*)keyx.get_data()) <= last_key) { ++n_scanned; if (cursorp->get(&keyx, &datax, DB_NEXT) == DB_NOTFOUND) break; } if (n_scanned >= 10 * n_range_queries) { ++i; break; } } n_range_queries = i; Timer.stop(); if (cursorp != NULL) cursorp->close(); STXXL_MSG("Range query elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_scanned) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec, #queries " << n_range_queries << " #scanned elements: " << n_scanned); db.get_env()->memp_stat_print(DB_STAT_CLEAR); ////////////////////////////////////// ran32State = 0xdeadbeef; memset(key1_storage.keybuf, 'a', KEY_SIZE); Timer.reset(); Timer.start(); for (i = 0; i < n_deletes; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); Dbt keyx(key1_storage.keybuf, KEY_SIZE); db.del(NULL, &keyx, 0); } Timer.stop(); db.stat(NULL, &dbstat, 0); STXXL_MSG("Records in map: " << dbstat->bt_ndata); STXXL_MSG("Erase elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); db.get_env()->memp_stat_print(DB_STAT_CLEAR); db.close(0); } catch (DbException& e) { STXXL_ERRMSG("DbException happened"); } catch (std::exception& e) { STXXL_ERRMSG("std::exception happened"); } unlink(filename); } void run_stxxl_map(stxxl::int64 ops) { map_type Map(NODE_CACHE_SIZE, LEAF_CACHE_SIZE); Map.enable_prefetching(); stxxl::stats* Stats = stxxl::stats::get_instance(); std::pair element; memset(element.first.keybuf, 'a', KEY_SIZE); memset(element.second.databuf, 'b', DATA_SIZE); stxxl::timer Timer; stxxl::int64 n_inserts = ops, n_locates = ops, n_range_queries = ops, n_deletes = ops; stxxl::int64 i; //comp_type cmp_; ran32State = 0xdeadbeef; //stxxl::random_number32 myrand; STXXL_MSG("Records in map: " << Map.size()); stxxl::stats_data stats_begin(*Stats); Timer.start(); for (i = 0; i < n_inserts; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); Map.insert(element); } Timer.stop(); STXXL_MSG("Records in map: " << Map.size()); STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_inserts) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); std::cout << (stxxl::stats_data(*Stats) - stats_begin); //////////////////////////////////////////////// const map_type& CMap(Map); // const map reference stats_begin = stxxl::stats_data(*Stats); Timer.reset(); Timer.start(); for (i = 0; i < n_locates; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); CMap.lower_bound(element.first); } Timer.stop(); STXXL_MSG("Locates elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_locates) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); std::cout << (stxxl::stats_data(*Stats) - stats_begin); //////////////////////////////////// stats_begin = stxxl::stats_data(*Stats); Timer.reset(); Timer.start(); stxxl::int64 n_scanned = 0; //, skipped_qieries = 0; for (i = 0; i < n_range_queries; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); my_key begin_key = element.first; map_type::const_iterator begin = CMap.lower_bound(element.first); //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); map_type::const_iterator beyond = CMap.lower_bound(element.first); if (element.first < begin_key) std::swap(begin, beyond); while (begin != beyond) { my_data tmp = begin->second; stxxl::STXXL_UNUSED(tmp); ++n_scanned; ++begin; } if (n_scanned >= 10 * n_range_queries) { ++i; break; } } n_range_queries = i; Timer.stop(); STXXL_MSG("Range query elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_scanned) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec, #queries " << n_range_queries << " #scanned elements: " << n_scanned); std::cout << (stxxl::stats_data(*Stats) - stats_begin); ////////////////////////////////////// ran32State = 0xdeadbeef; memset(element.first.keybuf, 'a', KEY_SIZE); memset(element.second.databuf, 'b', DATA_SIZE); stats_begin = stxxl::stats_data(*Stats); Timer.reset(); Timer.start(); for (i = 0; i < n_deletes; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); Map.erase(element.first); } Timer.stop(); STXXL_MSG("Records in map: " << Map.size()); STXXL_MSG("Erase elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_deletes) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); std::cout << (stxxl::stats_data(*Stats) - stats_begin); } class rand_key_gen { stxxl::int64 counter; my_key& current; stxxl::random_number32 myrand; rand_key_gen(); public: typedef my_key value_type; rand_key_gen(stxxl::int64 el, my_key& cur) : counter(el), current(cur) { //const stxxl::int64 & i = counter; //current.keybuf[KEYPOS] = letters[VALUE]; rand_key(counter, current); } const my_key& operator * () { return current; } const my_key* operator -> () { return ¤t; } rand_key_gen& operator ++ () { --counter; //const stxxl::int64 & i = counter; //current.keybuf[KEYPOS] = letters[VALUE]; rand_key(counter, current); return *this; } bool empty() const { return counter == 0; } }; template class key2pair { InputType& in; std::pair current; key2pair(); public: typedef std::pair value_type; key2pair(InputType& in_) : in(in_) { if (!in.empty()) current.first = *in; } const value_type& operator * () { return current; } const value_type* operator -> () { return ¤t; } key2pair& operator ++ () { ++in; if (!empty()) current.first = *in; return *this; } bool empty() const { return in.empty(); } }; void run_stxxl_map_big(stxxl::int64 n, unsigned ops) { stxxl::stats* Stats = stxxl::stats::get_instance(); std::pair element; memset(element.first.keybuf, 'a', KEY_SIZE); memset(element.second.databuf, 'b', DATA_SIZE); stxxl::timer Timer; stxxl::int64 n_inserts = ops, n_locates = ops, n_range_queries = ops, n_deletes = ops; stxxl::int64 i; //comp_type cmp_; ran32State = 0xdeadbeef; //stxxl::random_number32 myrand; Timer.start(); vector_type SortedSeq(n); const vector_type& CSortedSeq(SortedSeq); { rand_key_gen Gen(n, element.first); typedef stxxl::stream::sort sorter_type; sorter_type Sorter(Gen, comp_type(), SORTER_MEM); typedef key2pair key2pair_type; key2pair_type Key2Pair(Sorter); stxxl::stream::materialize(Key2Pair, SortedSeq.begin()); } Timer.stop(); STXXL_MSG("Finished sorting input. Elapsed time: " << (Timer.mseconds() / 1000.) << " seconds."); stxxl::stats_data stats_begin(*Stats); Timer.reset(); Timer.start(); // bulk construction map_type Map(CSortedSeq.begin(), CSortedSeq.end(), NODE_CACHE_SIZE, LEAF_CACHE_SIZE, true); Timer.stop(); STXXL_MSG("Records in map: " << Map.size()); STXXL_MSG("Construction elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); Map.print_statistics(cout); Map.reset_statistics(); std::cout << (stxxl::stats_data(*Stats) - stats_begin); //////////////////////////////////////// Map.disable_prefetching(); stats_begin = stxxl::stats_data(*Stats); Timer.reset(); Timer.start(); for (i = 0; i < n_inserts; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); Map.insert(element); } Timer.stop(); STXXL_MSG("Records in map: " << Map.size()); STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_inserts) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); Map.print_statistics(cout); Map.reset_statistics(); std::cout << (stxxl::stats_data(*Stats) - stats_begin); //////////////////////////////////// const map_type& CMap(Map); // const map reference Timer.reset(); Timer.start(); for (i = 0; i < n_locates; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); CMap.lower_bound(element.first); } Timer.stop(); STXXL_MSG("Locates elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); Map.print_statistics(cout); Map.reset_statistics(); std::cout << (stxxl::stats_data(*Stats) - stats_begin); //////////////////////////////////// Map.enable_prefetching(); stats_begin = stxxl::stats_data(*Stats); Timer.reset(); Timer.start(); stxxl::int64 n_scanned = 0; //, skipped_qieries = 0; for (i = 0; i < n_range_queries; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); my_key begin_key = element.first; map_type::const_iterator begin = CMap.lower_bound(element.first); //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); map_type::const_iterator beyond = CMap.lower_bound(element.first); if (element.first < begin_key) std::swap(begin, beyond); /* STXXL_MSG("Looking "<begin_key)?element.first:begin_key)); }*/ while (begin != beyond) { ++n_scanned; ++begin; } if (n_scanned >= SCAN_LIMIT(n)) { ++i; break; } } n_range_queries = i; Timer.stop(); STXXL_MSG("Range query elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_scanned) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec, #queries " << n_range_queries << " #scanned elements: " << n_scanned); Map.print_statistics(cout); Map.reset_statistics(); std::cout << (stxxl::stats_data(*Stats) - stats_begin); ////////////////////////////////////// ran32State = 0xdeadbeef; memset(element.first.keybuf, 'a', KEY_SIZE); memset(element.second.databuf, 'b', DATA_SIZE); Map.disable_prefetching(); stats_begin = stxxl::stats_data(*Stats); Timer.reset(); Timer.start(); for (i = n_deletes; i > 0; --i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.first); Map.erase(element.first); } Timer.stop(); STXXL_MSG("Records in map: " << Map.size()); STXXL_MSG("Erase elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); Map.print_statistics(cout); Map.reset_statistics(); std::cout << (stxxl::stats_data(*Stats) - stats_begin); } ///////////////////////////////////////////////////////////////////////// typedef AMI_STREAM stream_t; char dummy; class MyFilter { public: bool operator () (const el_t& v) const { dummy += v.key_.keybuf[0]; // touch element return true; } }; void run_tpie_btree_big(stxxl::int64 n, unsigned ops) { el_t element; memset(element.key_.keybuf, 'a', KEY_SIZE); memset(element.data_.databuf, 'b', DATA_SIZE); // Log debugging info from the application, but not from the library. tpie_log_init(TPIE_LOG_APP_DEBUG); MM_manager.set_memory_limit(TOTAL_CACHE_SIZE); MM_manager.enforce_memory_limit(); stream_t* is = new stream_t; stxxl::timer Timer; stxxl::int64 n_inserts = ops, n_locates = ops, n_range_queries = ops, n_deletes = ops; stxxl::int64 i; //comp_type cmp_; ran32State = 0xdeadbeef; //stxxl::random_number32 myrand; Timer.start(); { rand_key_gen Gen(n, element.key_); typedef stxxl::stream::sort sorter_type; sorter_type Sorter(Gen, comp_type(), SORTER_MEM); //typedef key2pair key2pair_type; //key2pair_type Key2Pair(Sorter); while (!Sorter.empty()) { is->write_item(*Sorter); ++Sorter; } } Timer.stop(); STXXL_MSG("Finished sorting input. Elapsed time: " << (Timer.mseconds() / 1000.) << " seconds."); Timer.reset(); Timer.start(); // bulk construction u_btree_t* u_btree; AMI_btree_params params; params.node_block_factor = NODE_BLOCK_SIZE / 4096; params.leaf_block_factor = LEAF_BLOCK_SIZE / 4096; params.leaf_cache_size = LEAF_CACHE_SIZE / LEAF_BLOCK_SIZE; params.node_cache_size = NODE_CACHE_SIZE / NODE_BLOCK_SIZE; u_btree = new u_btree_t(params); using std::cout; using std::cerr; if (!u_btree->is_valid()) { cerr << "Error initializing btree. Aborting.\n"; delete u_btree; exit(1); } if (u_btree->load_sorted(is, 1.0, 1.0) != AMI_ERROR_NO_ERROR) cerr << "Error during bulk loading.\n"; Timer.stop(); STXXL_MSG("Records in map: " << u_btree->size()); STXXL_MSG("Construction elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); //////////////////////////////////////// Timer.reset(); Timer.start(); for (i = 0; i < n_inserts; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.key_); u_btree->insert(element); } Timer.stop(); STXXL_MSG("Records in map: " << u_btree->size()); STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_inserts) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); //////////////////////////////////////////////// Timer.reset(); Timer.start(); el_t result; for (i = 0; i < n_locates; ++i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.key_); u_btree->succ(element.key_, result); } Timer.stop(); STXXL_MSG("Locates elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); //////////////////////////////////// Timer.reset(); Timer.start(); stxxl::int64 n_scanned = 0; //, skipped_qieries = 0; MyFilter filter; for (i = 0; i < n_range_queries; ++i) { rand_key(i, element.key_); my_key begin_key = element.key_; rand_key(i, element.key_); if (element.key_ < begin_key) n_scanned += u_btree->range_query(element.key_, begin_key, NULL, filter); else n_scanned += u_btree->range_query(begin_key, element.key_, NULL, filter); if (n_scanned >= SCAN_LIMIT(n)) { ++i; break; } } n_range_queries = i; Timer.stop(); STXXL_MSG("Range query elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_scanned) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec, #queries " << n_range_queries << " #scanned elements: " << n_scanned); ////////////////////////////////////// ran32State = 0xdeadbeef; memset(element.key_.keybuf, 'a', KEY_SIZE); memset(element.data_.databuf, 'b', DATA_SIZE); Timer.reset(); Timer.start(); for (i = n_deletes; i > 0; --i) { //element.first.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, element.key_); u_btree->erase(element.key_); } Timer.stop(); STXXL_MSG("Records in map: " << u_btree->size()); STXXL_MSG("Erase elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); delete u_btree; delete is; } void run_bdb_btree_big(stxxl::int64 n, unsigned ops) { const char* filename = BDB_FILE; my_key key1_storage; my_data data1_storage; #ifdef BDB_BULK_SCAN int* bulk_buffer = new int[logbufsize / sizeof(int)]; #endif unlink(filename); memset(key1_storage.keybuf, 'a', KEY_SIZE); memset(data1_storage.databuf, 'b', DATA_SIZE); Db db(NULL, 0); // Instantiate the Db object try { // here we start with the tests Dbt key1(key1_storage.keybuf, KEY_SIZE); Dbt data1(data1_storage.databuf, DATA_SIZE); stxxl::timer Timer; stxxl::int64 n_inserts = ops, n_locates = ops, n_range_queries = ops, n_deletes = ops; stxxl::int64 i; //comp_type cmp_; ran32State = 0xdeadbeef; Timer.start(); vector_type SortedSeq(n); //const vector_type & CSortedSeq(SortedSeq); { rand_key_gen Gen(n, key1_storage); typedef stxxl::stream::sort sorter_type; sorter_type Sorter(Gen, comp_type(), SORTER_MEM); typedef key2pair key2pair_type; key2pair_type Key2Pair(Sorter); stxxl::stream::materialize(Key2Pair, SortedSeq.begin()); } Timer.stop(); STXXL_MSG("Finished sorting input. Elapsed time: " << (Timer.mseconds() / 1000.) << " seconds."); db.set_errfile(stderr); db.set_pagesize(pagesize); db.set_cachesize(0, cachesize, 10); STXXL_MSG("BDB cache size set."); // Open the database db.open(NULL, // Transaction pointer filename, // Database file name NULL, // Optional logical database name DB_BTREE, // Database access method DB_CREATE, // Open flags 0); // File mode (using defaults) db.get_env()->memp_stat(NULL, NULL, DB_STAT_CLEAR); Timer.reset(); Timer.start(); // DBD does not have bulk construction // however inserting in sorted order might help // to improve performance vector_type::const_iterator cit = SortedSeq.begin(); for (i = 0; i < n; ++i, ++cit) { key1_storage = cit->first; db.put(NULL, &key1, &data1, DB_NOOVERWRITE); } Timer.stop(); DB_BTREE_STAT* dbstat; db.stat(NULL, &dbstat, 0); STXXL_MSG("Records in map: " << dbstat->bt_ndata); STXXL_MSG("Construction elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); db.stat_print(0); db.get_env()->memp_stat_print(DB_STAT_CLEAR); //////////////////////////////////////// Timer.reset(); Timer.start(); for (i = 0; i < n_inserts; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); db.put(NULL, &key1, &data1, DB_NOOVERWRITE); } Timer.stop(); db.stat(NULL, &dbstat, 0); STXXL_MSG("Records in map: " << dbstat->bt_ndata); STXXL_MSG("Insertions elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_inserts) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); db.stat_print(0); db.get_env()->memp_stat_print(DB_STAT_CLEAR); ///////////////////////////////////////// Timer.reset(); Timer.start(); Dbc* cursorp; db.cursor(NULL, &cursorp, 0); for (i = 0; i < n_locates; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); Dbt keyx(key1_storage.keybuf, KEY_SIZE); Dbt datax(data1_storage.databuf, DATA_SIZE); cursorp->get(&keyx, &datax, DB_SET_RANGE); } Timer.stop(); STXXL_MSG("Locates elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); db.stat_print(0); db.get_env()->memp_stat_print(DB_STAT_CLEAR); //////////////////////////////////// Timer.reset(); Timer.start(); stxxl::int64 n_scanned = 0; for (i = 0; i < n_range_queries; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); my_key last_key = key1_storage; //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); if (last_key < key1_storage) std::swap(last_key, key1_storage); //STXXL_MSG("Looking "<get(&keyx, &datax, DB_SET_RANGE | DB_MULTIPLE_KEY) == DB_NOTFOUND) continue; do { DbMultipleKeyDataIterator BulkIterator(datax); Dbt key1, data1; while (BulkIterator.next(key1, data1) && *((my_key*)key1.get_data()) <= last_key) { ++n_scanned; //STXXL_MSG("Result "<<*((my_key *)key1.get_data())); } if (cursorp->get(&keyx, &datax, DB_NEXT | DB_MULTIPLE_KEY) == DB_NOTFOUND) break; if (*((my_key*)keyx.get_data()) > last_key) { break; } } while (1); #else if (cursorp->get(&keyx, &datax, DB_SET_RANGE) == DB_NOTFOUND) continue; while (*((my_key*)keyx.get_data()) <= last_key) { ++n_scanned; if (cursorp->get(&keyx, &datax, DB_NEXT) == DB_NOTFOUND) break; } #endif if (n_scanned >= SCAN_LIMIT(n)) { ++i; break; } } n_range_queries = i; Timer.stop(); if (cursorp != NULL) cursorp->close(); STXXL_MSG("Range query elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(n_scanned) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec, #queries " << n_range_queries << " #scanned elements: " << n_scanned); db.stat_print(0); db.get_env()->memp_stat_print(DB_STAT_CLEAR); ////////////////////////////////////// ran32State = 0xdeadbeef; memset(key1_storage.keybuf, 'a', KEY_SIZE); Timer.reset(); Timer.start(); for (i = 0; i < n_deletes; ++i) { //key1_storage.keybuf[KEYPOS] = letters[VALUE]; rand_key(i, key1_storage); Dbt keyx(key1_storage.keybuf, KEY_SIZE); db.del(NULL, &keyx, 0); } Timer.stop(); db.stat(NULL, &dbstat, 0); STXXL_MSG("Records in map: " << dbstat->bt_ndata); STXXL_MSG("Erase elapsed time: " << (Timer.mseconds() / 1000.) << " seconds : " << (double(ops) / (Timer.mseconds() / 1000.)) << " key/data pairs per sec"); db.stat_print(0); db.get_env()->memp_stat_print(DB_STAT_CLEAR); db.close(0); } catch (DbException& e) { STXXL_ERRMSG("DbException happened"); } catch (std::exception& e) { STXXL_ERRMSG("std::exception happened"); } unlink(filename); #ifdef BDB_BULK_SCAN delete[] bulk_buffer; #endif } int main(int argc, char* argv[]) { STXXL_MSG("stxxl::map Real Node block size: " << REAL_NODE_BLOCK_SIZE << " bytes"); STXXL_MSG("stxxl::map Real Leaf block size: " << REAL_LEAF_BLOCK_SIZE << " bytes"); STXXL_MSG("stxxl::map Node max elements : " << REAL_NODE_MELEMENTS); STXXL_MSG("stxxl::map Leaf max elements : " << REAL_LEAF_MELEMENTS); #if STXXL_DIRECT_IO_OFF STXXL_MSG("STXXL_DIRECT_IO_OFF is defined"); #else STXXL_MSG("STXXL_DIRECT_IO_OFF is NOT defined"); #endif if (argc < 3) { STXXL_MSG("Usage: " << argv[0] << " version #ops"); STXXL_MSG("\t version = 1: test stxxl map"); STXXL_MSG("\t version = 2: test Berkeley DB btree"); STXXL_MSG("\t version = 3: big test stxxl map"); STXXL_MSG("\t version = 4: big test Berkeley DB btree"); STXXL_MSG("\t version = 5: big test TPIE btree"); return -1; } init(); int version = atoi(argv[1]); stxxl::uint64 ops = stxxl::atouint64(argv[2]); STXXL_MSG("Running version : " << version); STXXL_MSG("Operations to perform: " << ops); STXXL_MSG("Btree cache size : " << TOTAL_CACHE_SIZE << " bytes"); STXXL_MSG("Leaf block size : " << LEAF_BLOCK_SIZE << " bytes"); switch (version) { case 1: run_stxxl_map(ops); break; case 2: run_bdb_btree(ops); break; case 3: run_stxxl_map_big(ops, 100000); break; case 4: run_bdb_btree_big(ops, 100000); break; case 5: run_tpie_btree_big(ops, 100000); break; default: STXXL_MSG("Unsupported version " << version); } } stxxl-1.4.1/tools/benchmarks/README000644 001411 000144 00000001534 12350112610 016624 0ustar00tbusers000000 000000 This directory contains some benchmarks referenced in past papers on STXXL. These mostly compare to versions of TPIE, LEDA-SM and BerkeleyDB. The code works only with old versions of TPIE: 091905 (which is the youngest version at duke.edu). To enable compilation with TPIE, download tpie_091905.tgz, unpack and compile it in the tools/benchmarks directory of the source tree. The cmake file will detect tools/benchmarks/tpie_*/lib/libtpie.a and compile the TPIE tests with it. LEDA-SM test were removed as the library is no longer available. Comparisons against BerkeleyDB are easier, because this library has a very stable interface. If TPIE was detected, the cmake script will search for BerkeleyDB on the system and attempt to compile with it. All benchmarks not depending on external libraries are build with -DBUILD_TESTS=ON. 2013-10-07 Timo Bingmannstxxl-1.4.1/tools/benchmarks/CMakeLists.txt000644 001411 000144 00000004644 12422213132 020512 0ustar00tbusers000000 000000 ############################################################################ # tools/benchmarks/CMakeLists.txt # # Part of the STXXL. See http://stxxl.sourceforge.net # # Copyright (C) 2013 Timo Bingmann # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) ############################################################################ stxxl_build_test(benchmark_naive_matrix) stxxl_build_test(matrix_benchmark) stxxl_build_test(monotonic_pq) stxxl_build_test(pq_benchmark) stxxl_build_test(stack_benchmark) add_define(benchmark_naive_matrix "STXXL_VERBOSE_LEVEL=0") if(USE_TPIE AND BUILD_TESTS) file(GLOB TPIE_DIR_GLOB RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "tpie_*") foreach(TPIE_TEST ${TPIE_DIR_GLOB}) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${TPIE_TEST}/lib/libtpie.a") set(TPIE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${TPIE_TEST}") break() endif() endforeach() if(EXISTS "${TPIE_DIR}/lib/libtpie.a") message(STATUS "Found compiled TPIE library at ${TPIE_DIR}") add_library(tpie STATIC IMPORTED) set_property(TARGET tpie PROPERTY IMPORTED_LOCATION "${TPIE_DIR}/lib/libtpie.a") include_directories("${TPIE_DIR}/include") stxxl_build_test(tpie_stack_benchmark) target_link_libraries(tpie_stack_benchmark ${STXXL_LIBRARIES} tpie) # try to locate a Berkeley DB version find_path(BERKELEYDB_INCLUDES db_cxx.h /usr/local/include/db4.8 /usr/local/include/db4.7 /usr/local/include/db4.6 /usr/local/include/db4.5 /usr/local/include/db4 /usr/local/include/db /usr/include/db4.8 /usr/include/db4.7 /usr/include/db4.6 /usr/include/db4.5 /usr/include/db4 /usr/include/db ) find_library(BERKELEYDB_LIBRARIES NAMES db_cxx) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(BERKELEYDB "Could not find Berkeley DB" BERKELEYDB_INCLUDES BERKELEYDB_LIBRARIES) mark_as_advanced(BERKELEYDB_INCLUDES BERKELEYDB_LIBRARIES) if(BERKELEYDB_FOUND) include_directories("${BERKELEYDB_INCLUDES}") stxxl_build_test(berkeley_db_benchmark) target_link_libraries(berkeley_db_benchmark ${STXXL_LIBRARIES} tpie ${BERKELEYDB_LIBRARIES}) endif() else() message(SEND_ERROR "Could not find TPIE for extra benchmarks.") endif() endif(USE_TPIE AND BUILD_TESTS) stxxl-1.4.1/tools/benchmarks/app_config.h000644 001411 000144 00000010524 12350112610 020221 0ustar00tbusers000000 000000 // // File: app_config.h // Authors: Darren Erik Vengroff // Octavian Procopiuc // // Created: 10/6/94 // // $Id: app_config.h,v 1.36 2004/08/17 16:49:16 jan Exp $ // #ifndef _APP_CONFIG_H #define _APP_CONFIG_H // Get the configuration as set up by the TPIE configure script. #include // <><><><><><><><><><><><><><><><><><><><><><> // // <><><><><><><> Developer use <><><><><><><> // // <><><><><><><><><><><><><><><><><><><><><><> // #define USE_LIBAIO 0 // Set up some defaults for the test applications #include #include // for size_t #include // for random() #define DEFAULT_TEST_SIZE (20000000) #define DEFAULT_RANDOM_SEED 17 #define DEFAULT_TEST_MM_SIZE (1024 * 1024 * 32) extern bool verbose; extern TPIE_OS_SIZE_T test_mm_size; extern TPIE_OS_OFFSET test_size; extern int random_seed; // <><><><><><><><><><><><><><><><><><><><><><> // // <><><> Choose default BTE COLLECTION <><><> // // <><><><><><><><><><><><><><><><><><><><><><> // #if (!defined (BTE_COLLECTION_IMP_MMAP) && !defined (BTE_COLLECTION_IMP_UFS) && !defined (BTE_COLLECTION_IMP_USER_DEFINED)) // Define only one (default is BTE_COLLECTION_IMP_MMAP) #define BTE_COLLECTION_IMP_MMAP //#define BTE_COLLECTION_IMP_UFS //#define BTE_COLLECTION_IMP_USER_DEFINED #endif // <><><><><><><><><><><><><><><><><><><><><><> // // <><><><><><> Choose BTE STREAM <><><><><><> // // <><><><><><><><><><><><><><><><><><><><><><> // // Define only one (default is BTE_STREAM_IMP_UFS) #define BTE_STREAM_IMP_UFS //#define BTE_STREAM_IMP_MMAP //#define BTE_STREAM_IMP_STDIO //#define BTE_STREAM_IMP_USER_DEFINED // <><><><><><><><><><><><><><><><><><><><><><><><> // // <> BTE_COLLECTION_MMAP configuration options <> // // <><><><><><><><><><><><><><><><><><><><><><><><> // // Define write behavior. // Allowed values: // 0 (synchronous writes) // 1 (asynchronous writes using MS_ASYNC - see msync(2)) // 2 (asynchronous bulk writes) [default] #ifndef BTE_COLLECTION_MMAP_LAZY_WRITE #define BTE_COLLECTION_MMAP_LAZY_WRITE 2 #endif // <><><><><><><><><><><><><><><><><><><><><><><><> // // <> BTE_COLLECTION_UFS configuration options <> // // <><><><><><><><><><><><><><><><><><><><><><><><> // // <><><><><><><><><><><><><><><><><><><><><><><><> // // <><> BTE_STREAM_MMAP configuration options <><> // // <><><><><><><><><><><><><><><><><><><><><><><><> // #ifdef BTE_STREAM_IMP_MMAP // Define logical blocksize factor (default is 32) #ifndef BTE_STREAM_MMAP_BLOCK_FACTOR #ifdef _WIN32 #define BTE_STREAM_MMAP_BLOCK_FACTOR 4 #else #define BTE_STREAM_MMAP_BLOCK_FACTOR 512 #endif #endif // Enable/disable TPIE read ahead; default is enabled (set to 1) //#define BTE_STREAM_MMAP_READ_AHEAD 1 // read ahead method, ignored unless BTE_STREAM_MMAP_READ_AHEAD is set // to 1; if USE_LIBAIO is enabled, use asynchronous IO read ahead; // otherwise use use mmap-based read ahead; default is mmap-based read // ahead (USE_LIBAIO not defined) //#define USE_LIBAIO #endif // <><><><><><><><><><><><><><><><><><><><><><><><> // // <><> BTE_STREAM_UFS configuration options <><><> // // <><><><><><><><><><><><><><><><><><><><><><><><> // #ifdef BTE_STREAM_IMP_UFS // Define logical blocksize factor (default is 32) #ifndef BTE_STREAM_UFS_BLOCK_FACTOR #ifdef _WIN32 #define BTE_STREAM_UFS_BLOCK_FACTOR 4 #else #define BTE_STREAM_UFS_BLOCK_FACTOR 512 #endif #endif // Enable/disable TPIE read ahead; default is disabled (set to 0) #define BTE_STREAM_UFS_READ_AHEAD 0 // read ahead method, ignored unless BTE_STREAM_UFS_READ_AHEAD is set // to 1; if USE_LIBAIO is set to 1, use asynchronous IO read ahead; // otherwise no TPIE read ahead is done; default is disabled (set to 0) #define USE_LIBAIO 0 #endif // <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> // // logging and assertions; // // this should NOT be modified by user!!! // // in order to enable/disable library/application logging, // // run tpie configure script with appropriate options // // <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> // // Use logs if requested. #if TP_LOG_APPS #define TPL_LOGGING 1 #endif #include // Enable assertions if requested. #if TP_ASSERT_APPS #define DEBUG_ASSERTIONS 1 #endif #include #endif stxxl-1.4.1/tools/benchmark_disks.cpp000644 001411 000144 00000024463 12411366426 017506 0ustar00tbusers000000 000000 /*************************************************************************** * tools/benchmark_disks.cpp * * Part of the STXXL. See http://stxxl.sourceforge.net * * Copyright (C) 2009 Johannes Singler * Copyright (C) 2013 Timo Bingmann * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) **************************************************************************/ /* This programm will benchmark the disks configured via .stxxl disk configuration files. The block manager is used to read and write blocks using the different allocation strategies. */ /* example gnuplot command for the output of this program: (x-axis: offset in GiB, y-axis: bandwidth in MiB/s) plot \ "disk.log" using ($2/1024):($7) w l title "read", \ "disk.log" using ($2/1024):($4) w l title "write" */ #include #include #include #include #include using stxxl::timestamp; using stxxl::unsigned_type; using stxxl::uint64; #ifdef BLOCK_ALIGN #undef BLOCK_ALIGN #endif #define BLOCK_ALIGN 4096 #define POLL_DELAY 1000 #define CHECK_AFTER_READ 0 #define KiB (1024) #define MiB (1024 * 1024) template int benchmark_disks_blocksize_alloc(uint64 length, uint64 batch_size, std::string optrw) { uint64 offset = 0, endpos = offset + length; if (length == 0) endpos = std::numeric_limits::max(); bool do_read = (optrw.find('r') != std::string::npos); bool do_write = (optrw.find('w') != std::string::npos); // construct block type const unsigned_type raw_block_size = RawBlockSize; const unsigned_type block_size = raw_block_size / sizeof(int); typedef stxxl::typed_block block_type; typedef stxxl::BID BID_type; if (batch_size == 0) batch_size = stxxl::config::get_instance()->disks_number(); // calculate total bytes processed in a batch batch_size = raw_block_size * batch_size; unsigned_type num_blocks_per_batch = (unsigned_type)stxxl::div_ceil(batch_size, raw_block_size); batch_size = num_blocks_per_batch * raw_block_size; block_type* buffer = new block_type[num_blocks_per_batch]; stxxl::request_ptr* reqs = new stxxl::request_ptr[num_blocks_per_batch]; std::vector blocks; double totaltimeread = 0, totaltimewrite = 0; stxxl::int64 totalsizeread = 0, totalsizewrite = 0; std::cout << "# Batch size: " << stxxl::add_IEC_binary_multiplier(batch_size, "B") << " (" << num_blocks_per_batch << " blocks of " << stxxl::add_IEC_binary_multiplier(raw_block_size, "B") << ")" << " using " << AllocStrategy().name() << std::endl; // touch data, so it is actually allcoated for (unsigned j = 0; j < num_blocks_per_batch; ++j) for (unsigned i = 0; i < block_size; ++i) buffer[j][i] = (unsigned)(j * block_size + i); try { AllocStrategy alloc; while (offset < endpos) { const stxxl::int64 current_batch_size = std::min(batch_size, endpos - offset); #if CHECK_AFTER_READ const stxxl::int64 current_batch_size_int = current_batch_size / sizeof(int); #endif const unsigned_type current_num_blocks_per_batch = (unsigned_type)stxxl::div_ceil(current_batch_size, raw_block_size); std::cout << "Offset " << std::setw(7) << offset / MiB << " MiB: " << std::fixed; unsigned_type num_total_blocks = blocks.size(); blocks.resize(num_total_blocks + current_num_blocks_per_batch); stxxl::block_manager::get_instance()->new_blocks(alloc, blocks.begin() + num_total_blocks, blocks.end()); double begin = timestamp(), end, elapsed; if (do_write) { for (unsigned j = 0; j < current_num_blocks_per_batch; j++) reqs[j] = buffer[j].write(blocks[num_total_blocks + j]); wait_all(reqs, current_num_blocks_per_batch); end = timestamp(); elapsed = end - begin; totalsizewrite += current_batch_size; totaltimewrite += elapsed; } else elapsed = 0.0; std::cout << std::setw(5) << std::setprecision(1) << (double(current_batch_size) / MiB / elapsed) << " MiB/s write, "; begin = timestamp(); if (do_read) { for (unsigned j = 0; j < current_num_blocks_per_batch; j++) reqs[j] = buffer[j].read(blocks[num_total_blocks + j]); wait_all(reqs, current_num_blocks_per_batch); end = timestamp(); elapsed = end - begin; totalsizeread += current_batch_size; totaltimeread += elapsed; } else elapsed = 0.0; std::cout << std::setw(5) << std::setprecision(1) << (double(current_batch_size) / MiB / elapsed) << " MiB/s read" << std::endl; #if CHECK_AFTER_READ for (unsigned j = 0; j < current_num_blocks_per_batch; j++) { for (unsigned i = 0; i < block_size; i++) { if (buffer[j][i] != j * block_size + i) { int ibuf = i / current_batch_size_int; int pos = i % current_batch_size_int; std::cout << "Error on disk " << ibuf << " position " << std::hex << std::setw(8) << offset + pos * sizeof(int) << " got: " << std::hex << std::setw(8) << buffer[j][i] << " wanted: " << std::hex << std::setw(8) << (j * block_size + i) << std::dec << std::endl; i = (ibuf + 1) * current_batch_size_int; // jump to next } } } #endif offset += current_batch_size; } } catch (const std::exception& ex) { std::cout << std::endl; STXXL_ERRMSG(ex.what()); } std::cout << "=============================================================================================" << std::endl; std::cout << "# Average over " << std::setw(7) << totalsizewrite / MiB << " MiB: "; std::cout << std::setw(5) << std::setprecision(1) << (double(totalsizewrite) / MiB / totaltimewrite) << " MiB/s write, "; std::cout << std::setw(5) << std::setprecision(1) << (double(totalsizeread) / MiB / totaltimeread) << " MiB/s read" << std::endl; delete[] reqs; delete[] buffer; return 0; } template int benchmark_disks_alloc(uint64 length, uint64 batch_size, unsigned_type block_size, std::string optrw) { #define run(bs) benchmark_disks_blocksize_alloc(length, batch_size, optrw) if (block_size == 4 * KiB) run(4 * KiB); else if (block_size == 8 * KiB) run(8 * KiB); else if (block_size == 16 * KiB) run(16 * KiB); else if (block_size == 32 * KiB) run(32 * KiB); else if (block_size == 64 * KiB) run(64 * KiB); else if (block_size == 128 * KiB) run(128 * KiB); else if (block_size == 256 * KiB) run(256 * KiB); else if (block_size == 512 * KiB) run(512 * KiB); else if (block_size == 1 * MiB) run(1 * MiB); else if (block_size == 2 * MiB) run(2 * MiB); else if (block_size == 4 * MiB) run(4 * MiB); else if (block_size == 8 * MiB) run(8 * MiB); else if (block_size == 16 * MiB) run(16 * MiB); else if (block_size == 32 * MiB) run(32 * MiB); else if (block_size == 64 * MiB) run(64 * MiB); else if (block_size == 128 * MiB) run(128 * MiB); else std::cerr << "Unsupported block_size " << block_size << "." << std::endl << "Available are only powers of two from 4 KiB to 128 MiB. You must use 'ki' instead of 'k'." << std::endl; #undef run return 0; } int benchmark_disks(int argc, char* argv[]) { // parse command line stxxl::cmdline_parser cp; uint64 length = 0; unsigned int batch_size = 0; unsigned_type block_size = 8 * MiB; std::string optrw = "rw", allocstr; cp.add_param_bytes("size", "Amount of data to write/read from disks (e.g. 10GiB)", length); cp.add_opt_param_string("r|w", "Only read or write blocks (default: both write and read)", optrw); cp.add_opt_param_string("alloc", "Block allocation strategy: RC, SR, FR, striping. (default: RC)", allocstr); cp.add_uint('b', "batch", "Number of blocks written/read in one batch (default: D * B)", batch_size); cp.add_bytes('B', "block_size", "Size of blocks written in one syscall. (default: B = 8MiB)", block_size); cp.set_description( "This program will benchmark the disks configured by the standard " ".stxxl disk configuration files mechanism. Blocks of 8 MiB are " "written and/or read in sequence using the block manager. The batch " "size describes how many blocks are written/read in one batch. The " "are taken from block_manager using given the specified allocation " "strategy. If size == 0, then writing/reading operation are done " "until an error occurs. " ); if (!cp.process(argc, argv)) return -1; if (allocstr.size()) { if (allocstr == "RC") return benchmark_disks_alloc(length, batch_size, block_size, optrw); if (allocstr == "SR") return benchmark_disks_alloc(length, batch_size, block_size, optrw); if (allocstr == "FR") return benchmark_disks_alloc(length, batch_size, block_size, optrw); if (allocstr == "striping") return benchmark_disks_alloc(length, batch_size, block_size, optrw); std::cout << "Unknown allocation strategy '" << allocstr << "'" << std::endl; cp.print_usage(); return -1; } return benchmark_disks_alloc(length, batch_size, block_size, optrw); } stxxl-1.4.1/TODO000644 001411 000144 00000012021 12424127146 013164 0ustar00tbusers000000 000000 * asynchronous pipelining (currently being developed in branch parallel_pipelining_integration) * if the stxxl disk files have been enlarged because more external memory was requested by the program, resize them afterwards to max(size_at_program_start, configured_size) https://sourceforge.net/forum/message.php?msg_id=4925158 * allocation strategies: provide a method get_num_disks() and don't use stxxl::config::get_instance()->disks_number() inappropriately * implement recursion in stable_ksort and do not assume random key distribution, do sampling instead as a start, at least abort early if the expected size of a bucket is larger than the memory available to sort it * debug stable_ksort in depth, there are still some crashing cases left * continue using the new approach for STXXL_VERBOSE: $(CXX) -DSTXXL_VERBOSE_FOO=STXXL_VERBOSEx * check+fix all sorted_runs() calls to not cause write I/Os * on disk destruction, check whether all blocks had been deallocated before, i.e. free_bytes == disk_size * implement an allocator which looks at the available free space on disks when distributing blocks to disks, or does load-balancing depending on the given speed of the disks * abstract away block manager so every container can attach to a file. * retry incomplete I/Os for all file types (currently only syscall) * iostats: add support for splitting I/Os and truncating reads at EOF * do not specify marginal properties such as allocation strategy as part of the template type. instead, make such properties dynamically configurable using run-time polymorphism, which would incur only a negligible running time overhead (one virtual function call per block). * If we get rid of the sentinels (at least for sorting), we can drop the min_value()/max_value() requirement on the comparator and therefore unmodified comparators could be used for stxxl. * Traditionally stxxl only supports PODs in external containers, e.g. nothing that has non-trivial constructors, destructors or copy/assignemnt operators. That is neccessary to allow fast operations by accessing the underlying blocks directly without caring about copying/moving/otherwise manipulating the content or uninitialized elements. For some applications it could be helpful to have containers that take non-POD elements, but you would probably lose the features you gain by having direct access to the blocks. Some discussion regarding using some shared_ptr<> in a stxxl::vector can be found here: https://sourceforge.net/projects/stxxl/forums/forum/446474/topic/4099030 * The following code is not correct: file * f = create_file(..., RDONLY); vector<> v(f); v[42]; // non-const access, sets dirty flag but the error message it produces is very unclear and may come very asynchronous: terminate called after throwing an instance of 'stxxl::io_error' what(): Error in function virtual void stxxl::mmap_file::serve(const stxxl::request*): Info: Mapping failed. Page size: 4096 offset modulo page size 0 Permission denied Caused by stxxl::vector<> when swapping out a dirty page. Reproducible with containers/test_vector_sizes.stxxl.bin Possible solution: vector::write_page() should check if vector-is-bound-to-a-file and file-is-opened-read-only throw "please use only const access to a read only vector" * I've noticed that the destructor for stxxl::vector flushes any dirty data to disk. Possibly that makes sense for a file-backed vector (I haven't looked at how those work), but for a vector that uses the disks configured in .stxxl it is wasted I/O as the data is immediately freed. For the moment I am working around this by calling thevector.clear() immediately before destroying it, which seems to prevent the writeback (based on querying the stats). * There should be a function like bool supports_filetype(const char *) that allows to check at runtime whether a file type (given as string) is available. Useful for tests that take a file type as parameter - they currently throw an exception "unsupported filetype". * Change the constructor stxxl::vector<>::vector(stxxl::file*) to stxxl::vector<>::vector(shared_ptr) so that ownership of the file can be transferred to the vector and cleanup (closing etc.) of the file can happen automatically * materialize() that takes an empty vector as argument and grows it, including allocating more blocks of fills the existing part of a vectorand once we came to it's end automatically expands the vector (including proper resizes). This approach should be more efficient (due to overlapping) than repeated push_back() * streamop passthrough() useful if one wants to replace some streamop by a no-op by just redefining one type needs peformance measurements, hopefully has no impact on speed. * stream::discard() should not need to call in.operator*() - are there side effects to be expected? -> add a streamop "inspect" or so for this purpose -> drop *in call from discard * add discard(StreamOp, unsigned_irgendwas n): discard at most n elements stxxl-1.4.1/AUTHORS000644 001411 000144 00000001006 12424126720 013542 0ustar00tbusers000000 000000 Andreas Beckmann Daniel Feist Daniel Godas-Lopez Ilja Andronov Jaroslaw Fedorowicz Jens Mehnert Johannes Singler Manuel Krings Markus Westphal Peter Sanders Raoul Steffen Roman Dementiev Thomas Nowak Timo Bingmann stxxl-1.4.1/LICENSE_1_0.txt000644 001411 000144 00000002472 12350112610 014753 0ustar00tbusers000000 000000 Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.