MCE-1.608/0000755000076400007640000000000012511673554011155 5ustar mariomarioMCE-1.608/CHANGES0000644000076400007640000012646012511672445012157 0ustar mariomario Revision history for MCE 1.608 Fri Apr 10 03:00:00 EST 2015 * Correction for $prog_name (-e) to (perl) in MCE::Signal. 1.607 Fri Apr 10 01:00:00 EST 2015 [BUG FIXES] * Updated t/01_load_signal_arg.t to address false-positive from bug fix in 1.606. This was missed in the 1.606 release. Thank you Nigel Horne. [ENHANCEMENTS] * Replaced (-e) with (perl) for the $prog_name value in MCE::Signal; e.g. from running perl -e 'command'. 1.606 Wed Apr 08 18:00:00 EST 2015 [BUG FIXES] * Added -d and -w tests to ensure $ENV{TEMP} exists and writeable in MCE::Signal. Otherwise /tmp is used as usual. [ENHANCEMENTS] * Determine running state in MCE->exit. Call stop_and_exit if not already running to not hang the script. 1.605 Mon Apr 06 00:30:00 EST 2015 [BUG FIXES] * Improved fix for the die handler in MCE::Signal and MCE::Core::Worker. * Improved support for threads in MCE::Signal's stop_and_exit function. 1.604 Sat Mar 21 21:00:00 EST 2015 [BUG FIXES] * All bugs found during testing of the upcoming 1.7 release have been backported to the 1.6 branch. [NEW FEATURES] * Added out_iter_array and out_iter_fh to MCE::Candy. These preserve output order and cover the two general use cases. 1.603 Tue Mar 17 21:00:00 EST 2015 [ENHANCEMENTS] * A safer solution by Dmitry Karasik for the die handler in MCE::Signal and MCE::Core::Worker. * Moved ->forchunk, ->foreach, and ->forseq sugar methods to MCE::Candy. Stubs exist in MCE. Thus, no breakage to existing apps. * Removed the link to MCE::Shared on the main page. I decided to backport all the fixes into 1.6. The MCE::Shared link was missed and requires the upcoming 1.7 release. 1.602 Mon Mar 16 20:00:00 EST 2015 [BUG FIXES] * Updated die handler in MCE::Core::Worker and MCE::Signal (RT#102802) Bug reported by Dmitry Karasik. [ENHANCEMENTS] * MCE child processes call CORE::exit during exiting; previously CORE::kill. * Improved IPC stability on Windows and Cygwin. * Multiple calls to mutex->lock by the same process or thread is now safe. The mutex will remain locked until mutex->unlock is called. 1.601 Sun Mar 15 21:00:00 EST 2015 This release is attributed to the many use cases sent by George Bouras. [BUG FIXES] * Updated Makefile.PL allowing installation of bin/mce_grep (RT#102040). Running (MCE_INSTALL_TOOLS=1 perl Makefile.PL) installs bin/mce_grep. * Ensure MCE instances spawned by workers have shutdown before leaving. * An exiting forked non-MCE process will not cause the worker to exit. * Fixed sockets not closing immediately in Cygwin and Windows. * Fixed a pod error in MCE::Mutex. * Fixed a rare condition where socket handles for MCE::Mutex and MCE::Queue were closing pre-maturely when using threads. * Fixed an undefined variable inside croak statements in MCE Models' import subroutine. * Fixed automatic shutdown due to loading threads and specifying use_threads => 0 with workers persisting before exiting the script. [ENHANCEMENTS] * Moved relay methods, introduced in 1.600, from MCE to MCE::Relay. The MCE init_relay option loads and enables MCE::Relay automatically. * Captured metrics from Linux (previously OS X) for forchunk.pl, foreach.pl, and forseq.pl. Updated Examples.pod. * Default to 'auto' for max_workers in bin/mce_grep, examples/egrep.pl, forchunk.pl, foreach.pl, forseq.pl, iterator.pl, and wc.pl. * Moved _create_socket_pair from MCE to MCE::Util as _make_socket_pair. Added _destroy_sockets. Updated MCE, Mutex, and Queue to use MCE::Util. * Added a CARP_NOT line to MCE Models. * Added support for ->next and ->last from any sub-tasks in MCE::Step. * Reverted a small change applied in 1.600 to ->do and ->gather. * The 'synchronize' method in Mutex is wantarray aware. * Updated POD header lines for method names; from =item to =head2. * Workers now set STDERR and STDOUT to flush automatically. [NEW FEATURES] * Added ->pid method to MCE. 1.600 Sat Jan 31 20:00:00 EST 2015 [BUG FIXES] * Die handlers (in MCE::Signal and MCE::Core::Worker) are finally 100%. Furthermore, eval { die ... } statements behave correctly while running MCE itself inside an eval block. Thus, MCE on iPerl on top of iPython is possible via Devel::IPerl. * Fixed queues stalling from running (MCE::Queue fast => 1) on Linux. * MCE Models now set $MCE::FREEZE, $MCE::THAW, and $MCE::TMP_DIR when overriding freeze, thaw, and tmp_dir respectively at load time. * Pressing CTRL-D now ends STDIN the first time. [ENHANCEMENTS] * Added seven names to CREDITS; David Farrell, Demian Riccardi, Hisham Eldai, Joel Berger, Oleksandr Kharchenko, Wei Shen, and Zakariyya Mughal. * Refactored ->print, ->printf, and ->say. Optimized ->print some more. * Shorten $_queue to $_Q in Queue. ID is always sent first during IPC. * The init method in MCE Models can now take an array of options. * Optimized memory consumption in Handle.pm, Iterator.pm, and Request.pm. * Optimized memory consumption for ->sendto, ->do, and ->gather. * Optimized memory consumption for overall IPC in general. * Refreshed the MCE->new method. [NEW FEATURES] * Added a new module; MCE::Mutex providing simple semaphore. * Added ->relay_recv, ->relay, and ->relay_final methods to the Core API. Refreshed cat.pl and findnull.pl examples to relay the number of lines read. Workers output exclusively and orderly to STDOUT in cat.pl. * Added several examples; biofasta (folder), mutex.pl, and relay.pl. The FASTA examples process by records "\n>", not by lines. * MCE applies additional logic when RS begins with a newline character; e.g. RS => "\n>". It trims away characters after the newline and prepends them to the next record. This happens automatically when not slurping. Otherwise, the logic is applied to the first and last records only. This is illustrated in the Core API documentation. * Updated the Core API documentation (RS, added relay methods). 1.522 Thu Dec 25 16:00:00 EST 2014 [BUG FIXES] * Applied fix to MCE->shutdown so that MCE Models do not err when receiving signal to terminate. * Optimization for MCE->print, MCE->printf, MCE->say and MCE->sendto. This was calling fileno unnecessarily. MCE->print('STDERR', ...) is not supported. This works; MCE->print(\*STDERR, ...) [ENHANCEMENTS] * Inserts the actual lib-path at the head of @INC in example files. * Massive documentation updates throughout the entire distribution. * Renamed barrier_sync.pl to sync.pl; scaling_pings.pl to ping.pl. * Updated requires in META.yml, Makefile.PL and perl-MCE.spec. * Updated comment in MCE::Queue test scripts. * Updated the README file. [NEW FEATURES] * Added sampledb examples demonstrating DBI and SQLite with MCE. * Added step_demo.pl to examples. 1.521 Thu Dec 11 16:00:00 EST 2014 [BUG FIXES] * Fixed broken MCE::Queue ->insert and ->peek methods. FIFO and LIFO are fully supported with this release. * Support running in taint mode. [ENHANCEMENTS] * Added support for negative index in MCE::Queue ->insert and ->peek. Updated the documentation. * CBOR::XS serialization is mentioned in documentation along with JSON::XS and Sereal. * Completed code refactoring for the 1.5 branch. * Optimized argument parsing in import routines. * Removed the MCE spawn_delay option from test scripts. [NEW FEATURES] * Added 15 new test scripts for testing user_args, MCE::Queue and the MCE Models. * An upper-limit of 8 is set when specifying max_workers => 'auto'. Several folks have requested this. More info at MCE::Util::get_ncpu for increasing or decreasing the limit. 1.520 Wed Nov 05 03:00:00 EST 2014 [ENHANCEMENTS] * MCE::Step and MCE::Stream can take the 'fast' option when including the module. The 'fast' option was introduced in 1.518. * Removed the type declaration for self (feedback from Sri). * Removed -s from files_flow.pl (was left behind). * Added support for GNU Hurd OS in get_ncpu. 1.519 Mon Oct 27 19:00:00 EST 2014 [ENHANCEMENTS] * Unset the need for channel locking if only worker riding the channel. There are 8 data channels in MCE. Basically, a worker will obtain a lock only when sharing the data channel with another worker. * Updated files_flow.pl, files_mce.pl and files_thr.pl to allow for many workers for the first task. Updated the synopsis in MCE::Queue. Synced example listing in Examples.pod with the examples folder. * Remove period from summary line. 1.518 Mon Oct 27 10:00:00 EST 2014 [BUG FIXES] * Corrected MCE::Queue's synopsis due to missing List::MoreUtils line. Changed the synopsis to use 1 worker for the 'dir' task in the event one were to copy/patse the code and use threads. The glob() function is not thread-safe in Perl 5.16.x; fixed in 5.18.2, okay in 5.8 - 14. * Use portable syntax for setpgrp in MCE::Signal. This closes issue 1 at https://code.google.com/p/many-core-engine-perl. [NEW FEATURES] * New 'fast' option for MCE::Queue. The 'fast' option speeds up ->dequeue ops and not enabled by default. It is beneficial for queues not needing ->clear or ->dequeue_nb and not altering the optional count value while running; e.g. ->dequeue( [ $count ] ). * Added three examples: files_flow.pl, files_mce.pl, and files_thr.pl. * Benchmarked on several OSes and appended results to MCE::Queue synopsis. 1.517 Thu Oct 23 10:00:00 EST 2014 [BUG FIXES] * Correction applied to MCE::Util::get_ncpu for Tru64 UNIX. This method will emit a warning (not croak) whenever the OS is unknown. * Changed ${^CHILD_ERROR_NATIVE} to $? in examples/pipe2.pl. This was missed in the previous release. [ENHANCEMENTS] * Added support for DragonFly BSD, SCO OpenServer 5, SCO OpenServer 6 and SCO UnixWare 7 to MCE::Util::get_ncpu. * Also, validated MCE on FreeBSD, NetBSD, OpenBSD, PC-BSD and JabirOS. No further changes to MCE::Util::get_ncpu. 1.516 Fri Oct 03 02:00:00 EST 2014 [BUG FIXES] * Updated IPC for better stability across multiple environments. The fix addresses an issue on Windows where sockets fail to respond after a period of inactivity; i.e. 4 minutes. Added George Bouras to CREDITS for reporting the issue. * Tip for folks developing on Windows: Open an explorer window and go to C:\Users\\AppData\Local\Temp. Right-click on the 'mce' folder and create a shortcut on the desktop. Although MCE removes its temp files automatically, it is possible for files to remain from a failing app. Go inside the 'mce' folder and press ctrl-a to select all files. Then press shift-delete; (macbook[pro] folks: fn-shift-delete). [ENHANCEMENTS] * Corrections to documentation. 1.515 Thu Jul 24 23:30:00 EST 2014 [BUG FIXES] * bin/mce_grep: ${^CHILD_ERROR_NATIVE} is not defined in Perl 5.8.x. Changed to $?. [ENHANCEMENTS] * Tweaked bin/mce_grep. Compute chunk_level => 'auto' to use 'file' when reading STDIN. Set chunk_size to 8M when not specified (4M previously). * Added the following names to CREDITS. Stephan Kulow ; for making the OpenSUSE MCE package Henry Lu ; for listening while I chatted away about MCE Jillian Rowe ; for reporting IO::File failing as input_data Sylvia Roy ; for driving while I worked in the passenger seat Tom Sasser ; for reporting bin/mce_grep failing with Perl 5.8.x Florian Schlichting ; for making the Debian MCE package [NEW FEATURES] * Added support for IO::File handles as valid input_data including IO:Uncompress:Gunzip. Modified SYNTAX section for INPUT_DATA in MCE::Core.pod. input_data => $fh, ## new IO::File "file", "r" input_data => $fh, ## new IO::Uncompress::Gunzip "file.gz" 1.514 Thu Jun 05 09:00:00 EST 2014 [BUG FIXES] * Fixed typo in MCE::Step POD (RT#95250) (Florian Schlichting). * Updated MCE::Util's get_ncpu function for AIX (Dana Jacobsen). * Do not send a KILL signal after receiving a SIGPIPE. * Fixed issue with Makefile.PL on defining the minimum Perl version. * Use Scalar::Util (looks_like_number) for validation logic. The regex previously were insufficient for large numbers containing scientific notation. This impacted the sequence option in MCE. [ENHANCEMENTS] * The mce-sandbox demo has been released on Github demonstrating Perl + MCE + Inline::C. The theme is Prime Numbers. This journey which began 2 years ago has been completed. There is also the mce-sort exercise demonstrating Perl + MCE + External C. https://github.com/marioroy/mce-sandbox (is thread-safe) https://github.com/marioroy/mce-sort (not thread-safe) * A hard decision had to made for MCE, particularly Perl under Windows excluding Cygwin. MCE will now load the 'threads' module automatically for Windows only. Folks may specify use_threads => 0 if threads is not desired. The reason for this is from seeing Math::Prime::Util crashing once workers exit. The same is true without MCE and forking a child process. Threads does not exhibit this behavior. It is a hard problem to solve. Why not default to threads for Windows since forking is emulated. 1.513 Sat Apr 19 20:30:00 EST 2014 [BUG FIXES] * Added fix for bug RT#94869 -- crash when restarting workers with 9+ workers. Updated the perldoc for restart_worker in MCE::Core.pod. [ENHANCEMENTS] * Replaced $self with $mce in MCE::/Core.pod to be consistent with examples described in MCE::Examples.pod. 1.512 Fri Apr 18 21:00:00 EST 2014 [BUG FIXES] * Signal-handling update for MCE::Signal. Passing the -setpgrp option is not necessary, even with Daemon::Control. Piping data into and out is better supported with this release (\*STDIN). This resolves bug RT#94706. cat infile | mce_script | head mce_script < infile | head Added Shawn Halpenny to the CREDITS file. * The utf8.pl example now runs under the BSD 9.0 environment. This was failing due to $^H{charnames} is not defined error. Removed the constant from the list of unicode characters inside the script. [ENHANCEMENTS] * Added examples/pipe1.pl and pipe2.pl. These process STDIN or FILE in parallel. Processing is via Perl for pipe1.pl, whereas an external command for pipe2.pl. 1.511 Fri Apr 04 22:30:00 EST 2014 [BUG FIXES] * Added "use bytes;" in several files to have length() return physical bytes, not logical characters. MCE is now UTF-8 safe when passing scalar data between workers and the manager process. Added Marcus Smith to the CREDITS file for reporting this bug. [ENHANCEMENTS] * Added examples/utf8.pl 1.510 Thu Mar 27 10:00:00 EST 2014 [ENHANCEMENTS] * The user_begin and user_end functions now receive 3 arguments. my ($mce, $task_id, $task_name) = @_; * Pass the chunk_size value when calling the iterator function. Added a DBI example under "SYNTAX for INPUT_DATA" in MCE::Core.pod. * Store the last scalar reference to not have workers re-spawn unnecessarily when input_data => \$same_scalar_ref. [NEW FEATURES] * New parallel_io option to further enhance slurp IO when specifying use_slurpio => 1 and chunk_size is greater than 8192. Try with chunk_size => '300k' or chunk_size => '2m'. The parallel_io option is beneficial when reading from fast storage. However, possibly not recommended if running MCE on many compute nodes and having workers read various input_data from shared storage. Enable parallel_io only if it makes sence and without impacting the environment such as nfsd. Because use_slurpio => 1, parallel_io => 1, and chunk_size => '2560k' is truly parallel. 1.509 Sat Feb 03 05:30:00 EST 2014 [BUG FIXES] * Fixed an issue with all the models (Flow, Grep, Loop, Map, Step, and Stream) ending immediately on subsequent runs when input_data is specified through the init method. * Things have finally settled down with all the models. [ENHANCEMENTS] * Refactored the fix applied in 1.508 for addressing bug #92627. * MCE, for the most part, has been completed. This release touches up on many examples under the 'examples' directory. * Brought the MCE::Examples Perl documentation up to date. Inlined MCE::Loop snippets under the chunking sections. Added a new section GLOBALLY SCOPED VARIABLES AND MCE MODELS. Added a new section MONTE CARLO SIMULATION. 1.508 Sat Feb 01 04:00:00 EST 2014 [BUG FIXES] * Applied fix for bug #92627 submitted by Philip Mabon. MCE scripts may exit with a non zero error code due to leaving workers up thereby having MCE performing the shut down in its END block. For folks running on an older release, the workaround is to shut down workers prior to exiting. 1.507 Fri Jan 30 23:00:00 EST 2014 [ENHANCEMENTS] * Added clarity around preserving output order for several Models; MCE::Flow, MCE::Loop, and MCE::Step. * Inlined comments in the code for overriding MCE options when using an anonymous hash for the first argument; MCE::Flow, MCE::Step, and MCE::Stream. * Removed unnecessary code due to one not able to pass MCE options other than through the init method for MCE::Loop, MCE::Grep, and MCE::Map. * Added to doc a use-case example searching a large file with mce_grep_f. The emphasis is comparing the memory consumption against the native grep function as well as time to complete. 1.506 Fri Jan 30 03:00:00 EST 2014 [BUG FIXES] * Fixed issue with all 5 models croaking for an internal '_file' option. This has been broken since the 1.502 release. Methods impacted were: mce_flow_f, mce_grep_f, mce_loop_f, mce_map_f, and mce_stream_f * Specifying a different value for gather on subsequent runs is now taking effect for when workers persist after running. The models impacted are MCE::Flow and MCE::Loop. * Updated _parse_chunk_size in MCE::Util to compute chunk_size correctly for new edge cases. One may call either mce_grep or mce_grep_f for a GLOB or scalar reference as input data. The same also applies for the other models; mce_flow, mce_loop, mce_map and mce_stream. [ENHANCEMENTS] * Updates to MCE::Grep and MCE::Map. The logic is now aware of wantarray for faster processing when storing to a scalar value. In addition, the use_slurpio option is enabled for efficient IO when processing large files. * The task_end option can now be specified for MCE::Stream, although being used internally. * The interval option can take a decimal number. Previously, this wanted a hash reference with up to 3 key/value pairs. Most often, all one needed was delay and not max_nodes or node_id. interval => { delay => 0.05 } ## Choose either format for specifying interval => 0.05 ## delay in 1.506 and above. MCE will ## translate 0.05 to { delay => 0.05 } ## automatically. * Slight optimization to get_ncpu (public method) in MCE::Util. * Tweak to _parse_chunk_size (private method) in MCE::Util. [NEW FEATURES] * Added a new model MCE::Step for transparent use of MCE::Queue when passing data among sub-tasks. MCE::Step is basically a spin off from MCE::Flow with a touch of MCE::Stream. This new model is crazy :) 1.505 Thu Jan 21 01:30:00 EST 2014 [BUG FIXES] * Delete $self->{input_data} inside the worker immediately during spawning (if input_data is an ARRAY, GLOB, or Iterator reference). * Reverted the logic for the RS (record separator) option from the 1.4x code base. This is now working as expected. * Specifying chunk_size => 'auto' via the 'init' method (all 5 models) causes MCE validation to croak due to 'auto' being carried over to the core API. Note that only the 5 models are allowed 'auto' for chunk_size as of this time. * Update to the MCE->do method. Previously, an undef sent back from the callback function ended up as a blank value "". Both "" and undef are now properly captured and sent back to the worker process. * Added an if statement inside the 'abort' method. * Removed an old unlink _store.db statement (old code left behind). * Removed an unused _next variable inside Request.pm. [ENHANCEMENTS] * Enhanced egrep.pl to handle additional options including recursion. This script now supports many egrep options [ceHhiLlmnqRrsv]. * IO performance for examples/cat.pl was improved. The real focus here is demonstrating output order. * The chunk_size option can take a suffix; K (Kilobytes) or M (Megabytes). * The following examples except --max-workers=NUM --chunk-size=NUM options. cat.pl, egrep.pl, findnull.pl, scaling_pings.pl, and wc.pl [NEW FEATURES] * The input_data option can now receive an iterator reference. Added a new example iterator.pl for demonstraton. In addition, there are several examples listed under a new section "SYNTAX for INPUT_DATA" under MCE::Core.pod. * Added a new demo script; bin/mce_grep. This is a wrapper script for the grep binary. This script supports agrep, grep, egrep, fgrep, & tre-agrep. Simply create a link to mce_grep or make a copy. Both Windows and Cygwin are supported as well. Recursion works for all binaries including agrep (-R, -r options). ln mce_grep mce_agrep ln mce_grep mce_egrep ln mce_grep mce_fgrep ln mce_grep mce_tre-agrep Try with the --lang=C option for faster execution time (-i runs faster). Try mce_agrep or mce_tre-agrep against very large files. The speedup is linear and makes good utilization of all available cores on the box. One may specify the chunking level via the --chunk-level option. For large files, specify 'file' (chunks file). For many small files, use 'list'. I have tested against the following GnuWin32 packages found at this URL. http://gnuwin32.sourceforge.net/packages.html tre-0.7.5-bin.zip ## Contains agrep.exe (also runs under Cygwin) grep-2.5.4-bin.zip ## Contains egrep.exe, fgrep.exe, grep.exe libiconv-1.9.2-1-bin.zip ## These are required for the GnuWin32 binaries. libintl-0.14.4-bin.zip pcre-7.0-bin.zip regex-2.7-bin.zip Btw, bin/mce_grep is optional and therefore not installed by default when running make install for the MCE module. Simply copy mce_grep as mce_agrep.pl for Windows. The '.pl' suffix is optional for other environments. 1.504 Tue Oct 29 16:00:00 EST 2013 [BUG FIXES] * MCE::Grep, MCE::Map, and MCE::Stream were failing for mce_grep_s, mce_map_s, and mce_stream_s when specifying chunk_size => 1. [ENHANCEMENTS] * Calibrated 'auto' slightly in MCE::Util::_parse_chunk_size. 1.503 Mon Oct 28 17:00:00 EST 2013 [BUG FIXES] * The previous release introduced a bug by removing the line closing STDERR and STDOUT prior to workers exiting. The proper fix intended is to flush (not close) the handles. Closing them was not the thing to do in the first place due to possibly needed by the END block or WARN/DIE handlers initiated from inside the END block. [ENHANCEMENTS] * Updated README under examples/tbray and examples/matmult. * Changed 0.499 to 0.5 inside the yield method. * Small cosmetic changes otherwise. 1.502 Mon Oct 21 16:00:00 EST 2013 [BUG FIXES] * The END block for all models will return immediately when called by a worker thread or process. Removed the line closing STDERR and STDOUT prior to workers exiting. It turns out that workers were calling shutdown which is not allowed. This impacted MCE::Flow, MCE::Grep, MCE::Loop, MCE::Map, and MCE::Stream. * An update was applied to DESTROY in MCE::Queue to address an edge case during additional testing across several environments. * Added documentation describing the core methods in MCE. I had moved the missing sections to another file some time back and totally forgotten about them when finalizing on MCE::Core.pod for the 1.5 release. [ENHANCEMENTS] * All models will croak when specifying an invalid MCE option. [NEW FEATURES] * One can specify a hash reference for the gather option. Updated documentation describing the gather option and the gather method. 1.501 Wed Oct 16 01:20:00 EST 2013 [BUG FIXES] * Addressed an issue with the worker signal "die" handler (#89538). Eval'd code should not raise an exception causing the app to die. This is working as expected for workers spawned as children. For threads, a die called explicitly inside an eval block will cause the worker to exit (same as previously). However, an eval 'use MissingModule' will no longer cause the thread to die. [ENHANCEMENTS] * Changelog section uses a new format beginning with this release. [NEW FEATURES] * Added examples/flow_model.pl for demonstrating MCE::Flow, MCE::Queue, and MCE->gather. 1.500 Thu Oct 10 01:00:00 EST 2013 * MCE 1.5 is backwards compatible with 1.4 and below. * The documentation, previously MCE.pod, moved to MCE::Core.pod with the examples section placed in MCE::Examples.pod. The MCE.pod file serves as an index page for the various documentation. * IPC has been enhanced with 8 data channels. Many operations run 3x when compared with MCE 1.4. * Five models: MCE::Flow, MCE::Grep, MCE::Loop, MCE::Map, and MCE::Stream. * Hybrid queues via MCE::Queue allowing for normal and priority queues. * MCE::Subs for exporting functions prefixed with mce_; e.g. mce_wid. * All public methods can be called directly using the package name and method e.g. MCE->wid, MCE->run. MCE->new( max_workers => 'auto', user_func => sub { my $wid = MCE->wid; MCE->sendto("STDOUT", "Hello from $wid\n"); } ); MCE->run; * Localize the input scalar $_ prior to calling user_func. Folks can use $_ for input_data and sequence of numbers. Added section to docs explaining DEFAULT INPUT SCALAR. * New options (bounds_only, gather, interval, task_name). The task_end option can now be specified at the top level. See docs for use case. Input_data can be specified inside the first task instead of having to specify this at the top level. Input_data is ignored when specified for tasks other than the first task. user_tasks => [{ input_data => \@list, ... },{ ... } * New public methods chunk_id, gather, freeze, thaw, yield, task_name, print, printf, say * New example (interval.pl). * Optimized the egrep.pl and wc.pl examples. These run much faster. Try these out against large log files. Both examples fly. * Barrier synchronization update. Two sockets are utilized instead of 2 lock files. This, now works wonderfully under the Cygwin environment. For threading, the removal of 2 lock files increases the number of threads allowed from about 1/3rd previously to under 1/2 of ulimit, e.g. int(ulimit -n / 2 - 20). * Removed the logic for determining MAX_OPEN_FILES and MAX_USER_PROCS. MCE no longer has a constraint on max_workers allowed. * Code re-factor work. Added a private method _validate_runstate called by various methods. Organized the code slightly such as placement of methods. 1.415 Mon Jun 17 15:00:00 EST 2013 * Code-refactor in preparation for the upcoming 1.5 release. This completes the 1.4x branch for a very solid and stable release. * IPC optimization. Localize $\ and $/ only as needed. Small tweak to logic when specifying RS (record separator) for input data. Foreach is very communication intensive. Forseq is less so. Chunking was made faster by increasing chunk size from 500 to 2500. I've reached my goal for forseq by reaching 60,000+. A while back, foreach could barely reach 18,000. Before and after results with IPC optimization. (Before) (After) Parallel::Loops 600 600 $mce->foreach 20,000 21,500 (+ 1,500) $mce->forseq 55,000 64,000 (+ 9,000) $mce->forchunk 395,000 450,000 (+ 55,000) * Updated the process method. Allows sequence to be specified as an option. Also allow the hash to be specified as the 1st or 2nd argument. $mce->process( \@input_array, { options } ); $mce->process( { options }, [ 1..1000 ] ); $mce->process( { sequence => [ 20, 40, 2 ] } ); * The forseq method will now honor chunk_size greater than 1. Updated docs with use case. * Added CONST short for EXPORT_CONST, e.g. use MCE CONST => 1; 1.414 Sun Jun 16 13:00:00 EST 2013 * Upstream bug fixes. Mainly chunk_size was not honored when specifying user_tasks and sequence/chunk_size for the first task. Bug is with not setting abort_msg correctly. 1.413 Mon Jun 10 02:00:00 EST 2013 * Fixed typo: Changed local @_ to local $@ inside _parse_max_workers before evaling. 1.412 Sun Jun 09 14:00:00 EST 2013 * Updated the main README file and CHANGES under 1.411 below. * Added support for multiplication and division as well as case insentivity to the private _parse_max_workers method. max_workers => 'auto', ## = MCE::Util::get_ncpu() max_workers => 'Auto-1', ## = MCE::Util::get_ncpu() - 1 max_workers => 'AUTO + 3', ## = MCE::Util::get_ncpu() + 3 max_workers => 'AUTO * 1.5', max_workers => 'auto / 1.333', 1.411 Sun Jun 09 02:00:00 EST 2013 * Reverted the change made in MCE-1.410. That degraded slurp IO on large files. This restores slurp IO performance in MCE. * Corrected an if statement towards the end of the shutdown method. The delay was occurring always when it was met only if MCE was launched from inside a non-main thread. * Addressed a race condition with barrier synchronization under the Cygwin environment. Was not able to reproduce this with other OS's. This takes barrier synchronization work to 100%. Delay statements for Cygwin were removed around barrier synchronization. * Optimized writes to sockets. Arguments passed to print statements are concatenated as one big string. Removed local $/ = $LF when reading from the queue socket. It's not required there since using read and known size. The foreach.pl example (very communication intensive) can now do 20000 in 1 second on my Macbook Pro. Updated benchmark results inside foreach.pl, forchunk.pl, and forseq.pl. * MAX_WORKERS, CHUNK_SIZE, TMP_DIR, FREEZE and THAW can be specified when loading the module. FREEZE and THAW allows one to choose an alternative serialization module if preferred for your project. use Sereal qw(encode_sereal decode_sereal); use MCE FREEZE => \&encode_sereal, THAW => \&decode_sereal; * Passing EXPORT_CONST => 1 will export 3 constants: SELF, CHUNK, CID The "my ($self, $chunk_ref, $chunk_id) = @_" line is not necessary. $_[SELF], $_[CHUNK], $_[CID] refers to $_[0], $_[1], $_[2]. * The MCE::Util module was created. It contains the get_ncpu function. This is largely borrowed from Test::Smoke::Util.pm. MCE supports 'auto' when specifying max_workers. Read doc for other use case. max_workers => 'auto'; ## Same as MCE::Util::get_ncpu() * Re-factored code in preparation for the upcoming 1.5 release. The 1.5 release will use just 3 socket pairs versus 4. I did not want to include that change in this release. Therefore, I decided to take the upstream bug fixes and apply them to the 1.4 base. Re-factored documentation by making use of the full 78 character width per line. * Updated the main README file under the top level dir. 1.410 Tue May 28 23:30:00 EST 2013 * Use threads under MSWin32 for 02* and 03* test scripts. * Removed sysopen/sysseek/sysread to simplify logic due to negligible performance gains over open/seek/read. * Minor updates to documentation. 1.409 Sun May 12 22:45:00 EST 2013 * Croak if user_func is not defined and input_data/sequence is specified. * Fix barrier synchronization when running multiple tasks via user_tasks. * Updated Perl documentation for easier reading through cpan/metapan. * Renamed private method _worker_sequence to _worker_sequence_queue. 1.408 Tue Mar 19 22:00:00 EST 2013 * Minor tweaks here and there to further increase reliability. * Updated the barrier synchronization logic to not stall. Updated the perl docs on mixing "sync" with "do" or "sendto" methods. * Added new "status" method for the manager process. * Added new arguments for MCE::Signal: -no_kill9, -no_sigmsg 1.407 Thu Mar 14 21:00:00 EST 2013 * This marks a tremendous effort in achieving parity across the board from Cygwin to Windows and obviously UNIX. MCE now works beautifully under the Cygwin environment including Perl for Windows. * Up'ed the maximum workers allowed for both Cygwin and Windows to 56 (forking) and 80 (threading). * Barrier synchronization requires an extra semaphore file, therefore the maximum workers allowed under the UNIX environment for threading decreased. It shouldn't be a problem as there are many threads already. * Addressed an issue with barrier synchronization under Cygwin. * Addressed an issue with the die handler for the main worker method when threading. * Thank you for hanging in there. It took quite some time to get there. This is the most stable release thus far for MCE across the board. 1.406 Tue Mar 12 19:00:00 EST 2013 * Added support for barrier synchronization (via new sync method). Added barrier_sync.pl example. * Addressed rounding errors with the sequence generator. The sequence option now follows a bank-teller queuing model when generating numbers. This applies to task 0 only. Other tasks can still specify sequence where numbers will be distributed equally among workers like before. * Optimized the _worker_request_chunk private method. * A take 2 on the matrix multiplication examples. This is better organized with updated README file to include the script running time in the results. 1.405 Mon Mar 04 19:00:00 EST 2013 * Added strassen_pdl_t.pl in the event folks cannot make use of /dev/shm used by the strassen_pdl_s.pl example. * Optimized the 'send' method -- workers process immediately after receiving data. Updated run times in README for the strassen examples. * MCE no longer calls setpgrp by default as of MCE 1.405. There is only one reason to call setpgrp, but many reasons not to. The sole reason was for MCE to run correctly with Daemon::Control. If needed, one can pass the option to MCE::Signal qw(-setpgrp). * Return void in the shutdown method (previously was returning $self). * Tidy code inside sequence generator. 1.404 Sun Feb 24 13:00:00 EST 2013 * Added sess_dir method * Completed work with matmult/* examples Added matmult_pdl_q.pl, removed strassen_pdl_h.pl Added strassen_pdl_o/p/q/r/s.pl Added benchmark results from a 32-way box at the end of the readme * Removed lines setting max limit for files/procs 1.403 Sun Feb 17 15:00:00 EST 2013 * Wrap sub PDL::CLONE_SKIP into a no warnings 'redefine' block MCE now works with PDL::Parallel::threads without any warnings * Added missing examples/matmult/matmult_pdl_n.pl to MANIFEST * Refactored strassen examples, memory consumption reduced by > than 50% * Added matmult_pdl_o.pl -- uses PDL::Parallel::threads to share matrices * Added matmult_pdl_p.pl -- matrix b is read from shared memory, not mmap * Added strassen_pdl_n.pl -- additional improvements to memory reduction * Added strassen_pdl_h.pl -- shown running with 4 workers (half and half) * Re-ran matrix multiplication examples and updated results in README file * Added -no_setpgrp option to MCE::Signal.pm Ctrl-C does not respond when running /usr/bin/time mce_script.pl * Added undef $buffer in a couple of places within MCE.pm * Added David Mertens and Adam Sjøgren to CREDITS * The 'send' method now checks if sending > total workers after spawning not before 1.402 Thr Feb 14 07:30:00 EST 2013 * Updated matrix multiplication examples including README * Added examples/matmult/matmult_pdl_n.pl 1.401 Tue Feb 12 19:00:00 EST 2013 * Added sub PDL::CLONE_SKIP { 1 } to MCE.pm. Running PDL + MCE threads no longer crashes during exiting. * Updated matrix multiplication examples. All examples now work under the Windows environment no matter if threading or forking. Unix is stable as well if wanting to use PDL + MCE and use_threads => 1 or 0. * Added benchmark results for 2048x2048, 4096x4096, and 8192x8192 to the README file under examples/matmult/ * Updated documentation 1.400 Mon Feb 11 07:00:00 EST 2013 * Slight optimization in the _do_callback method * Added 2 new options: user_args and RS (record separator) * Added new send method for sending data to workers after spawning and prior to running * The sequence option can now take an ARRAY reference * Updated documentation on new features * Added matrix multiplication examples 1.306 Sat Jan 05 18:00:00 EST 2013 * Added if statement around setpgrp(0,0). That function is not supported under Windows. * Updated logic for removing any remaining MCE session directories inside MCE::Signal. 1.305 Sat Jan 05 16:00:00 EST 2013 * Added check for $^S to the DIE handler inside the _worker_main method * Added setpgrp(0,0) to MCE::Signal's BEGIN block * MCE::Signal points to a _mce_sess_dir hash in the event of a signal, will remove the sess_dir(s) as well. This is needed when tmp_dir is specified during instantiation and pointing to another location than MCE::Signal::tmp_dir. 1.304 Wed Jan 02 22:00:00 EST 2013 * Added Oliver Gorwits to CREDITS for identifying 2 issues * Direct die to CORE::die inside handler if executing an eval * Undef $mce_spawned_ref if signal was caught (stop_and_exit) * Changed INIT to sub import in MCE.pm 1.303 Tue Jan 01 20:00:00 EST 2013 * Bump version -- MCE.pm VERSION now matches with META.yml * Sorted forchunk, foreach, forseq methods inside MCE.pm * Modified if statement in run method * Task ID is never undef, therefore removed check inside restart_worker and worker_do methods * Added 2 package variables to MCE::Signal $display_die_with_localtime and $display_warn_with_localtime * Completed updates to documentation * Update to forseq.pl and seq_demo.pl examples 1.302 Tue Jan 01 07:30:00 EST 2013 * Fixed validation logic for sequence * Updated the sequence generator -- now supports chunking * Updated seq_demo.pl example to demo user_tasks, sequence, and chunk_size applied together * Documentation updates 1.301 Mon Dec 31 15:00:00 EST 2012 * Emphasis on documentation -- better flow plus additional clarity * Minor updates to sequence option validation * Minor updates to included examples 1.300 Mon Dec 31 06:00:00 EST 2012 * New methods...: chunk_size, restart_worker, task_id, task_wid, tmp_dir * New options...: on_post_exit, on_post_run, sequence * New examples..: forseq.pl, seq_demo.pl * Overhaul to exit method Workers can exit or die without impacting the manager process * Enabled executable bit for test files * Removed localtime output in die and warn handlers * All 3 delay options are consistent whether user_tasks is specified * Removed logic around total_ended count -- replaced with new exit logic * Code refactoring plus documentation updates * Added LICENSE file 1.201 Fri Dec 21 00:00:00 EST 2012 * Added MCE.pod -- moved documentation from MCE.pm to pod file * Added missing use strict/warnings to test scripts * Default to 1 for chunk_size and max_workers if not specified * Test::More is not a requirement to run MCE, only for building * Changed the format for the change log file 1.200 Thu Dec 20 00:00:00 EST 2012 * Added new user_tasks option * Added space between method name and left-paren for header lines in POD * Remove not-needed BSD::Resource and forks inside BEGIN/INIT blocks 1.106 Wed Dec 19 05:00:00 EST 2012 * Added t/pod-coverage.t * Big overhaul of the MCE documentation -- all methods are documented * Croak if method suited for a MCE worker is called by the main MCE process * Croak if method suited for the main MCE process is called by a MCE worker * Updated Makefile.PL to declare the minimum Perl version 1.105 Sun Dec 16 23:00:00 EST 2012 * Completed code re-factoring * Added t/pod.t 1.104 Sun Nov 25 17:00:00 EST 2012 * Added 1 new example to MCE's Perl documentation * Use module::method name versus constant symbol when calling _croak * Croak if session directory is not writeable inside MCE::spawn * Renamed _mce_id to _mce_sid (met to be spawn id actually) * Re-calibrated maximum workers allowed 1.103 Fri Nov 23 13:00:00 EST 2012 * Added writeable check on /dev/shm * Croak if tmp dir is not writeable inside MCE::Signal::import 1.102 Thu Nov 22 13:00:00 EST 2012 * Woohoot !!! MCE now passes with Perl 5.17.x * Added Copying file -- same as in Perl 1.101 Wed Nov 21 16:00:00 EST 2012 * Shifted white space to the left for code blocks inside documentation 1.100 Wed Nov 21 10:00:00 EST 2012 * Completed optimization and shakeout for MCE's existing API * File handles are cached when calling sendto and appending to a file * The sendto method now supports multiple arguments -- see perldoc * Added new option: flush_file 1.008 Sat Nov 17 23:00:00 EST 2012 * Updates to __DIE__ and __WARN__ handling in MCE. These address the unreferenced scalars seen in packaging logs at activestate.com for Perl under Windows: http://code.activestate.com/ppm/MCE/ * Update t/01_load_signal_arg.t -- added check for $ENV{TEMP} This fixes issue seen under Cygwin 1.007 Thu Nov 15 21:30:00 EST 2012 * At last, the "Voila" release :) * Small change to __DIE__ and __WARN__ signal handling for spawn method 1.006 Thu Nov 15 03:30:00 EST 2012 * Added description section to MCE::Signal's Perl doc * Do not set trap on __DIE__ and __WARN__ inside MCE::Signal * Localized __DIE__ and __WARN__ handlers inside MCE instead * Clarify the use of threads in documentation 1.005 Tue Nov 13 06:11:00 EST 2012 * Removed underscore from package globals in MCE::Signal * Optimized _worker_read_handle method in MCE * Updated files under examples/tbray/ 1.004 Mon Nov 12 01:50:00 EST 2012 * Updated examples/mce_usage.readme * Updated examples/wide_finder.pl * Added examples/tbray/README * Added examples/tbray/tbray_baseline1.pl * Added examples/tbray/tbray_baseline2.pl * Added examples/tbray/wf_mce1.pl * Added examples/tbray/wf_mce2.pl * Added examples/tbray/wf_mce3.pl (../wide_finder.pl moved here) * Added examples/tbray/wf_mmap.pl 1.003 Sat Nov 10 12:55:00 EST 2012 * Updated README * Updated images/06_Shared_Sockets.gif * Updated images/10_Scaling_Pings.gif * Added images/11_SNMP_Collection.gif * Minor updates to MCE::Signal 1.002 Thu Nov 08 01:13:10 EST 2012 * Renamed continue method to next 1.001 Wed Nov 07 23:58:20 EST 2012 * Added perl-MCE.spec to trunk http://code.google.com/p/many-core-engine-perl/source/browse/trunk/ * Added CREDITS * Added 3 new methods to MCE.pm: continue, last, and exit * Both foreach & forchunk now call run(1, {...}) to auto-shutdown workers 1.000 Mon Nov 05 10:00:00 EST 2012 * First release MCE-1.608/MANIFEST.SKIP0000644000076400007640000000065012511671246013050 0ustar mariomarioperl-MCE.spec ^# ^MANIFEST\. ^Makefile$ ~$ \.html$ \.old$ ^blib/ _blib$ ^MakeMaker-\d ^\.exists \bdebian\b \btest\b # Avoid version control files. \bRCS\b \bCVS\b ,v$ \B\.svn\b \B\.git\b # Avoid Makemaker generated and utility files. \bMANIFEST\.bak \bMakefile$ \bblib/ \bMakeMaker-\d \bpm_to_blib$ # Avoid Module::Build generated and utility files. \bBuild$ \b_build/ # Avoid temp and backup files. ~$ \.old$ \#$ \b\.# MCE-1.608/lib/0000755000076400007640000000000012511673554011723 5ustar mariomarioMCE-1.608/lib/MCE.pm0000644000076400007640000015764212511672676012710 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE - Many-Core Engine for Perl providing parallel processing capabilities. ## ############################################################################### package MCE; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use Carp (); BEGIN { ## Forking is emulated under the Windows enviornment (excluding Cygwin). ## MCE 1.514+ will load the 'threads' module by default on Windows. ## Folks may specify use_threads => 0 if threads is not desired. if ($^O eq 'MSWin32' && !defined $threads::VERSION) { local $@; local $SIG{__DIE__} = \&_NOOP; eval 'use threads; use threads::shared'; } elsif (defined $threads::VERSION) { unless (defined $threads::shared::VERSION) { local $@; local $SIG{__DIE__} = \&_NOOP; eval 'use threads::shared'; } } } no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use Scalar::Util qw( looks_like_number refaddr ); use Time::HiRes qw( sleep time ); use Fcntl qw( :flock O_RDONLY ); use Symbol qw( qualify_to_ref ); use Storable qw( ); use MCE::Util qw( $LF ); use MCE::Signal; use bytes; our $VERSION = '1.608'; our ($MCE, $_que_read_size, $_que_template, %_valid_fields_new); my ($_prev_mce, %_params_allowed_args, %_valid_fields_task); my ($_is_MSWin32, $_is_winenv, $_is_cygwin); BEGIN { ## Configure pack/unpack template for writing to and from the queue. ## Each entry contains 2 positive numbers: chunk_id & msg_id. ## Attempt 64-bit size, otherwize fall back to machine's word length. { local $@; local $SIG{__DIE__} = \&_NOOP; eval { $_que_read_size = length pack('Q2', 0, 0); }; $_que_template = ($@) ? 'I2' : 'Q2'; $_que_read_size = length pack($_que_template, 0, 0); } ## Attributes used internally. ## _abort_msg _chn _com_lock _dat_lock _i_app_st _i_app_tb _i_wrk_st _wuf ## _chunk_id _mce_sid _mce_tid _pids _run_mode _single_dim _thrs _tids _wid ## _exiting _exit_pid _total_exited _total_running _total_workers _task_wid ## _send_cnt _sess_dir _spawned _state _status _task _task_id _wrk_status ## _init_total_workers _last_sref _rla_data _rla_return ## ## _bsb_r_sock _bsb_w_sock _bse_r_sock _bse_w_sock _com_r_sock _com_w_sock ## _dat_r_sock _dat_w_sock _que_r_sock _que_w_sock _rla_r_sock _rla_w_sock ## _data_channels _lock_chn %_valid_fields_new = map { $_ => 1 } qw( max_workers tmp_dir use_threads user_tasks task_end task_name freeze thaw chunk_size input_data sequence job_delay spawn_delay submit_delay RS flush_file flush_stderr flush_stdout stderr_file stdout_file use_slurpio interval user_args user_begin user_end user_func user_error user_output bounds_only gather init_relay on_post_exit on_post_run parallel_io ); %_params_allowed_args = map { $_ => 1 } qw( chunk_size input_data sequence job_delay spawn_delay submit_delay RS flush_file flush_stderr flush_stdout stderr_file stdout_file use_slurpio interval user_args user_begin user_end user_func user_error user_output bounds_only gather init_relay on_post_exit on_post_run parallel_io ); %_valid_fields_task = map { $_ => 1 } qw( max_workers chunk_size input_data interval sequence task_end task_name bounds_only gather init_relay user_args user_begin user_end user_func RS use_slurpio use_threads parallel_io ); $_is_cygwin = ($^O eq 'cygwin' ) ? 1 : 0; $_is_MSWin32 = ($^O eq 'MSWin32') ? 1 : 0; $_is_winenv = ($_is_cygwin || $_is_MSWin32) ? 1 : 0; ## Create accessor functions. no strict 'refs'; no warnings 'redefine'; for my $_p (qw( chunk_size max_workers task_name tmp_dir user_args )) { *{ $_p } = sub () { my $x = shift; my $self = ref($x) ? $x : $MCE; return $self->{$_p}; }; } for my $_p (qw( chunk_id sess_dir task_id task_wid wid )) { *{ $_p } = sub () { my $x = shift; my $self = ref($x) ? $x : $MCE; return $self->{"_${_p}"}; }; } for my $_p (qw( freeze thaw )) { *{ $_p } = sub () { my $x = shift; my $self = ref($x) ? $x : $MCE; return $self->{$_p}(@_); }; } ## PDL + MCE (spawning as threads) is not stable. Thanks to David Mertens ## for reporting on how he fixed it for his PDL::Parallel::threads module. sub PDL::CLONE_SKIP { return 1; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### use constant { SELF => 0, CHUNK => 1, CID => 2 }; our $_MCE_LOCK : shared = 1; our $_RUN_LOCK : shared = 1; our $_WIN_LOCK : shared = 1; our $TMP_DIR = $MCE::Signal::tmp_dir; our $FREEZE = \&Storable::freeze; our $THAW = \&Storable::thaw; my ($MAX_WORKERS, $CHUNK_SIZE) = (1, 1); my ($_has_threads, $_loaded); sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $MAX_WORKERS = shift and next if ( $_arg eq 'max_workers' ); $CHUNK_SIZE = shift and next if ( $_arg eq 'chunk_size' ); $FREEZE = shift and next if ( $_arg eq 'freeze' ); $THAW = shift and next if ( $_arg eq 'thaw' ); if ( $_arg eq 'sereal' ) { if (shift eq '1') { local $@; eval 'use Sereal qw(encode_sereal decode_sereal)'; unless ($@) { $FREEZE = \&encode_sereal; $THAW = \&decode_sereal; } } next; } if ( $_arg eq 'tmp_dir' ) { $TMP_DIR = shift; my $_e1 = 'is not a directory or does not exist'; my $_e2 = 'is not writeable'; _croak("MCE::import: ($TMP_DIR) $_e1") unless -d $TMP_DIR; _croak("MCE::import: ($TMP_DIR) $_e2") unless -w $TMP_DIR; next; } if ( $_arg eq 'export_const' || $_arg eq 'const' ) { if (shift eq '1') { no strict 'refs'; no warnings 'redefine'; my $_package = caller; *{ $_package . '::SELF' } = \&SELF; *{ $_package . '::CHUNK' } = \&CHUNK; *{ $_package . '::CID' } = \&CID; } next; } _croak("MCE::import: ($_argument) is not a valid module argument"); } ## Automatically spawn threads when threads is present, otherwise processes. $_has_threads = ($INC{'threads.pm'}) ? 1 : 0; ## Preload essential modules. require MCE::Core::Validation; require MCE::Core::Manager; require MCE::Core::Worker; { no strict 'refs'; no warnings 'redefine'; *{ 'MCE::_parse_max_workers' } = \&MCE::Util::_parse_max_workers; } ## Instantiate a module-level instance. $MCE = MCE->new( _module_instance => 1, max_workers => 0 ); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Define constants & variables. ## ############################################################################### use constant { DATA_CHANNELS => 8, ## Maximum IPC "DATA" channels FAST_SEND_SIZE => 1024 * 64 + 128, ## Use one print call if < size MAX_CHUNK_SIZE => 1024 * 1024 * 64, ## Maximum chunk size allowed MAX_RECS_SIZE => 8192, ## Reads # of records if <= value ## Reads # of bytes if > value OUTPUT_W_ABT => 'W~ABT', ## Worker has aborted OUTPUT_W_DNE => 'W~DNE', ## Worker has completed OUTPUT_W_RLA => 'W~RLA', ## Worker has relayed OUTPUT_W_EXT => 'W~EXT', ## Worker has exited OUTPUT_A_ARY => 'A~ARY', ## Array << Array OUTPUT_S_GLB => 'S~GLB', ## Scalar << Glob FH OUTPUT_U_ITR => 'U~ITR', ## User << Iterator OUTPUT_A_CBK => 'A~CBK', ## Callback w/ multiple args OUTPUT_S_CBK => 'S~CBK', ## Callback w/ 1 scalar arg OUTPUT_N_CBK => 'N~CBK', ## Callback w/ no args OUTPUT_A_GTR => 'A~GTR', ## Gather w/ multiple args OUTPUT_R_GTR => 'R~GTR', ## Gather w/ 1 reference arg OUTPUT_S_GTR => 'S~GTR', ## Gather w/ 1 scalar arg OUTPUT_O_SND => 'O~SND', ## Send >> STDOUT OUTPUT_E_SND => 'E~SND', ## Send >> STDERR OUTPUT_F_SND => 'F~SND', ## Send >> File OUTPUT_D_SND => 'D~SND', ## Send >> File descriptor OUTPUT_B_SYN => 'B~SYN', ## Barrier sync - begin OUTPUT_E_SYN => 'E~SYN', ## Barrier sync - end READ_FILE => 0, ## Worker reads file handle READ_MEMORY => 1, ## Worker reads memory handle REQUEST_ARRAY => 0, ## Worker requests next array chunk REQUEST_GLOB => 1, ## Worker requests next glob chunk SENDTO_FILEV1 => 0, ## Worker sends to 'file', $a, '/path' SENDTO_FILEV2 => 1, ## Worker sends to 'file:/path', $a SENDTO_STDOUT => 2, ## Worker sends to STDOUT SENDTO_STDERR => 3, ## Worker sends to STDERR SENDTO_FD => 4, ## Worker sends to file descriptor WANTS_UNDEF => 0, ## Callee wants nothing WANTS_ARRAY => 1, ## Callee wants list WANTS_SCALAR => 2, ## Callee wants scalar WANTS_REF => 3 ## Callee wants H/A/S ref }; my (%_mce_sess_dir, %_mce_spawned); my $_mce_count = 0; MCE::Signal::_set_session_vars(\%_mce_sess_dir, \%_mce_spawned); sub _clean_sessions { my ($_mce_sid) = @_; for my $_s (keys %_mce_spawned) { delete $_mce_spawned{$_s} unless ($_s eq $_mce_sid); } return; } sub _clear_session { my ($_mce_sid) = @_; delete $_mce_spawned{$_mce_sid}; for my $_s (keys %_mce_spawned) { (delete $_mce_spawned{$_s})->shutdown(1); } return; } sub DESTROY { } ############################################################################### ## ---------------------------------------------------------------------------- ## Plugin interface for external modules plugging into MCE, e.g. MCE::Queue. ## ############################################################################### my (%_plugin_function, @_plugin_loop_begin, @_plugin_loop_end); my (%_plugin_list, @_plugin_worker_init); sub _attach_plugin { my $_ext_module = caller; unless (exists $_plugin_list{$_ext_module}) { $_plugin_list{$_ext_module} = 1; my $_ext_output_function = $_[0]; my $_ext_output_loop_begin = $_[1]; my $_ext_output_loop_end = $_[2]; my $_ext_worker_init = $_[3]; return unless (ref $_ext_output_function eq 'HASH'); for my $_p (keys %{ $_ext_output_function }) { $_plugin_function{$_p} = $_ext_output_function->{$_p} unless (exists $_plugin_function{$_p}); } push @_plugin_loop_begin, $_ext_output_loop_begin if (ref $_ext_output_loop_begin eq 'CODE'); push @_plugin_loop_end, $_ext_output_loop_end if (ref $_ext_output_loop_end eq 'CODE'); push @_plugin_worker_init, $_ext_worker_init if (ref $_ext_worker_init eq 'CODE'); } @_ = (); return; } ## Functions for saving and restoring $MCE. This is mainly helpful for ## modules using MCE. e.g. MCE::Map. sub _restore_state { $MCE = $_prev_mce; $_prev_mce = undef; return; } sub _save_state { $_prev_mce = $MCE; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## New instance instantiation. ## ############################################################################### sub new { my ($class, %self) = @_; @_ = (); bless(\%self, ref($class) || $class); ## Public options. $self{max_workers} ||= $MAX_WORKERS; $self{chunk_size} ||= $CHUNK_SIZE; $self{tmp_dir} ||= $TMP_DIR; $self{freeze} ||= $FREEZE; $self{thaw} ||= $THAW; $self{task_name} ||= 'MCE'; if (exists $self{_module_instance}) { $self{_init_total_workers} = $self{max_workers}; $self{_chunk_id} = $self{_task_wid} = $self{_wrk_status} = 0; $self{_spawned} = $self{_task_id} = $self{_wid} = 0; $self{_data_channels} = 1; return \%self; } for my $_p (keys %self) { _croak("MCE::new: ($_p) is not a valid constructor argument") unless (exists $_valid_fields_new{$_p}); } if (defined $self{use_threads}) { if (!$_has_threads && $self{use_threads} ne '0') { my $_msg = "\n"; $_msg .= "## Please include threads support prior to loading MCE\n"; $_msg .= "## when specifying use_threads => $self{use_threads}\n"; $_msg .= "\n"; _croak($_msg); } } else { $self{use_threads} = ($_has_threads) ? 1 : 0; } $self{flush_file} ||= 0; $self{flush_stderr} ||= 0; $self{flush_stdout} ||= 0; $self{use_slurpio} ||= 0; $self{parallel_io} ||= 0; ## ------------------------------------------------------------------------- ## Validation. _croak("MCE::new: ($self{tmp_dir}) is not a directory or does not exist") unless (-d $self{tmp_dir}); _croak("MCE::new: ($self{tmp_dir}) is not writeable") unless (-w $self{tmp_dir}); if (defined $self{user_tasks}) { _croak('MCE::new: (user_tasks) is not an ARRAY reference') unless (ref $self{user_tasks} eq 'ARRAY'); $self{max_workers} = _parse_max_workers($self{max_workers}); $self{init_relay} = $self{user_tasks}->[0]->{init_relay} if ($self{user_tasks}->[0]->{init_relay}); for my $_task (@{ $self{user_tasks} }) { for my $_p (keys %{ $_task }) { _croak("MCE::new: ($_p) is not a valid task constructor argument") unless (exists $_valid_fields_task{$_p}); } $_task->{max_workers} = $self{max_workers} unless (defined $_task->{max_workers}); $_task->{use_threads} = $self{use_threads} unless (defined $_task->{use_threads}); bless($_task, ref(\%self) || \%self); } ## File locking fails under Cygwin between children and threads. ## Must be all children or all threads, not intermixed. if ($_is_cygwin) { my (%_values, $_value); for my $_task (@{ $self{user_tasks} }) { $_value = (defined $_task->{use_threads}) ? $_task->{use_threads} : $self{use_threads}; $_values{$_value} = ''; } _croak('MCE::new: (cannot mix) use_threads => 0/1 under Cygwin') if (keys %_values > 1); } } _validate_args(\%self); ## ------------------------------------------------------------------------- ## Private options. Limit chunk_size. $self{_chunk_id} = 0; ## Chunk ID $self{_send_cnt} = 0; ## Number of times data was sent via send $self{_spawned} = 0; ## Have workers been spawned $self{_task_id} = 0; ## Task ID, starts at 0 (array index) $self{_task_wid} = 0; ## Task Worker ID, starts at 1 per task $self{_wid} = 0; ## Worker ID, starts at 1 per MCE instance $self{_wrk_status} = 0; ## For saving exit status when worker exits $self{chunk_size} = MAX_CHUNK_SIZE if ($self{chunk_size} > MAX_CHUNK_SIZE); my $_total_workers = 0; if (defined $self{user_tasks}) { $_total_workers += $_->{max_workers} for (@{ $self{user_tasks} }); } else { $_total_workers = $self{max_workers}; } $self{_init_total_workers} = $_total_workers; $self{_data_channels} = ($_total_workers < DATA_CHANNELS) ? $_total_workers : DATA_CHANNELS; $self{_last_sref} = (ref $self{input_data} eq 'SCALAR') ? refaddr($self{input_data}) : 0; $self{_lock_chn} = ($_total_workers > DATA_CHANNELS) ? 1 : 0; $MCE = \%self if ($MCE->{_wid} == 0); return \%self; } ############################################################################### ## ---------------------------------------------------------------------------- ## Spawn method. ## ############################################################################### sub spawn { my $x = shift; my $self = ref($x) ? $x : $MCE; @_ = (); _croak('MCE::spawn: method cannot be called by the worker process') if ($self->{_wid}); ## Return if workers have already been spawned. return $self if ($self->{_spawned}); lock $_MCE_LOCK if ($_has_threads); ## Obtain MCE lock. lock $_WIN_LOCK if ($_has_threads && $_is_winenv); my $_die_handler = $SIG{__DIE__}; $SIG{__DIE__} = \&_die; my $_warn_handler = $SIG{__WARN__}; $SIG{__WARN__} = \&_warn; ## Configure tid/sid for this instance here, not in the new method above. ## We want the actual thread id in which spawn was called under. unless ($self->{_mce_tid}) { $self->{_mce_tid} = ($_has_threads) ? threads->tid() : ''; $self->{_mce_tid} = '' unless (defined $self->{_mce_tid}); $self->{_mce_sid} = $$ .'.'. $self->{_mce_tid} .'.'. (++$_mce_count); } my $_mce_sid = $self->{_mce_sid}; my $_sess_dir = $self->{_sess_dir}; my $_tmp_dir = $self->{tmp_dir}; ## Create temp dir. unless ($_sess_dir) { _croak("MCE::spawn: ($_tmp_dir) is not defined") if (!defined $_tmp_dir || $_tmp_dir eq ''); _croak("MCE::spawn: ($_tmp_dir) is not a directory or does not exist") unless (-d $_tmp_dir); _croak("MCE::spawn: ($_tmp_dir) is not writeable") unless (-w $_tmp_dir); my $_cnt = 0; $_sess_dir = $self->{_sess_dir} = "$_tmp_dir/$_mce_sid"; $_sess_dir = $self->{_sess_dir} = "$_tmp_dir/$_mce_sid." . (++$_cnt) while ( !(mkdir $_sess_dir, 0770) ); $_mce_sess_dir{$_sess_dir} = 1; } ## Obtain lock. open my $_COM_LOCK, '+>>:raw:stdio', "$_sess_dir/_com.lock" or die "(M) open error $_sess_dir/_com.lock: $!\n"; flock $_COM_LOCK, LOCK_EX; ## ------------------------------------------------------------------------- my $_data_channels = $self->{_data_channels}; my $_max_workers = _get_max_workers($self); my $_use_threads = $self->{use_threads}; ## Create socket pairs for IPC. MCE::Util::_make_socket_pair($self, qw(_bsb_r_sock _bsb_w_sock)); # sync MCE::Util::_make_socket_pair($self, qw(_bse_r_sock _bse_w_sock)); # sync MCE::Util::_make_socket_pair($self, qw(_com_r_sock _com_w_sock)); # core MCE::Util::_make_socket_pair($self, qw(_que_r_sock _que_w_sock)); # core MCE::Util::_make_socket_pair($self, qw(_dat_r_sock _dat_w_sock), $_) # core for (0 .. $_data_channels); if (defined $self->{init_relay}) { # relay unless (defined $MCE::Relay::VERSION) { require MCE::Relay; MCE::Relay->import(); } MCE::Util::_make_socket_pair($self, qw(_rla_r_sock _rla_w_sock), $_) for (0 .. $_max_workers - 1); } ## ------------------------------------------------------------------------- ## Spawn workers. $_mce_spawned{$_mce_sid} = $self; $self->{_pids} = []; $self->{_thrs} = []; $self->{_tids} = []; $self->{_status} = []; $self->{_state} = []; $self->{_task} = []; if (!defined $self->{user_tasks}) { $self->{_total_workers} = $_max_workers; if (defined $_use_threads && $_use_threads == 1) { _dispatch_thread($self, $_) for (1 .. $_max_workers); } else { _dispatch_child($self, $_) for (1 .. $_max_workers); } $self->{_task}->[0] = { _total_workers => $_max_workers }; for my $_i (1 .. $_max_workers) { keys(%{ $self->{_state}->[$_i] }) = 5; $self->{_state}->[$_i] = { _task => undef, _task_id => undef, _task_wid => undef, _params => undef, _chn => $_i % $_data_channels + 1 } } } else { my ($_task_id, $_wid); $_task_id = $_wid = $self->{_total_workers} = 0; $self->{_total_workers} += $_->{max_workers} for (@{ $self->{user_tasks} }); for my $_task (@{ $self->{user_tasks} }) { my $_tsk_use_threads = $_task->{use_threads}; if (defined $_tsk_use_threads && $_tsk_use_threads == 1) { _dispatch_thread($self, ++$_wid, $_task, $_task_id, $_) for (1 .. $_task->{max_workers}); } else { _dispatch_child($self, ++$_wid, $_task, $_task_id, $_) for (1 .. $_task->{max_workers}); } $_task_id++; } $_task_id = $_wid = 0; for my $_task (@{ $self->{user_tasks} }) { $self->{_task}->[$_task_id] = { _total_running => 0, _total_workers => $_task->{max_workers} }; for my $_i (1 .. $_task->{max_workers}) { keys(%{ $self->{_state}->[++$_wid] }) = 5; $self->{_state}->[$_wid] = { _task => $_task, _task_id => $_task_id, _task_wid => $_i, _params => undef, _chn => $_wid % $_data_channels + 1 } } $_task_id++; } } ## ------------------------------------------------------------------------- $self->{_com_lock} = $_COM_LOCK; $self->{_send_cnt} = 0; $self->{_spawned} = 1; ## Release lock. flock $_COM_LOCK, LOCK_UN; $SIG{__DIE__} = $_die_handler; $SIG{__WARN__} = $_warn_handler; $MCE = $self if ($MCE->{_wid} == 0); return $self; } ############################################################################### ## ---------------------------------------------------------------------------- ## "for" sugar methods, process method, and relay stubs for MCE::Relay. ## ############################################################################### sub forchunk { require MCE::Candy unless (defined $MCE::Candy::VERSION); return MCE::Candy::forchunk(@_); } sub foreach { require MCE::Candy unless (defined $MCE::Candy::VERSION); return MCE::Candy::foreach(@_); } sub forseq { require MCE::Candy unless (defined $MCE::Candy::VERSION); return MCE::Candy::forseq(@_); } sub process { my $x = shift; my $self = ref($x) ? $x : $MCE; _validate_runstate($self, 'MCE::process'); my ($_input_data, $_params_ref); if (ref $_[0] eq 'HASH') { $_input_data = $_[1]; $_params_ref = $_[0]; } else { $_input_data = $_[0]; $_params_ref = $_[1]; } @_ = (); ## Set input data. if (defined $_input_data) { $_params_ref->{input_data} = $_input_data; } elsif ( !defined $_params_ref->{input_data} && !defined $_params_ref->{sequence} ) { _croak('MCE::process: (input_data or sequence) is not specified'); } ## Pass 0 to "not" auto-shutdown after processing. $self->run(0, $_params_ref); return $self; } sub relay_final { } sub relay_recv { _croak('MCE::relay: (init_relay) is not specified') unless (defined $MCE->{init_relay}); } sub relay (;&) { _croak('MCE::relay: (init_relay) is not specified') unless (defined $MCE->{init_relay}); } ############################################################################### ## ---------------------------------------------------------------------------- ## Restart worker method. ## ############################################################################### sub restart_worker { my $x = shift; my $self = ref($x) ? $x : $MCE; @_ = (); _croak('MCE::restart_worker: method cannot be called by the worker process') if ($self->{_wid}); my $_wid = $self->{_exited_wid}; my $_params = $self->{_state}->[$_wid]->{_params}; my $_task_wid = $self->{_state}->[$_wid]->{_task_wid}; my $_task_id = $self->{_state}->[$_wid]->{_task_id}; my $_task = $self->{_state}->[$_wid]->{_task}; my $_chn = $self->{_state}->[$_wid]->{_chn}; $_params->{_chn} = $_chn; my $_use_threads = (defined $_task_id) ? $_task->{use_threads} : $self->{use_threads}; $self->{_task}->[$_task_id]->{_total_running} += 1 if (defined $_task_id); $self->{_task}->[$_task_id]->{_total_workers} += 1 if (defined $_task_id); $self->{_total_running} += 1; $self->{_total_workers} += 1; if (defined $_use_threads && $_use_threads == 1) { _dispatch_thread($self, $_wid, $_task, $_task_id, $_task_wid, $_params); } else { _dispatch_child($self, $_wid, $_task, $_task_id, $_task_wid, $_params); } sleep 0.001; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Run method. ## ############################################################################### sub run { my $x = shift; my $self = ref($x) ? $x : $MCE; _croak('MCE::run: method cannot be called by the worker process') if ($self->{_wid}); my ($_auto_shutdown, $_params_ref); if (ref $_[0] eq 'HASH') { $_auto_shutdown = (defined $_[1]) ? $_[1] : 1; $_params_ref = $_[0]; } else { $_auto_shutdown = (defined $_[0]) ? $_[0] : 1; $_params_ref = $_[1]; } @_ = (); my $_has_user_tasks = (defined $self->{user_tasks}) ? 1 : 0; my $_requires_shutdown = 0; ## Unset params if workers have already been sent user_data via send. ## Set user_func to NOOP if not specified. $_params_ref = undef if ($self->{_send_cnt}); if (!defined $self->{user_func} && !defined $_params_ref->{user_func}) { $self->{user_func} = \&_NOOP; } ## Set user specified params if specified. ## Shutdown workers if determined by _sync_params or if processing a ## scalar reference. Workers need to be restarted in order to pick up ## on the new code or scalar reference. if (defined $_params_ref && ref $_params_ref eq 'HASH') { $_requires_shutdown = _sync_params($self, $_params_ref); _validate_args($self); } if ($_has_user_tasks) { $self->{input_data} = $self->{user_tasks}->[0]->{input_data} if ($self->{user_tasks}->[0]->{input_data}); $self->{use_slurpio} = $self->{user_tasks}->[0]->{use_slurpio} if ($self->{user_tasks}->[0]->{use_slurpio}); $self->{parallel_io} = $self->{user_tasks}->[0]->{parallel_io} if ($self->{user_tasks}->[0]->{parallel_io}); $self->{RS} = $self->{user_tasks}->[0]->{RS} if ($self->{user_tasks}->[0]->{RS}); } if (ref $self->{input_data} eq 'SCALAR') { if (refaddr($self->{input_data}) != $self->{_last_sref}) { $_requires_shutdown = 1; } $self->{_last_sref} = refaddr($self->{input_data}); } $self->shutdown() if ($_requires_shutdown); ## ------------------------------------------------------------------------- $self->{_wrk_status} = 0; ## Spawn workers. $self->spawn() unless ($self->{_spawned}); return $self unless ($self->{_total_workers}); local $SIG{__DIE__} = \&_die; local $SIG{__WARN__} = \&_warn; $MCE = $self if ($MCE->{_wid} == 0); my ($_input_data, $_input_file, $_input_glob, $_seq); my ($_abort_msg, $_first_msg, $_run_mode, $_single_dim); my $_chunk_size = $self->{chunk_size}; $_seq = ($_has_user_tasks && $self->{user_tasks}->[0]->{sequence}) ? $self->{user_tasks}->[0]->{sequence} : $self->{sequence}; ## Determine run mode for workers. if (defined $_seq) { my ($_begin, $_end, $_step, $_fmt) = (ref $_seq eq 'ARRAY') ? @{ $_seq } : ($_seq->{begin}, $_seq->{end}, $_seq->{step}); $_chunk_size = $self->{user_tasks}->[0]->{chunk_size} if ($_has_user_tasks && $self->{user_tasks}->[0]->{chunk_size}); $_run_mode = 'sequence'; $_abort_msg = int(($_end - $_begin) / $_step / $_chunk_size) + 1; $_first_msg = 0; } elsif (defined $self->{input_data}) { my $_ref = ref $self->{input_data}; if ($_ref eq 'ARRAY') { ## Array mode. $_run_mode = 'array'; $_input_data = $self->{input_data}; $_input_file = $_input_glob = undef; $_single_dim = 1 if (ref $_input_data->[0] eq ''); $_abort_msg = 0; ## Flag: Has Data: No $_first_msg = 1; ## Flag: Has Data: Yes if (@{ $_input_data } == 0) { return $self->shutdown() if ($_auto_shutdown == 1); } } elsif ($_ref eq 'GLOB' || $_ref =~ /^IO::/) { ## Glob mode. $_run_mode = 'glob'; $_input_glob = $self->{input_data}; $_input_data = $_input_file = undef; $_abort_msg = 0; ## Flag: Has Data: No $_first_msg = 1; ## Flag: Has Data: Yes } elsif ($_ref eq 'CODE') { ## Iterator mode. $_run_mode = 'iterator'; $_input_data = $self->{input_data}; $_input_file = $_input_glob = undef; $_abort_msg = 0; ## Flag: Has Data: No $_first_msg = 1; ## Flag: Has Data: Yes } elsif ($_ref eq '') { ## File mode. $_run_mode = 'file'; $_input_file = $self->{input_data}; $_input_data = $_input_glob = undef; $_abort_msg = (-s $_input_file) + 1; $_first_msg = 0; ## Begin at offset position if ((-s $_input_file) == 0) { return $self->shutdown() if ($_auto_shutdown == 1); } } elsif ($_ref eq 'SCALAR') { ## Memory mode. $_run_mode = 'memory'; $_input_data = $_input_file = $_input_glob = undef; $_abort_msg = length(${ $self->{input_data} }) + 1; $_first_msg = 0; ## Begin at offset position if (length(${ $self->{input_data} }) == 0) { return $self->shutdown() if ($_auto_shutdown == 1); } } else { _croak('MCE::run: (input_data) is not valid'); } } else { ## Nodata mode. $_run_mode = 'nodata'; $_abort_msg = undef; } ## ------------------------------------------------------------------------- my $_COM_LOCK = $self->{_com_lock}; my $_bounds_only = $self->{bounds_only}; my $_interval = $self->{interval}; my $_sequence = $self->{sequence}; my $_user_args = $self->{user_args}; my $_use_slurpio = $self->{use_slurpio}; my $_parallel_io = $self->{parallel_io}; my $_sess_dir = $self->{_sess_dir}; my $_total_workers = $self->{_total_workers}; my $_send_cnt = $self->{_send_cnt}; my $_RS = $self->{RS}; ## Begin processing. unless ($_send_cnt) { my %_params = ( '_abort_msg' => $_abort_msg, '_run_mode' => $_run_mode, '_chunk_size' => $_chunk_size, '_single_dim' => $_single_dim, '_input_file' => $_input_file, '_interval' => $_interval, '_sequence' => $_sequence, '_bounds_only' => $_bounds_only, '_use_slurpio' => $_use_slurpio, '_parallel_io' => $_parallel_io, '_user_args' => $_user_args, '_RS' => $_RS, ); my %_params_nodata = ( '_abort_msg' => undef, '_run_mode' => 'nodata', '_chunk_size' => $_chunk_size, '_single_dim' => $_single_dim, '_input_file' => $_input_file, '_interval' => $_interval, '_sequence' => $_sequence, '_bounds_only' => $_bounds_only, '_use_slurpio' => $_use_slurpio, '_parallel_io' => $_parallel_io, '_user_args' => $_user_args, '_RS' => $_RS, ); local $\ = undef; local $/ = $LF; ## Obtain MCE or RUN lock. lock $_MCE_LOCK if ($_has_threads && $_is_winenv); lock $_RUN_LOCK if ($_has_threads && !$_is_winenv); my ($_frozen_nodata, $_wid, %_task0_wids); my $_BSE_W_SOCK = $self->{_bse_w_sock}; my $_COM_R_SOCK = $self->{_com_r_sock}; my $_submit_delay = $self->{submit_delay}; my $_frozen_params = $self->{freeze}(\%_params); $_frozen_nodata = $self->{freeze}(\%_params_nodata) if ($_has_user_tasks); if ($_has_user_tasks) { for my $_i (1 .. @{ $self->{_state} } - 1) { $_task0_wids{$_i} = 1 unless ($self->{_state}->[$_i]->{_task_id}); }} ## Insert the first message into the queue if defined. if (defined $_first_msg) { my $_QUE_W_SOCK = $self->{_que_w_sock}; syswrite $_QUE_W_SOCK, pack($_que_template, 0, $_first_msg); } ## Submit params data to workers. for my $_i (1 .. $_total_workers) { print {$_COM_R_SOCK} $_i . $LF; chomp($_wid = <$_COM_R_SOCK>); if (!$_has_user_tasks || exists $_task0_wids{$_wid}) { print {$_COM_R_SOCK} length($_frozen_params) . $LF . $_frozen_params; $self->{_state}->[$_wid]->{_params} = \%_params; } else { print {$_COM_R_SOCK} length($_frozen_nodata) . $LF . $_frozen_nodata; $self->{_state}->[$_wid]->{_params} = \%_params_nodata; } <$_COM_R_SOCK>; if (defined $_submit_delay && $_submit_delay > 0.0) { sleep $_submit_delay; } sleep 0.003 if ($_is_winenv); } ## Obtain lock. flock $_COM_LOCK, LOCK_EX; if (($self->{_mce_tid} ne '' && $self->{_mce_tid} ne '0') || $_is_winenv) { sleep $_is_winenv ? 0.005 : 0.002; } syswrite $_BSE_W_SOCK, $LF for (1 .. $_total_workers); } ## ------------------------------------------------------------------------- $self->{_total_exited} = 0; if ($_send_cnt) { $self->{_total_running} = $_send_cnt; $self->{_task}->[0]->{_total_running} = $_send_cnt; } else { $self->{_total_running} = $_total_workers; if (defined $self->{user_tasks}) { $_->{_total_running} = $_->{_total_workers} for (@{ $self->{_task} }); } } ## Call the output function. if ($self->{_total_running} > 0) { $self->{_abort_msg} = $_abort_msg; $self->{_run_mode} = $_run_mode; $self->{_single_dim} = $_single_dim; _output_loop( $self, $_input_data, $_input_glob, \%_plugin_function, \@_plugin_loop_begin, \@_plugin_loop_end ); undef $self->{_abort_msg}; undef $self->{_run_mode}; undef $self->{_single_dim}; } unless ($_send_cnt) { ## Remove the last message from the queue. unless ($_run_mode eq 'nodata') { if (defined $self->{_que_r_sock}) { my $_QUE_R_SOCK = $self->{_que_r_sock}; sysread $_QUE_R_SOCK, (my $_next), $_que_read_size; } } ## Release lock. flock $_COM_LOCK, LOCK_UN; } $self->{_send_cnt} = 0; ## Shutdown workers (also, if any workers have exited). if ($_auto_shutdown == 1 || $self->{_total_exited} > 0) { $self->shutdown(); } return $self; } ############################################################################### ## ---------------------------------------------------------------------------- ## Send method. ## ############################################################################### sub send { my $x = shift; my $self = ref($x) ? $x : $MCE; _croak('MCE::send: method cannot be called by the worker process') if ($self->{_wid}); _croak('MCE::send: method cannot be called while running') if ($self->{_total_running}); _croak('MCE::send: method cannot be used with input_data or sequence') if (defined $self->{input_data} || defined $self->{sequence}); _croak('MCE::send: method cannot be used with user_tasks') if (defined $self->{user_tasks}); my $_data_ref; if (ref $_[0] eq 'ARRAY' || ref $_[0] eq 'HASH' || ref $_[0] eq 'PDL') { $_data_ref = $_[0]; } else { _croak('MCE::send: ARRAY, HASH, or a PDL reference is not specified'); } @_ = (); $self->{_send_cnt} = 0 unless (defined $self->{_send_cnt}); ## ------------------------------------------------------------------------- ## Spawn workers. $self->spawn() unless ($self->{_spawned}); _croak('MCE::send: Sending greater than # of workers is not allowed') if ($self->{_send_cnt} >= $self->{_task}->[0]->{_total_workers}); local $SIG{__DIE__} = \&_die; local $SIG{__WARN__} = \&_warn; ## Begin data submission. { local $\ = undef; local $/ = $LF; my $_COM_R_SOCK = $self->{_com_r_sock}; my $_sess_dir = $self->{_sess_dir}; my $_submit_delay = $self->{submit_delay}; my $_frozen_data = $self->{freeze}($_data_ref); my $_len = length $_frozen_data; ## Submit data to worker. print {$_COM_R_SOCK} '_data' . $LF; <$_COM_R_SOCK>; if ($_len < FAST_SEND_SIZE) { print {$_COM_R_SOCK} $_len . $LF . $_frozen_data; } else { print {$_COM_R_SOCK} $_len . $LF; print {$_COM_R_SOCK} $_frozen_data; } <$_COM_R_SOCK>; if (defined $_submit_delay && $_submit_delay > 0.0) { sleep $_submit_delay; } sleep 0.003 if ($_is_winenv); } $self->{_send_cnt} += 1; return $self; } ############################################################################### ## ---------------------------------------------------------------------------- ## Shutdown method. ## ############################################################################### sub shutdown { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_no_lock = shift || 0; @_ = (); ## Return if workers have not been spawned or have already been shutdown. return unless (defined $MCE::Signal::tmp_dir); return unless ($self->{_spawned}); ## Wait for workers to complete processing before shutting down. _validate_runstate($self, 'MCE::shutdown'); $self->run(0) if ($self->{_send_cnt}); local $SIG{__DIE__} = \&_die; local $SIG{__WARN__} = \&_warn; lock $_MCE_LOCK if ($_has_threads && ! $_no_lock); my $_COM_R_SOCK = $self->{_com_r_sock}; my $_data_channels = $self->{_data_channels}; my $_mce_sid = $self->{_mce_sid}; my $_sess_dir = $self->{_sess_dir}; my $_total_workers = $self->{_total_workers}; my $_lock_chn = $self->{_lock_chn}; ## Delete entry. delete $_mce_spawned{$_mce_sid}; ## ------------------------------------------------------------------------- ## Notify workers to exit loop. Close _bse_w_sock first afterwards. local ($!, $?); local $\ = undef; local $/ = $LF; for (1 .. $_total_workers) { print {$_COM_R_SOCK} '_exit' . $LF; <$_COM_R_SOCK>; } sleep 0.005 if ($_is_winenv); $_COM_R_SOCK = undef; MCE::Util::_destroy_sockets( $self, qw( _bse_w_sock _bse_r_sock _bsb_w_sock _bsb_r_sock _com_w_sock _com_r_sock _que_w_sock _que_r_sock _dat_w_sock _dat_r_sock _rla_w_sock _rla_r_sock )); ## Reap children and/or threads. if (defined $self->{_pids} && @{ $self->{_pids} } > 0) { my $_list = $self->{_pids}; for my $i (0 .. @{ $_list }) { waitpid $_list->[$i], 0 if ($_list->[$i]); } } if (defined $self->{_thrs} && @{ $self->{_thrs} } > 0) { my $_list = $self->{_thrs}; for my $i (0 .. @{ $_list }) { ${ $_list->[$i] }->join() if ($_list->[$i]); } } close $self->{_com_lock}; undef $self->{_com_lock}; ## ------------------------------------------------------------------------- ## Remove session directory. if (defined $_sess_dir) { unlink "$_sess_dir/_dat.lock.e" if (-e "$_sess_dir/_dat.lock.e"); if ($_lock_chn) { unlink "$_sess_dir/_dat.lock.$_" for (1 .. $_data_channels); } unlink "$_sess_dir/_com.lock"; rmdir "$_sess_dir"; delete $_mce_sess_dir{$_sess_dir}; } ## Reset instance. @{$self->{_pids}} = (); @{$self->{_thrs}} = (); @{$self->{_tids}} = (); @{$self->{_state}} = (); @{$self->{_status}} = (); @{$self->{_task}} = (); $self->{_mce_sid} = $self->{_mce_tid} = $self->{_sess_dir} = undef; $self->{_chunk_id} = $self->{_send_cnt} = $self->{_spawned} = 0; $self->{_total_running} = $self->{_total_exited} = 0; $self->{_total_workers} = 0; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Barrier sync and yield methods. ## ############################################################################### sub sync { my $x = shift; my $self = ref($x) ? $x : $MCE; _croak('MCE::sync: method cannot be called by the manager process') unless ($self->{_wid}); ## Barrier synchronization is supported for task 0 at this time. ## Note: Workers are assigned task_id 0 when omitting user_tasks. return if ($self->{_task_id} > 0); my $_chn = $self->{_chn}; my $_DAT_LOCK = $self->{_dat_lock}; my $_DAT_W_SOCK = $self->{_dat_w_sock}->[0]; my $_BSB_R_SOCK = $self->{_bsb_r_sock}; my $_BSE_R_SOCK = $self->{_bse_r_sock}; my $_lock_chn = $self->{_lock_chn}; my $_buffer; local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); ## Notify the manager process (begin). flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_B_SYN . $LF . $_chn . $LF; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); ## Wait here until all workers (task_id 0) have synced. sysread $_BSB_R_SOCK, $_buffer, 1; ## Notify the manager process (end). flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_E_SYN . $LF . $_chn . $LF; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); ## Wait here until all workers (task_id 0) have un-synced. sysread $_BSE_R_SOCK, $_buffer, 1; return; } sub yield { my $x = shift; my $self = ref($x) ? $x : $MCE; return unless ($self->{_i_wrk_st}); return unless ($self->{_task_wid}); my $_delay = $self->{_i_wrk_st} - time; my $_count; if ($_delay < 0.0) { $_count = int($_delay * -1 / $self->{_i_app_tb} + 0.5) + 1; $_delay += $self->{_i_app_tb} * $_count; } sleep $_delay if ($_delay > 0.0); if ($_count && $_count > 2_000_000_000) { $self->{_i_wrk_st} = time; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Miscellaneous methods: abort exit last next pid status. ## ############################################################################### ## Abort current job. sub abort { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_QUE_R_SOCK = $self->{_que_r_sock}; my $_QUE_W_SOCK = $self->{_que_w_sock}; my $_abort_msg = $self->{_abort_msg}; my $_lock_chn = $self->{_lock_chn}; if (defined $_abort_msg) { local $\ = undef; if ($_abort_msg > 0) { my $_next; sysread $_QUE_R_SOCK, $_next, $_que_read_size; syswrite $_QUE_W_SOCK, pack($_que_template, 0, $_abort_msg); } if ($self->{_wid} > 0) { my $_chn = $self->{_chn}; my $_DAT_LOCK = $self->{_dat_lock}; my $_DAT_W_SOCK = $self->{_dat_w_sock}->[0]; my $_DAU_W_SOCK = $self->{_dat_w_sock}->[$_chn]; flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); if (exists $self->{_rla_return}) { print {$_DAT_W_SOCK} OUTPUT_W_RLA . $LF . $_chn . $LF; print {$_DAU_W_SOCK} (delete $self->{_rla_return}) . $LF; } print {$_DAT_W_SOCK} OUTPUT_W_ABT . $LF . $_chn . $LF; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } } return; } ## Worker exits from MCE. sub exit { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_exit_status = (defined $_[0]) ? $_[0] : $?; my $_exit_msg = (defined $_[1]) ? $_[1] : ''; my $_exit_id = (defined $_[2]) ? $_[2] : ''; @_ = (); _croak('MCE::exit: method cannot be called by the manager process') unless ($self->{_wid}); MCE::Signal::stop_and_exit('__DIE__') unless ($self->{_running}); _clear_session( $self->{_mce_sid} ); my $_chn = $self->{_chn}; my $_COM_LOCK = $self->{_com_lock}; my $_DAT_LOCK = $self->{_dat_lock}; my $_DAT_W_SOCK = $self->{_dat_w_sock}->[0]; my $_DAU_W_SOCK = $self->{_dat_w_sock}->[$_chn]; my $_lock_chn = $self->{_lock_chn}; my $_task_id = $self->{_task_id}; my $_sess_dir = $self->{_sess_dir}; unless ($self->{_exiting}) { $self->{_exiting} = 1; local $\ = undef if (defined $\); my $_len = length $_exit_msg; $_exit_id =~ s/[\r\n][\r\n]*/ /mg; open my $_DAE_LOCK, '+>>:raw:stdio', "$_sess_dir/_dat.lock.e" or die "(W) open error $_sess_dir/_dat.lock.e: $!\n"; flock $_DAE_LOCK, LOCK_EX; flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); if (exists $self->{_rla_return}) { print {$_DAT_W_SOCK} OUTPUT_W_RLA . $LF . $_chn . $LF; print {$_DAU_W_SOCK} (delete $self->{_rla_return}) . $LF; } print {$_DAT_W_SOCK} OUTPUT_W_EXT . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_task_id . $LF . $self->{_wid} . $LF . $self->{_exit_pid} . $LF . $_exit_status . $LF . $_exit_id . $LF . $_len . $LF . $_exit_msg ; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); flock $_DAE_LOCK, LOCK_UN; close $_DAE_LOCK; undef $_DAE_LOCK; } ## Exit thread/child process. $SIG{__DIE__} = $SIG{__WARN__} = sub { }; if ($_lock_chn) { close $_DAT_LOCK; undef $_DAT_LOCK; } close $_COM_LOCK; undef $_COM_LOCK; if ($_has_threads && threads->can('exit')) { threads->exit($_exit_status); } CORE::exit($_exit_status); } ## Worker immediately exits the chunking loop. sub last { my $x = shift; my $self = ref($x) ? $x : $MCE; _croak('MCE::last: method cannot be called by the manager process') unless ($self->{_wid}); $self->{_last_jmp}() if (defined $self->{_last_jmp}); return; } ## Worker starts the next iteration of the chunking loop. sub next { my $x = shift; my $self = ref($x) ? $x : $MCE; _croak('MCE::next: method cannot be called by the manager process') unless ($self->{_wid}); $self->{_next_jmp}() if (defined $self->{_next_jmp}); return; } ## Return the process ID. Attach the thread ID for threads. sub pid { my $x = shift; my $self = ref($x) ? $x : $MCE; if (defined $self->{_pid}) { $self->{_pid}; } elsif ($_has_threads && $self->{use_threads}) { $$ .'.'. threads->tid(); } else { $$; } } ## Return the exit status. "_wrk_status" holds the greatest exit status ## among workers exiting. sub status { my $x = shift; my $self = ref($x) ? $x : $MCE; _croak('MCE::status: method cannot be called by the worker process') if ($self->{_wid}); return (defined $self->{_wrk_status}) ? $self->{_wrk_status} : 0; } ############################################################################### ## ---------------------------------------------------------------------------- ## Methods for serializing data from workers to the main process. ## ############################################################################### ## Do method. Additional arguments are optional. sub do { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_callback = shift; _croak('MCE::do: method cannot be called by the manager process') unless ($self->{_wid}); _croak('MCE::do: (callback) is not specified') unless (defined $_callback); $_callback = "main::$_callback" if (index($_callback, ':') < 0); return _do_callback($self, $_callback, \@_); } ## Gather method. sub gather { my $x = shift; my $self = ref($x) ? $x : $MCE; _croak('MCE::gather: method cannot be called by the manager process') unless ($self->{_wid}); return _do_gather($self, \@_); } ## Sendto method. { my %_sendto_lkup = ( 'file' => SENDTO_FILEV1, 'FILE' => SENDTO_FILEV1, 'file:' => SENDTO_FILEV2, 'FILE:' => SENDTO_FILEV2, 'stdout' => SENDTO_STDOUT, 'STDOUT' => SENDTO_STDOUT, 'stderr' => SENDTO_STDERR, 'STDERR' => SENDTO_STDERR, 'fd:' => SENDTO_FD, 'FD:' => SENDTO_FD, ); my $_v2_regx = qr/^([^:]+:)(.+)/; sub sendto { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_to = shift; _croak('MCE::sendto: method cannot be called by the manager process') unless ($self->{_wid}); return unless (defined $_[0]); my ($_dest, $_value); $_dest = (exists $_sendto_lkup{$_to}) ? $_sendto_lkup{$_to} : undef; if (!defined $_dest) { if (ref $_to && defined (my $_fd = fileno($_to))) { my $_data_ref = (scalar @_ == 1) ? \$_[0] : \join('', @_); return _do_send_glob($self, $_to, $_fd, $_data_ref); } if (defined $_to && $_to =~ /$_v2_regx/o) { $_dest = (exists $_sendto_lkup{$1}) ? $_sendto_lkup{$1} : undef; $_value = $2; } if (!defined $_dest || ( !defined $_value && ( $_dest == SENDTO_FILEV2 || $_dest == SENDTO_FD ))) { my $_msg = "\n"; $_msg .= "MCE::sendto: improper use of method\n"; $_msg .= "\n"; $_msg .= "## usage:\n"; $_msg .= "## ->sendto(\"stderr\", ...);\n"; $_msg .= "## ->sendto(\"stdout\", ...);\n"; $_msg .= "## ->sendto(\"file:/path/to/file\", ...);\n"; $_msg .= "## ->sendto(\"fd:2\", ...);\n"; $_msg .= "\n"; _croak($_msg); } } if ($_dest == SENDTO_FILEV1) { ## sendto 'file', $a, $path return if (!defined $_[1] || @_ > 2); ## Please switch to using V2 $_value = $_[1]; delete $_[1]; ## sendto 'file:/path', $a $_dest = SENDTO_FILEV2; } return _do_send($self, $_dest, $_value, @_); } } ############################################################################### ## ---------------------------------------------------------------------------- ## Functions for serializing print, printf and say statements. ## ############################################################################### sub print { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_fd = 0; my ($_glob, $_data_ref); if (ref $_[0] && defined ($_fd = fileno($_[0]))) { $_glob = shift; } if (scalar @_ == 1 ) { $_data_ref = \$_[0]; } elsif (scalar @_ > 1) { $_data_ref = \join('', @_); } else { $_data_ref = \$_; } return _do_send_glob($self, $_glob, $_fd, $_data_ref) if $_fd; return _do_send($self, SENDTO_STDOUT, undef, $_data_ref) if $self->{_wid}; return _do_send_glob($self, \*STDOUT, 1, $_data_ref); } sub printf { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_fd = 0; my ($_glob, $_fmt, $_data); if (ref $_[0] && defined ($_fd = fileno($_[0]))) { $_glob = shift; } $_fmt = shift || '%s'; $_data = (scalar @_) ? sprintf($_fmt, @_) : sprintf($_fmt, $_); return _do_send_glob($self, $_glob, $_fd, \$_data) if $_fd; return _do_send($self, SENDTO_STDOUT, undef, \$_data) if $self->{_wid}; return _do_send_glob($self, \*STDOUT, 1, \$_data); } sub say { my $x = shift; my $self = ref($x) ? $x : $MCE; my $_fd = 0; my ($_glob, $_data); if (ref $_[0] && defined ($_fd = fileno($_[0]))) { $_glob = shift; } $_data = (scalar @_) ? join("\n", @_) . "\n" : $_ . "\n"; return _do_send_glob($self, $_glob, $_fd, \$_data) if $_fd; return _do_send($self, SENDTO_STDOUT, undef, \$_data) if $self->{_wid}; return _do_send_glob($self, \*STDOUT, 1, \$_data); } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _die { return MCE::Signal->_die_handler(@_); } sub _warn { return MCE::Signal->_warn_handler(@_); } sub _NOOP { } sub _croak { if (MCE->wid == 0 || ! $^S) { $SIG{__DIE__} = \&MCE::_die; $SIG{__WARN__} = \&MCE::_warn; } $\ = undef; goto &Carp::croak; } sub _get_max_workers { my $x = shift; my $self = ref($x) ? $x : $MCE; if (defined $self->{user_tasks}) { if (defined $self->{user_tasks}->[0]->{max_workers}) { return $self->{user_tasks}->[0]->{max_workers}; } } return $self->{max_workers}; } sub _sync_buffer_to_array { my ($_buffer_ref, $_array_ref, $_chop_str) = @_; local $_; my $_cnt = 0; open my $_MEM_FILE, '<', $_buffer_ref; binmode $_MEM_FILE; unless (length $_chop_str) { $_array_ref->[$_cnt++] = $_ while (<$_MEM_FILE>); } else { $_array_ref->[$_cnt++] = <$_MEM_FILE>; while (<$_MEM_FILE>) { $_array_ref->[$_cnt ] = $_chop_str; $_array_ref->[$_cnt++] .= $_; } } close $_MEM_FILE; undef $_MEM_FILE; return; } sub _sync_params { my ($self, $_params_ref) = @_; my $_requires_shutdown = 0; if (defined $_params_ref->{init_relay} && !defined $self->{init_relay}) { $_requires_shutdown = 1; } for my $_p (qw( user_begin user_func user_end )) { if (defined $_params_ref->{$_p}) { $self->{$_p} = delete $_params_ref->{$_p}; $_requires_shutdown = 1; } } for my $_p (keys %{ $_params_ref }) { _croak("MCE::_sync_params: ($_p) is not a valid params argument") unless (exists $_params_allowed_args{$_p}); $self->{$_p} = $_params_ref->{$_p}; } return ($self->{_spawned}) ? $_requires_shutdown : 0; } ############################################################################### ## ---------------------------------------------------------------------------- ## Dispatch methods. ## ############################################################################### sub _dispatch { my @_args = @_; my $_is_thread = shift @_args; $MCE = $_args[0]; ## To avoid (Scalars leaked: N) messages; fixed in Perl 5.12.x @_ = (); ## Init worker. $MCE->{_pid} = ($_is_thread) ? $$ .'.'. threads->tid() : $$; ## Begin worker. _worker_main(@_args, \@_plugin_worker_init, $_is_winenv); sleep 0.005 if ($_is_winenv); ## Exit thread/child process. $SIG{__DIE__} = $SIG{__WARN__} = sub { }; if ($_has_threads && threads->can('exit')) { threads->exit(0); } CORE::exit(0); } sub _dispatch_thread { my ($self, $_wid, $_task, $_task_id, $_task_wid, $_params) = @_; @_ = (); local $_; my $_thr = threads->create( \&_dispatch, 1, $self, $_wid, $_task, $_task_id, $_task_wid, $_params ); _croak("MCE::_dispatch_thread: Failed to spawn worker $_wid: $!") unless (defined $_thr); if (defined $_thr) { ## Store into an available slot, otherwise append to arrays. if (defined $_params) { for my $_i (0 .. @{ $self->{_tids} } - 1) { unless (defined $self->{_tids}->[$_i]) { $self->{_thrs}->[$_i] = \$_thr; $self->{_tids}->[$_i] = $_thr->tid(); return; } }} push @{ $self->{_thrs} }, \$_thr; push @{ $self->{_tids} }, $_thr->tid(); } if (defined $self->{spawn_delay} && $self->{spawn_delay} > 0.0) { sleep $self->{spawn_delay}; } else { sleep 0.001 if ($_is_winenv || $_wid % 2 == 0); } return; } sub _dispatch_child { my ($self, $_wid, $_task, $_task_id, $_task_wid, $_params) = @_; @_ = (); local $_; my $_pid = fork(); _croak("MCE::_dispatch_child: Failed to spawn worker $_wid: $!") unless (defined $_pid); unless ($_pid) { _dispatch(0, $self, $_wid, $_task, $_task_id, $_task_wid, $_params); } if (defined $_pid) { ## Store into an available slot, otherwise append to array. if (defined $_params) { for my $_i (0 .. @{ $self->{_pids} } - 1) { unless (defined $self->{_pids}->[$_i]) { $self->{_pids}->[$_i] = $_pid; return; } }} push @{ $self->{_pids} }, $_pid; } if (defined $self->{spawn_delay} && $self->{spawn_delay} > 0.0) { sleep $self->{spawn_delay}; } else { sleep 0.001 if ($_is_winenv); } return; } 1; MCE-1.608/lib/MCE/0000755000076400007640000000000012511673554012327 5ustar mariomarioMCE-1.608/lib/MCE/Util.pm0000644000076400007640000002545512511673062013607 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Util - Utility functions for Many-Core Engine. ## ############################################################################### package MCE::Util; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) use Socket qw( PF_UNIX PF_UNSPEC SOCK_STREAM ); use base qw( Exporter ); use bytes; our $VERSION = '1.608'; my $_is_winenv = ($^O eq 'cygwin' || $^O eq 'MSWin32') ? 1 : 0; our $LF = "\012"; Internals::SvREADONLY($LF, 1); our @EXPORT_OK = qw( $LF get_ncpu ); our %EXPORT_TAGS = ( all => \@EXPORT_OK ); ############################################################################### ## ---------------------------------------------------------------------------- ## The get_ncpu subroutine, largely adopted from Test::Smoke::Util.pm, ## returns the number of logical (online/active/enabled) CPU cores; ## never smaller than one. ## ## A warning is emitted to STDERR when it cannot recognize the operating ## system or the external command failed. ## ############################################################################### my $g_ncpu; sub get_ncpu { return $g_ncpu if (defined $g_ncpu); local $ENV{PATH} = "/usr/sbin:/sbin:/usr/bin:/bin:$ENV{PATH}"; $ENV{PATH} =~ /(.*)/; $ENV{PATH} = $1; ## Remove tainted'ness my $ncpu = 1; OS_CHECK: { local $_ = lc $^O; /linux/ && do { my ($count, $fh); if ( open $fh, '<', '/proc/stat' ) { $count = grep { /^cpu\d/ } <$fh>; close $fh; } $ncpu = $count if $count; last OS_CHECK; }; /bsd|darwin|dragonfly/ && do { chomp( my @output = `sysctl -n hw.ncpu 2>/dev/null` ); $ncpu = $output[0] if @output; last OS_CHECK; }; /aix/ && do { my @output = `pmcycles -m 2>/dev/null`; if (@output) { $ncpu = scalar @output; } else { @output = `lsdev -Cc processor -S Available 2>/dev/null`; $ncpu = scalar @output if @output; } last OS_CHECK; }; /gnu/ && do { chomp( my @output = `nproc 2>/dev/null` ); $ncpu = $output[0] if @output; last OS_CHECK; }; /hp-?ux/ && do { my $count = grep { /^processor/ } `ioscan -fkC processor 2>/dev/null`; $ncpu = $count if $count; last OS_CHECK; }; /irix/ && do { my @out = grep { /\s+processors?$/i } `hinv -c processor 2>/dev/null`; $ncpu = (split ' ', $out[0])[0] if @out; last OS_CHECK; }; /osf|solaris|sunos|svr5|sco/ && do { if (-x '/usr/sbin/psrinfo') { my $count = grep { /on-?line/ } `psrinfo 2>/dev/null`; $ncpu = $count if $count; } else { my @output = grep { /^NumCPU = \d+/ } `uname -X 2>/dev/null`; $ncpu = (split ' ', $output[0])[2] if @output; } last OS_CHECK; }; /mswin|mingw|cygwin/ && do { if (exists $ENV{NUMBER_OF_PROCESSORS}) { $ncpu = $ENV{NUMBER_OF_PROCESSORS}; } last OS_CHECK; }; warn "MCE::Util::get_ncpu: command failed or unknown operating system\n"; } $ncpu = 1 if (!$ncpu || $ncpu < 1); return $g_ncpu = $ncpu; } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _destroy_sockets { my ($_obj, @_params) = @_; local ($!, $?); for my $_p (@_params) { if (defined $_obj->{$_p}) { if (ref $_obj->{$_p} eq 'ARRAY') { for my $_i (0 .. @{ $_obj->{$_p} } - 1) { ## an empty socket may not close immediately in Windows/Cygwin syswrite $_obj->{$_p}->[$_i], '0' if ($_is_winenv); # hack CORE::shutdown $_obj->{$_p}->[$_i], 2; close $_obj->{$_p}->[$_i]; undef $_obj->{$_p}->[$_i]; } } else { syswrite $_obj->{$_p}, '0' if ($_is_winenv); # ditto CORE::shutdown $_obj->{$_p}, 2; close $_obj->{$_p}; } undef $_obj->{$_p}; } } return; } sub _make_socket_pair { my ($_obj, $_r_sock, $_w_sock, $_i) = @_; local $!; my $_old_hndl; if (defined $_i) { socketpair( $_obj->{$_r_sock}->[$_i], $_obj->{$_w_sock}->[$_i], PF_UNIX, SOCK_STREAM, PF_UNSPEC ) or die "socketpair: $!\n"; binmode $_obj->{$_r_sock}->[$_i]; binmode $_obj->{$_w_sock}->[$_i]; ## Autoflush handles. $_old_hndl = select $_obj->{$_r_sock}->[$_i]; $| = 1; select $_obj->{$_w_sock}->[$_i]; $| = 1; } else { socketpair( $_obj->{$_r_sock}, $_obj->{$_w_sock}, PF_UNIX, SOCK_STREAM, PF_UNSPEC ) or die "socketpair: $!\n"; binmode $_obj->{$_r_sock}; binmode $_obj->{$_w_sock}; ## Autoflush handles. $_old_hndl = select $_obj->{$_r_sock}; $| = 1; select $_obj->{$_w_sock}; $| = 1; } select $_old_hndl; return; } sub _parse_max_workers { my ($_max_workers) = @_; @_ = (); return $_max_workers unless (defined $_max_workers); if ($_max_workers =~ /^auto(?:$|\s*([\-\+\/\*])\s*(.+)$)/i) { my ($_ncpu_ul, $_ncpu); $_ncpu_ul = $_ncpu = get_ncpu(); $_ncpu_ul = 8 if ($_ncpu_ul > 8); if ($1 && $2) { local $@; $_max_workers = eval "int($_ncpu_ul $1 $2 + 0.5)"; $_max_workers = 1 if (!$_max_workers || $_max_workers < 1); $_max_workers = $_ncpu if ($_max_workers > $_ncpu); } else { $_max_workers = $_ncpu_ul; } } return $_max_workers; } sub _parse_chunk_size { my ($_chunk_size, $_max_workers, $_params, $_input_data, $_array_size) = @_; @_ = (); return $_chunk_size if (!defined $_chunk_size || !defined $_max_workers); if (defined $_params && exists $_params->{chunk_size}) { $_chunk_size = $_params->{chunk_size}; } if ($_chunk_size =~ /([0-9\.]+)K\z/i) { $_chunk_size = int($1 * 1024 + 0.5); } elsif ($_chunk_size =~ /([0-9\.]+)M\z/i) { $_chunk_size = int($1 * 1024 * 1024 + 0.5); } if ($_chunk_size eq 'auto') { if ( (defined $_params && ref $_params->{input_data} eq 'CODE') || (defined $_input_data && ref $_input_data eq 'CODE') ) { return 1; } my $_size = (defined $_input_data && ref $_input_data eq 'ARRAY') ? scalar @{ $_input_data } : $_array_size; my $_is_file; if (defined $_params && exists $_params->{sequence}) { my ($_begin, $_end, $_step); if (ref $_params->{sequence} eq 'HASH') { $_begin = $_params->{sequence}->{begin}; $_end = $_params->{sequence}->{end}; $_step = $_params->{sequence}->{step} || 1; } else { $_begin = $_params->{sequence}->[0]; $_end = $_params->{sequence}->[1]; $_step = $_params->{sequence}->[2] || 1; } if (!defined $_input_data && !$_array_size) { $_size = abs($_end - $_begin) / $_step + 1; } } elsif (defined $_params && exists $_params->{_file}) { my $_ref = ref $_params->{_file}; if ($_ref eq 'SCALAR') { $_size = length ${ $_params->{_file} }; } elsif ($_ref eq '') { $_size = -s $_params->{_file}; } else { $_size = 0; $_chunk_size = 245_760; } $_is_file = 1; } elsif (defined $_input_data) { if (ref $_input_data eq 'GLOB' || ref($_input_data) =~ /^IO::/) { $_is_file = 1; $_size = 0; $_chunk_size = 245_760; } elsif (ref $_input_data eq 'SCALAR') { $_is_file = 1; $_size = length ${ $_input_data }; } } if (defined $_is_file) { if ($_size) { $_chunk_size = int($_size / $_max_workers / 24 + 0.5); $_chunk_size = 4_194_304 if $_chunk_size > 4_194_304; ## 4M $_chunk_size = 2 if $_chunk_size <= 8192; } } else { $_chunk_size = int($_size / $_max_workers / 24 + 0.5); $_chunk_size = 8000 if $_chunk_size > 8000; $_chunk_size = 2 if $_chunk_size < 2; } } return $_chunk_size; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Util - Utility functions for Many-Core Engine =head1 VERSION This document describes MCE::Util version 1.608 =head1 SYNOPSIS use MCE::Util; =head1 DESCRIPTION A utility module for MCE. Nothing is exported by default. Exportable is get_ncpu. =head2 get_ncpu() Returns the number of logical (online/active/enabled) CPU cores; never smaller than one. my $ncpu = MCE::Util::get_ncpu(); Specifying 'auto' for max_workers calls MCE::Util::get_ncpu automatically. MCE 1.521 sets an upper-limit when specifying 'auto'. The reason is mainly to safeguard apps from spawning 100 workers on a box having 100 cores. This is important for apps which are IO-bound. use MCE; ## 'Auto' is the total # of logical cores (lcores) (8 maximum, MCE 1.521). ## The computed value will not exceed the # of logical cores on the box. my $mce = MCE->new( max_workers => 'auto', ## 1 on HW with 1-lcores; 2 on 2-lcores max_workers => 16, ## 16 on HW with 4-lcores; 16 on 32-lcores max_workers => 'auto', ## 4 on HW with 4-lcores; 8 on 16-lcores max_workers => 'auto*1.5', ## 4 on HW with 4-lcores; 12 on 16-lcores max_workers => 'auto*2.0', ## 4 on HW with 4-lcores; 16 on 16-lcores max_workers => 'auto/2.0', ## 2 on HW with 4-lcores; 4 on 16-lcores max_workers => 'auto+3', ## 4 on HW with 4-lcores; 11 on 16-lcores max_workers => 'auto-1', ## 3 on HW with 4-lcores; 7 on 16-lcores max_workers => MCE::Util::get_ncpu, ## run on all lcores ); In summary: 1. Auto has an upper-limit of 8 in MCE 1.521 (# of lcores, 8 maximum) 2. Math can be applied with auto (*/+-) to change the upper limit 3. The computed value for auto will not exceed the total # of lcores 4. One can specify max_workers explicity to a hard value 5. MCE::Util::get_ncpu returns the actual # of lcores =head1 ACKNOWLEDGEMENTS The portable code for detecting the number of processors was adopted from L. =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Loop.pm0000644000076400007640000005632112511673027013600 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Loop - Parallel loop model for building creative loops. ## ############################################################################### package MCE::Loop; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use Scalar::Util qw( looks_like_number ); use MCE; our $VERSION = '1.608'; our @CARP_NOT = qw( MCE ); ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $MAX_WORKERS = 'auto'; my $CHUNK_SIZE = 'auto'; my ($_MCE, $_loaded); my ($_params, $_prev_c); my $_tag = 'MCE::Loop'; sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $MAX_WORKERS = shift and next if ( $_arg eq 'max_workers' ); $CHUNK_SIZE = shift and next if ( $_arg eq 'chunk_size' ); $MCE::FREEZE = $MCE::MCE->{freeze} = shift and next if ( $_arg eq 'freeze' ); $MCE::THAW = $MCE::MCE->{thaw} = shift and next if ( $_arg eq 'thaw' ); if ( $_arg eq 'sereal' ) { if (shift eq '1') { local $@; eval 'use Sereal qw(encode_sereal decode_sereal)'; unless ($@) { $MCE::FREEZE = $MCE::MCE->{freeze} = \&encode_sereal; $MCE::THAW = $MCE::MCE->{thaw} = \&decode_sereal; } } next; } if ( $_arg eq 'tmp_dir' ) { $MCE::TMP_DIR = $MCE::MCE->{tmp_dir} = shift; my $_e1 = 'is not a directory or does not exist'; my $_e2 = 'is not writeable'; _croak($_tag."::import: ($MCE::TMP_DIR) $_e1") unless -d $MCE::TMP_DIR; _croak($_tag."::import: ($MCE::TMP_DIR) $_e2") unless -w $MCE::TMP_DIR; next; } _croak($_tag."::import: ($_argument) is not a valid module argument"); } $MAX_WORKERS = MCE::Util::_parse_max_workers($MAX_WORKERS); _validate_number($MAX_WORKERS, 'MAX_WORKERS'); _validate_number($CHUNK_SIZE, 'CHUNK_SIZE') unless ($CHUNK_SIZE eq 'auto'); ## Import functions. no strict 'refs'; no warnings 'redefine'; my $_pkg = caller; *{ $_pkg.'::mce_loop_f' } = \&run_file; *{ $_pkg.'::mce_loop_s' } = \&run_seq; *{ $_pkg.'::mce_loop' } = \&run; return; } END { return if (defined $_MCE && $_MCE->wid); finish(); } ############################################################################### ## ---------------------------------------------------------------------------- ## Init and finish routines. ## ############################################################################### sub init (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Loop'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } finish(); $_params = (ref $_[0] eq 'HASH') ? shift : { @_ }; @_ = (); return; } sub finish () { if (defined $_MCE && $_MCE->{_spawned}) { MCE::_save_state; $_MCE->shutdown(); MCE::_restore_state; } $_prev_c = undef; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel loop with MCE -- file. ## ############################################################################### sub run_file (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Loop'); my $_code = shift; my $_file = shift; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{sequence} if (exists $_params->{sequence}); } else { $_params = {}; } if (defined $_file && ref $_file eq '' && $_file ne '') { _croak("$_tag: ($_file) does not exist") unless (-e $_file); _croak("$_tag: ($_file) is not readable") unless (-r $_file); _croak("$_tag: ($_file) is not a plain file") unless (-f $_file); $_params->{_file} = $_file; } elsif (ref $_file eq 'GLOB' || ref $_file eq 'SCALAR' || ref($_file) =~ /^IO::/) { $_params->{_file} = $_file; } else { _croak("$_tag: (file) is not specified or valid"); } @_ = (); return run($_code); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel loop with MCE -- sequence. ## ############################################################################### sub run_seq (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Loop'); my $_code = shift; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{_file} if (exists $_params->{_file}); } else { $_params = {}; } my ($_begin, $_end); if (ref $_[0] eq 'HASH') { $_begin = $_[0]->{begin}; $_end = $_[0]->{end}; $_params->{sequence} = $_[0]; } elsif (ref $_[0] eq 'ARRAY') { $_begin = $_[0]->[0]; $_end = $_[0]->[1]; $_params->{sequence} = $_[0]; } elsif (ref $_[0] eq '') { $_begin = $_[0]; $_end = $_[1]; $_params->{sequence} = [ @_ ]; } else { _croak("$_tag: (sequence) is not specified or valid"); } _croak("$_tag: (begin) is not specified for sequence") unless (defined $_begin); _croak("$_tag: (end) is not specified for sequence") unless (defined $_end); $_params->{sequence_run} = 1; @_ = (); return run($_code); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel loop with MCE. ## ############################################################################### sub run (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Loop'); my $_code = shift; if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } my $_input_data; my $_max_workers = $MAX_WORKERS; my $_r = ref $_[0]; if ($_r eq 'ARRAY' || $_r eq 'CODE' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_input_data = shift; } if (defined $_params) { my $_p = $_params; $_max_workers = MCE::Util::_parse_max_workers($_p->{max_workers}) if (exists $_p->{max_workers}); delete $_p->{sequence} if (defined $_input_data || scalar @_); delete $_p->{user_func} if (exists $_p->{user_func}); delete $_p->{user_tasks} if (exists $_p->{user_tasks}); } my $_chunk_size = MCE::Util::_parse_chunk_size( $CHUNK_SIZE, $_max_workers, $_params, $_input_data, scalar @_ ); if (defined $_params) { if (exists $_params->{_file}) { $_input_data = delete $_params->{_file}; } else { $_input_data = $_params->{input_data} if exists $_params->{input_data}; } } MCE::_save_state; ## ------------------------------------------------------------------------- if (!defined $_prev_c || $_prev_c != $_code) { $_MCE->shutdown() if (defined $_MCE); $_prev_c = $_code; my %_options = ( max_workers => $_max_workers, task_name => $_tag, user_func => $_code, ); if (defined $_params) { for my $_p (keys %{ $_params }) { next if ($_p eq 'sequence_run'); next if ($_p eq 'input_data'); next if ($_p eq 'chunk_size'); _croak("MCE::Loop: ($_p) is not a valid constructor argument") unless (exists $MCE::_valid_fields_new{$_p}); $_options{$_p} = $_params->{$_p}; } } $_MCE = MCE->new(%_options); } ## ------------------------------------------------------------------------- my @_a; my $_wa = wantarray; $_MCE->{gather} = \@_a if (defined $_wa); if (defined $_input_data) { @_ = (); $_MCE->process({ chunk_size => $_chunk_size }, $_input_data); delete $_MCE->{input_data}; } elsif (scalar @_) { $_MCE->process({ chunk_size => $_chunk_size }, \@_); delete $_MCE->{input_data}; } else { if (defined $_params && exists $_params->{sequence}) { $_MCE->run({ chunk_size => $_chunk_size, sequence => $_params->{sequence} }, 0); if (exists $_params->{sequence_run}) { delete $_params->{sequence_run}; delete $_params->{sequence}; } delete $_MCE->{sequence}; } } delete $_MCE->{gather} if (defined $_wa); MCE::_restore_state; if (exists $_MCE->{_rla_return}) { $MCE::MCE->{_rla_return} = delete $_MCE->{_rla_return}; } finish() if ($^S); ## shutdown if in eval state return ((defined $_wa) ? @_a : ()); } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _croak { goto &MCE::_croak; } sub _validate_number { my ($_n, $_key) = @_; _croak("$_tag: ($_key) is not valid") if (!defined $_n); $_n =~ s/K\z//i; $_n =~ s/M\z//i; if (!looks_like_number($_n) || int($_n) != $_n || $_n < 1) { _croak("$_tag: ($_key) is not valid"); } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Loop - Parallel loop model for building creative loops =head1 VERSION This document describes MCE::Loop version 1.608 =head1 DESCRIPTION This module provides a parallel loop implementation through Many-Core Engine. MCE::Loop is not MCE::Map but more along the lines of an easy way to spin up a MCE instance and have user_func pointing to your code block. If you want something similar to map, then see L. ## Construction when chunking is not desired use MCE::Loop; MCE::Loop::init { max_workers => 5, chunk_size => 1 }; mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->say("$chunk_id: $_"); } 40 .. 48; -- Output 3: 42 1: 40 2: 41 4: 43 5: 44 6: 45 7: 46 8: 47 9: 48 ## Construction for 'auto' or greater than 1 use MCE::Loop; MCE::Loop::init { max_workers => 5, chunk_size => 'auto' }; mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; for (@{ $chunk_ref }) { MCE->say("$chunk_id: $_"); } } 40 .. 48; -- Output 1: 40 2: 42 1: 41 4: 46 2: 43 5: 48 3: 44 4: 47 3: 45 =head1 SYNOPSIS when CHUNK_SIZE EQUALS 1 All models in MCE default to 'auto' for chunk_size. The arguments for the block are the same as writing a user_func block using the Core API. Beginning with MCE 1.5, the next input item is placed into the input scalar variable $_ when chunk_size equals 1. Otherwise, $_ points to $chunk_ref containing many items. Basically, line 2 below may be omitted from your code when using $_. One can call MCE->chunk_id to obtain the current chunk id. line 1: user_func => sub { line 2: my ($mce, $chunk_ref, $chunk_id) = @_; line 3: line 4: $_ points to $chunk_ref->[0] line 5: in MCE 1.5 when chunk_size == 1 line 6: line 7: $_ points to $chunk_ref line 8: in MCE 1.5 when chunk_size > 1 line 9: } Follow this synopsis when chunk_size equals one. Looping is not required from inside the block. Hence, the block is called once per each item. ## Exports mce_loop, mce_loop_f, and mce_loop_s use MCE::Loop; MCE::Loop::init { chunk_size => 1 }; ## Array or array_ref mce_loop { do_work($_) } 1..10000; mce_loop { do_work($_) } [ 1..10000 ]; ## File_path, glob_ref, or scalar_ref mce_loop_f { chomp; do_work($_) } "/path/to/file"; mce_loop_f { chomp; do_work($_) } $file_handle; mce_loop_f { chomp; do_work($_) } \$scalar; ## Sequence of numbers (begin, end [, step, format]) mce_loop_s { do_work($_) } 1, 10000, 5; mce_loop_s { do_work($_) } [ 1, 10000, 5 ]; mce_loop_s { do_work($_) } { begin => 1, end => 10000, step => 5, format => undef }; =head1 SYNOPSIS when CHUNK_SIZE is GREATER THAN 1 Follow this synopsis when chunk_size equals 'auto' or greater than 1. This means having to loop through the chunk from inside the block. use MCE::Loop; MCE::Loop::init { ## Chunk_size defaults to 'auto' when chunk_size => 'auto' ## not specified. Therefore, the init }; ## function may be omitted. ## Syntax is shown for mce_loop for demonstration purposes. ## Looping inside the block is the same for mce_loop_f and ## mce_loop_s. mce_loop { do_work($_) for (@{ $_ }) } 1..10000; ## Same as above, resembles code using the Core API. mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; for (@{ $chunk_ref }) { do_work($_); } } 1..10000; Chunking reduces the number of IPC calls behind the scene. Think in terms of chunks whenever processing a large amount of data. For relatively small data, choosing 1 for chunk_size is fine. =head1 OVERRIDING DEFAULTS The following list 5 options which may be overridden when loading the module. use Sereal qw( encode_sereal decode_sereal ); use CBOR::XS qw( encode_cbor decode_cbor ); use JSON::XS qw( encode_json decode_json ); use MCE::Loop max_workers => 4, ## Default 'auto' chunk_size => 100, ## Default 'auto' tmp_dir => "/path/to/app/tmp", ## $MCE::Signal::tmp_dir freeze => \&encode_sereal, ## \&Storable::freeze thaw => \&decode_sereal ## \&Storable::thaw ; There is a simpler way to enable Sereal with MCE 1.5. The following will attempt to use Sereal if available, otherwise defaults to Storable for serialization. use MCE::Loop Sereal => 1; MCE::Loop::init { chunk_size => 1 }; ## Serialization is by the Sereal module if available. my %answer = mce_loop { MCE->gather( $_, sqrt $_ ) } 1..10000; =head1 CUSTOMIZING MCE =over 3 =item MCE::Loop->init ( options ) =item MCE::Loop::init { options } The init function accepts a hash of MCE options. use MCE::Loop; MCE::Loop::init { chunk_size => 1, max_workers => 4, user_begin => sub { print "## ", MCE->wid, " started\n"; }, user_end => sub { print "## ", MCE->wid, " completed\n"; } }; my %a = mce_loop { MCE->gather($_, $_ * $_) } 1..100; print "\n", "@a{1..100}", "\n"; -- Output ## 3 started ## 1 started ## 2 started ## 4 started ## 1 completed ## 2 completed ## 3 completed ## 4 completed 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 1600 1681 1764 1849 1936 2025 2116 2209 2304 2401 2500 2601 2704 2809 2916 3025 3136 3249 3364 3481 3600 3721 3844 3969 4096 4225 4356 4489 4624 4761 4900 5041 5184 5329 5476 5625 5776 5929 6084 6241 6400 6561 6724 6889 7056 7225 7396 7569 7744 7921 8100 8281 8464 8649 8836 9025 9216 9409 9604 9801 10000 =back =head1 API DOCUMENTATION The following assumes chunk_size equals 1 in order to demonstrate all the possibilities of passing input data into the code block. =over 3 =item MCE::Loop->run ( sub { code }, iterator ) =item mce_loop { code } iterator An iterator reference can by specified for input_data. Iterators are described under "SYNTAX for INPUT_DATA" at L. mce_loop { $_ } make_iterator(10, 30, 2); =item MCE::Loop->run ( sub { code }, list ) =item mce_loop { code } list Input data can be defined using a list. mce_loop { $_ } 1..1000; mce_loop { $_ } [ 1..1000 ]; =item MCE::Loop->run_file ( sub { code }, file ) =item mce_loop_f { code } file The fastest of these is the /path/to/file. Workers communicate the next offset position among themselves without any interaction from the manager process. mce_loop_f { $_ } "/path/to/file"; mce_loop_f { $_ } $file_handle; mce_loop_f { $_ } \$scalar; =item MCE::Loop->run_seq ( sub { code }, $beg, $end [, $step, $fmt ] ) =item mce_loop_s { code } $beg, $end [, $step, $fmt ] Sequence can be defined as a list, an array reference, or a hash reference. The functions require both begin and end values to run. Step and format are optional. The format is passed to sprintf (% may be omitted below). my ($beg, $end, $step, $fmt) = (10, 20, 0.1, "%4.1f"); mce_loop_s { $_ } $beg, $end, $step, $fmt; mce_loop_s { $_ } [ $beg, $end, $step, $fmt ]; mce_loop_s { $_ } { begin => $beg, end => $end, step => $step, format => $fmt }; =back The sequence engine can compute 'begin' and 'end' items only, for the chunk, and not the items in between (hence boundaries only). This option applies to sequence only and has no effect when chunk_size equals 1. The time to run is 0.006s below. This becomes 0.827s without the bounds_only option due to computing all items in between, thus creating a very large array. Basically, specify bounds_only => 1 when boundaries is all you need for looping inside the block; e.g. Monte Carlo simulations. Time was measured using 1 worker to emphasize the difference. use MCE::Loop; MCE::Loop::init { max_workers => 1, chunk_size => 1_250_000, bounds_only => 1 }; ## For sequence, the input scalar $_ points to $chunk_ref ## when chunk_size > 1, otherwise $chunk_ref->[0]. ## ## mce_loop_s { ## my $begin = $_->[0]; my $end = $_->[-1]; ## ## for ($begin .. $end) { ## ... ## } ## ## } 1, 10_000_000; mce_loop_s { my ($mce, $chunk_ref, $chunk_id) = @_; ## $chunk_ref contains 2 items, not 1_250_000 my $begin = $chunk_ref->[ 0]; my $end = $chunk_ref->[-1]; ## or $chunk_ref->[1] MCE->printf("%7d .. %8d\n", $begin, $end); } 1, 10_000_000; -- Output 1 .. 1250000 1250001 .. 2500000 2500001 .. 3750000 3750001 .. 5000000 5000001 .. 6250000 6250001 .. 7500000 7500001 .. 8750000 8750001 .. 10000000 =head1 GATHERING DATA Unlike MCE::Map where gather and output order are done for you automatically, the gather method is used to have results sent back to the manager process. use MCE::Loop chunk_size => 1; ## Output order is not guaranteed. my @a = mce_loop { MCE->gather($_ * 2) } 1..100; print "@a\n\n"; ## Outputs to a hash instead (key, value). my %h1 = mce_loop { MCE->gather($_, $_ * 2) } 1..100; print "@h1{1..100}\n\n"; ## This does the same thing due to chunk_id starting at one. my %h2 = mce_loop { MCE->gather(MCE->chunk_id, $_ * 2) } 1..100; print "@h2{1..100}\n\n"; The gather method can be called multiple times within the block unlike return which would leave the block. Therefore, think of gather as yielding results immediately to the manager process without actually leaving the block. use MCE::Loop chunk_size => 1, max_workers => 3; my @hosts = qw( hosta hostb hostc hostd hoste ); my %h3 = mce_loop { my ($output, $error, $status); my $host = $_; ## Do something with $host; $output = "Worker ". MCE->wid .": Hello from $host"; if (MCE->chunk_id % 3 == 0) { ## Simulating an error condition local $? = 1; $status = $?; $error = "Error from $host" } else { $status = 0; } ## Ensure unique keys (key, value) when gathering to ## a hash. MCE->gather("$host.out", $output); MCE->gather("$host.err", $error) if (defined $error); MCE->gather("$host.sta", $status); } @hosts; foreach my $host (@hosts) { print $h3{"$host.out"}, "\n"; print $h3{"$host.err"}, "\n" if (exists $h3{"$host.err"}); print "Exit status: ", $h3{"$host.sta"}, "\n\n"; } -- Output Worker 2: Hello from hosta Exit status: 0 Worker 1: Hello from hostb Exit status: 0 Worker 3: Hello from hostc Error from hostc Exit status: 1 Worker 2: Hello from hostd Exit status: 0 Worker 1: Hello from hoste Exit status: 0 The following uses an anonymous array containing 3 elements when gathering data. Serialization is automatic behind the scene. my %h3 = mce_loop { ... MCE->gather($host, [$output, $error, $status]); } @hosts; foreach my $host (@hosts) { print $h3{$host}->[0], "\n"; print $h3{$host}->[1], "\n" if (defined $h3{$host}->[1]); print "Exit status: ", $h3{$host}->[2], "\n\n"; } Although MCE::Map comes to mind, one may want additional control when gathering data such as retaining output order. use MCE::Loop; sub preserve_order { my %tmp; my $order_id = 1; my $gather_ref = $_[0]; return sub { $tmp{ (shift) } = \@_; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ delete $tmp{$order_id++} }; } return; }; } my @m2; MCE::Loop::init { chunk_size => 'auto', max_workers => 'auto', gather => preserve_order(\@m2) }; mce_loop { my @a; my ($mce, $chunk_ref, $chunk_id) = @_; ## Compute the entire chunk data at once. push @a, map { $_ * 2 } @{ $chunk_ref }; ## Afterwards, invoke the gather feature, which ## will direct the data to the callback function. MCE->gather(MCE->chunk_id, @a); } 1..100000; MCE::Loop::finish; print scalar @m2, "\n"; All 6 models support 'auto' for chunk_size unlike the Core API. Think of the models as the basis for providing JIT for MCE. They create the instance, tune max_workers, and tune chunk_size automatically regardless of the hardware. The following does the same thing using the Core API. use MCE; sub preserve_order { ... } my $mce = MCE->new( max_workers => 'auto', chunk_size => 8000, user_func => sub { my @a; my ($mce, $chunk_ref, $chunk_id) = @_; ## Compute the entire chunk data at once. push @a, map { $_ * 2 } @{ $chunk_ref }; ## Afterwards, invoke the gather feature, which ## will direct the data to the callback function. MCE->gather(MCE->chunk_id, @a); } ); my @m2; $mce->process({ gather => preserve_order(\@m2) }, [1..100000]); $mce->shutdown; print scalar @m2, "\n"; =head1 MANUAL SHUTDOWN =over 3 =item MCE::Loop->finish =item MCE::Loop::finish Workers remain persistent as much as possible after running. Shutdown occurs automatically when the script terminates. Call finish when workers are no longer needed. use MCE::Loop; MCE::Loop::init { chunk_size => 20, max_workers => 'auto' }; mce_loop { ... } 1..100; MCE::Loop::finish; =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Core.pod0000644000076400007640000016761412511673004013730 0ustar mariomario =head1 NAME MCE::Core - Documentation describing the Core API for Many-Core Engine =head1 VERSION This document describes MCE::Core version 1.608 =head1 SYNOPSIS This is a simplistic use case of MCE running with 5 workers. ## Construction using the Core API use MCE; my $mce = MCE->new( max_workers => 5, user_func => sub { my ($mce) = @_; $mce->say("Hello from " . $mce->wid); } ); $mce->run; ## Construction using a MCE model use MCE::Flow max_workers => 5; mce_flow sub { my ($mce) = @_; MCE->say("Hello from " . MCE->wid); }; -- Output Hello from 2 Hello from 4 Hello from 5 Hello from 1 Hello from 3 =head2 MCE->new ( [ options ] ) Below, a new instance is configured with all available options. use MCE; my $mce = MCE->new( max_workers => 8, ## Default $MCE::MAX_WORKERS # Number of workers to spawn. This can be set automatically # with MCE 1.412 and later releases. # MCE 1.521 sets an upper-limit of 8 for 'auto'. # See MCE::Util::get_ncpu for more info. # max_workers => 'auto', ## # of lcores, 8 maximum # max_workers => 'auto-1', ## 7 on HW with 16-lcores # max_workers => 'auto-1', ## 3 on HW with 4-lcores # max_workers => MCE::Util::get_ncpu, # run on all lcores chunk_size => 2000, ## Default $MCE::CHUNK_SIZE # Can also take a suffix; K (Kilobytes) or M (Megabytes). # Smaller than or equal to 8192 is number of records. # Greater than 8192 is number of bytes. MCE reads until # the end of record before calling user_func. A value # above 64M will be set to 64M (the maximum). # chunk_size => 1, ## Consists of 1 record # chunk_size => 1000, ## Consists of 1000 records # chunk_size => '16K', ## Approximate 16 kilobytes # chunk_size => '20M', ## Approximate 20 megabytes tmp_dir => $tmp_dir, ## Default $MCE::TMP_DIR # Default is $MCE::Signal::tmp_dir which points to # $ENV{TEMP} if defined. Otherwise, tmp_dir points # to a location under /tmp. freeze => \&encode_sereal, ## Default $MCE::FREEZE thaw => \&decode_sereal, ## Default $MCE::THAW # Release 1.412 allows freeze and thaw to be overridden. # Simply include a serialization module prior to loading # MCE. Configure freeze/thaw options. # use Sereal qw( encode_sereal decode_sereal ); # use CBOR::XS qw( encode_cbor decode_cbor ); # use JSON::XS qw( encode_json decode_json ); # # use MCE; gather => \@a, ## Default undef # Release 1.5 allows for gathering of data to an array or # hash reference, a MCE::Queue/Thread::Queue object, or code # reference. One invokes gathering by calling the gather # method as often as needed. # gather => \@array, # gather => \%hash, # gather => $queue, # gather => \&order, init_relay => 0, ## Default undef # For specifying the initial relay value. Allowed values # are array_ref, hash_ref, or scalar. The MCE::Relay module # is loaded automatically when specified. # init_relay => \@array, # init_relay => \%hash, # init_relay => scalar, input_data => $input_file, ## Default undef RS => "\n>", ## Default undef # input_data => '/path/to/file' ## Process file # input_data => \@array ## Process array # input_data => \*FILE_HNDL ## Process file handle # input_data => \$scalar ## Treated like a file # input_data => \&iterator ## User specified iterator # The RS option (for input record separator) applies to files # and file handles. # MCE applies additional logic when RS begins with a newline # character; e.g. RS => "\n>". It trims away characters after # the newline and prepends them to the next record. # This happens automatically when not slurping. Otherwise, # the logic is applied to the first and last records only. # # Typically, the left side is what happens for $/ = "\n>". # The right side is what user_func receives. # # use_slurpio => 0 (all records begin with > and end with \n) # Record 1: >seq1 ... \n> (to) >seq1 ... \n # Record 2: seq2 ... \n> >seq2 ... \n # Record 3: seq3 ... \n> >seq3 ... \n # Last Rec: seqN ... \n >seqN ... \n # # use_slurpio => 1 (all chunks begin with > and end with \n) # 1 Record 1: >seq1 ... \n> (to) >seq1 ... \n> # Record 2: seq2 ... \n> seq2 ... \n> # Record 3: seq3 ... \n> seq3 ... \n # # 2 Record 4: seq4 ... \n> (to) >seq4 ... \n> # Record 5: seq5 ... \n> seq5 ... \n> # Record 6: seq6 ... \n> seq6 ... \n # # 3 Record 7: seq7 ... \n> (to) >seq7 ... \n> # Record 8: seq8 ... \n> seq8 ... \n> # Last Rec: seqN ... \n seqN ... \n use_slurpio => 1, ## Default 0 parallel_io => 1, ## Default 0 # Whether to enable slurpio when reading files. # Passes raw chunk (scalar ref) to user function. # The parallel_io option enables parallel reads during # large slurpio. Useful when reading from fast storage. # Do not enable parallel_io when running MCE on many # blades and input coming from shared storage. use_threads => 1, ## Auto 0 or 1 # MCE spawns child processes by default, not threads. # # However, MCE supports threads via 2 threading # libraries if threads is desired. # The use of threads in MCE requires that you include # threads support prior to loading MCE. The use_threads # option defaults to 1 when a thread library is loaded. # Threads is loaded automatically for $^O eq 'MSWin32'. # # use threads; use forks; # use threads::shared; (or) use forks::shared; # use MCE use MCE; spawn_delay => 0.035, ## Default undef submit_delay => 0.002, ## Default undef job_delay => 0.150, ## Default undef # Time to wait, in fractional seconds, after spawning # a worker, parameters submission to a worker, and # job commencement (running, staggered delay). # Specify job_delay when wanting to stagger # workers connecting to a database. on_post_exit => \&on_post_exit, ## Default undef on_post_run => \&on_post_run, ## Default undef # Execute code block after a worker exits. # exit, MCE->exit, or die # Execute code block after running a job. # MCE->process or MCE->run user_args => { env => 'test' }, ## Default undef # MCE release 1.4 added a new parameter to allow one to # specify arbitrary arguments such as a string, an ARRAY # or a HASH reference. Workers can access this directly: # my $args = $mce->{user_args} or MCE->user_args; user_begin => \&user_begin, ## Default undef user_func => \&user_func, ## Default undef user_end => \&user_end, ## Default undef # Think of user_begin, user_func, and user_end as in # the awk scripting language: # awk 'BEGIN { begin } { func } { func } ... END { end }' # MCE workers call user_begin once at the start of a job, # then user_func repeatedly until no chunks remain. # Afterwards, user_end is called. user_error => \&user_error, ## Default undef user_output => \&user_output, ## Default undef # MCE will forward data to user_error/user_output, # when defined, for the following methods. # MCE->sendto(\*STDERR, "sent to user_error\n"); # MCE->printf(\*STDERR, "%s\n", "sent to user_error"); # MCE->print(\*STDERR, "sent to user_error\n"); # MCE->say(\*STDERR, "sent to user_error"); # MCE->sendto(\*STDOUT, "sent to user_output\n"); # MCE->printf("%s\n", "sent to user_output"); # MCE->print("sent to user_output\n"); # MCE->say("sent to user_output"); stderr_file => 'err_file', ## Default STDERR stdout_file => 'out_file', ## Default STDOUT # Or to file; user_error and user_output take precedence. flush_file => 1, ## Default 0 flush_stderr => 1, ## Default 0 flush_stdout => 1, ## Default 0 # Flush sendto file, standard error, or standard output. interval => { delay => 0.007 [, max_nodes => 4, node_id => 1 ] }, # For use with the yield method introduced in MCE 1.5. # Both max_nodes & node_id are optional and default to 1. # Delay is the amount of time between intervals. # interval => 0.007 ## Shorter; MCE 1.506+ sequence => { ## Default undef begin => -1, end => 1 [, step => 0.1 [, format => "%4.1f" ] ] }, bounds_only => 1, ## Default undef # For looping through a sequence of numbers in parallel. # STEP, if omitted, defaults to 1 if BEGIN is smaller than # END or -1 if BEGIN is greater than END. The FORMAT string # is passed to sprintf behind the scene (% may be omitted). # e.g. $seq_n_formated = sprintf("%4.1f", $seq_n); # Do not specify both options; input_data and sequence. # Release 1.4 allows one to specify an array reference. # e.g. sequence => [ -1, 1, 0.1, "%4.1f" ] # The bounds_only => 1 option will compute the 'begin' and # 'end' items only for the chunk and not the items in between # (hence boundaries only). This option has no effect when # sequence is not specified or chunk_size equals 1. # my $begin = $chunk_ref->[0]; my $end = $chunk_ref->[1]; task_end => \&task_end, ## Default undef # This is called by the manager process after the task # has completed processing. MCE 1.5 allows this option # to be specified at the top level. task_name => 'string', ## Default 'MCE' # Added in MCE 1.5 and mainly beneficial for user_tasks. # One may specify a unique name per each sub-task. # The string is passed as the 3rd arg to task_end. user_tasks => [ ## Default undef { ... }, ## Options for task 0 { ... }, ## Options for task 1 { ... }, ## Options for task 2 ], # Takes a list of hash references, each allowing up to 17 # options. All other MCE options are ignored. The init_relay, # input_data, RS, and use_slurpio options are applicable to # the first task only. # max_workers, chunk_size, input_data, interval, sequence, # bounds_only, user_args, user_begin, user_end, user_func, # gather, task_end, task_name, use_slurpio, use_threads, # init_relay, RS # Options not specified here will default to same option # specified at the top level. ); =head2 EXPORT_CONST, CONST There are 3 constants which are exportable. Using the constants in lieu of 0,1,2 makes it more legible when accessing the user_func arguments directly. =head3 SELF CHUNK CID Exports SELF => 0, CHUNK => 1, and CID => 2. use MCE export_const => 1; use MCE const => 1; ## Shorter; MCE 1.415+ user_func => sub { # my ($mce, $chunk_ref, $chunk_id) = @_; print "Hello from ", $_[SELF]->wid, "\n"; } MCE 1.5 allows all public method to be called directly. use MCE; user_func => sub { # my ($mce, $chunk_ref, $chunk_id) = @_; print "Hello from ", MCE->wid, "\n"; } =head2 OVERRIDING DEFAULTS The following list 5 options which may be overridden when loading the module. use Sereal qw( encode_sereal decode_sereal ); use CBOR::XS qw( encode_cbor decode_cbor ); use JSON::XS qw( encode_json decode_json ); use MCE max_workers => 4, ## Default 1 chunk_size => 100, ## Default 1 tmp_dir => "/path/to/app/tmp", ## $MCE::Signal::tmp_dir freeze => \&encode_sereal, ## \&Storable::freeze thaw => \&decode_sereal ## \&Storable::thaw ; my $mce = MCE->new( ... ); There is a simpler way to enable Sereal with MCE 1.5. The following will attempt to use Sereal if available, otherwise defaults to Storable for serialization. use MCE Sereal => 1; ## Serialization is by the Sereal module if available. my $mce = MCE->new( ... ); =head2 RUNNING Run calls spawn, submits the job; workers call user_begin, user_func, and user_end. Run shuts down workers afterwards. Call spawn whenever the need arises for large data structures prior to running. $mce->spawn; ## Call early if desired $mce->run; ## Call run or process below ## Acquire data arrays and/or input_files. Workers persist after ## processing. $mce->process(\@input_data_1); ## Process arrays $mce->process(\@input_data_2); $mce->process(\@input_data_n); $mce->process('input_file_1'); ## Process files $mce->process('input_file_2'); $mce->process('input_file_n'); $mce->shutdown; ## Shutdown workers =head2 SYNTAX for ON_POST_EXIT Often times, one may want to capture the exit status. The on_post_exit option, if defined, is executed immediately by the manager process after a worker exits via exit (children only), MCE->exit (children and threads), or die. The format of $e->{pid} is PID_123 for children and THR_123 for threads. my $restart_flag = 1; sub on_post_exit { my ($mce, $e) = @_; ## Display all possible hash elements. print "$e->{wid}: $e->{pid}: $e->{status}: $e->{msg}: $e->{id}\n"; ## Restart this worker if desired. if ($restart_flag && $e->{wid} == 2) { $mce->restart_worker; $restart_flag = 0; } } sub user_func { my ($mce) = @_; MCE->exit(0, 'msg_foo', 1000 + MCE->wid); ## Args, not necessary } my $mce = MCE->new( on_post_exit => \&on_post_exit, user_func => \&user_func, max_workers => 3 ); $mce->run; -- Output (child processes) 2: PID_33223: 0: msg_foo: 1002 1: PID_33222: 0: msg_foo: 1001 3: PID_33224: 0: msg_foo: 1003 2: PID_33225: 0: msg_foo: 1002 -- Output (running with threads) 3: TID_3: 0: msg_foo: 1003 2: TID_2: 0: msg_foo: 1002 1: TID_1: 0: msg_foo: 1001 2: TID_4: 0: msg_foo: 1002 =head2 SYNTAX for ON_POST_RUN The on_post_run option, if defined, is executed immediately by the manager process after running MCE->process or MCE->run. This option receives an array reference of hashes. The difference between on_post_exit and on_post_run is that the former is called immediately whereas the latter is called after all workers have completed running. sub on_post_run { my ($mce, $status_ref) = @_; foreach my $e ( @{ $status_ref } ) { ## Display all possible hash elements. print "$e->{wid}: $e->{pid}: $e->{status}: $e->{msg}: $e->{id}\n"; } } sub user_func { my ($mce) = @_; MCE->exit(0, 'msg_foo', 1000 + MCE->wid); ## Args, not necessary } my $mce = MCE->new( on_post_run => \&on_post_run, user_func => \&user_func, max_workers => 3 ); $mce->run; -- Output (child processes) 3: PID_33174: 0: msg_foo: 1003 1: PID_33172: 0: msg_foo: 1001 2: PID_33173: 0: msg_foo: 1002 -- Output (running with threads) 2: TID_2: 0: msg_foo: 1002 3: TID_3: 0: msg_foo: 1003 1: TID_1: 0: msg_foo: 1001 =head2 SYNTAX for INPUT_DATA MCE supports many ways to specify input_data. Support for iterators was added in MCE 1.505. The RS option allows one to specify the record separator when processing files. MCE is a chunking engine. Therefore, chunk_size is applicable to input_data. Specifying 1 for use_slurpio causes user_func to receive a scalar reference containing the raw data (applicable to files only) instead of an array reference. input_data => '/path/to/file', ## process file input_data => \@array, ## process array input_data => \*FILE_HNDL, ## process file handle input_data => $fh, ## open $fh, "<", "file" input_data => $fh, ## IO::File "file", "r" input_data => $fh, ## IO::Uncompress::Gunzip "file.gz" input_data => \$scalar, ## treated like a file input_data => \&iterator, ## user specified iterator chunk_size => 1, ## >1 means looping inside user_func use_slurpio => 1, ## $chunk_ref is a scalar ref RS => "\n>", ## input record separator The chunk_size value determines the chunking mode to use when processing files. Otherwise, chunk_size is the number of elements for arrays. For files, a chunk size value of <= 8192 is how many records to read. Greater than 8192 is how many bytes to read. MCE appends (the rest) up to the next record separator. chunk_size => 8192, ## Consists of 8192 records chunk_size => 8193, ## Approximate 8193 bytes for files chunk_size => 1, ## Consists of 1 record or element chunk_size => 1000, ## Consists of 1000 records chunk_size => '16K', ## Approximate 16 kilobytes chunk_size => '20M', ## Approximate 20 megabytes The construction for user_func when chunk_size > 1 and assuming use_slurpio equals 0. user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; ## $_ is $chunk_ref->[0] when chunk_size equals 1 ## $_ is $chunk_ref otherwise; $_ can be used below for my $record ( @{ $chunk_ref } ) { print "$chunk_id: $record\n"; } } Specifying a value for input_data is straight forward for arrays and files. The next several examples specify an iterator reference for input_data. use MCE; ## A factory function which creates a closure (the iterator itself) ## for generating a sequence of numbers. The external variables ## ($n, $max, $step) are used for keeping state across successive ## calls to the closure. The iterator simply returns when $n > max. sub input_iterator { my ($n, $max, $step) = @_; return sub { return if $n > $max; my $current = $n; $n += $step; return $current; }; } ## Run user_func in parallel. Input data can be specified during ## the construction or as an argument to the process method. my $mce = MCE->new( # input_data => input_iterator(10, 30, 2), chunk_size => 1, max_workers => 4, user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->print("$_: ", $_ * 2, "\n"); } )->spawn; $mce->process( input_iterator(10, 30, 2) ); -- Output Note that output order is not guaranteed Take a look at iterator.pl for ordered output 10: 20 12: 24 16: 32 20: 40 14: 28 22: 44 18: 36 24: 48 26: 52 28: 56 30: 60 The following example queries the DB for the next 1000 rows. Notice the use of fetchall_arrayref. The iterator function itself recieves one argument which is the chunk_size value (added in MCE 1.510). use DBI; use MCE; sub db_iter { my $dsn = "DBI:Oracle:host=db_server;port=db_port;sid=db_name"; my $dbh = DBI->connect($dsn, 'db_user', 'db_passwd') || die "Could not connect to database: $DBI::errstr"; my $sth = $dbh->prepare('select color, desc from table'); $sth->execute; return sub { my $chunk_size = shift; if (my $aref = $sth->fetchall_arrayref(undef, $chunk_size)) { return @{ $aref }; } return; }; } ## Let's enumerate column indexes for easy column retrieval. my ($i_color, $i_desc) = (0 .. 1); my $mce = MCE->new( max_workers => 3, chunk_size => 1000, input_data => db_iter(), user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; my $ret = ''; foreach my $row (@{ $chunk_ref }) { $ret .= $row->[$i_color] .": ". $row->[$i_desc] ."\n"; } MCE->print($ret); } ); $mce->run; There are many modules on CPAN which return an iterator reference. Showing one such example below. The demonstration ensures MCE workers are spawned before obtaining the iterator. Note the worker_id value (left column) in the output. use Path::Iterator::Rule; use MCE; my $start_dir = shift or die "Please specify a starting directory"; -d $start_dir or die "Cannot open ($start_dir): No such file or directory"; my $mce = MCE->new( max_workers => 'auto', user_func => sub { MCE->say( MCE->wid . ": $_" ) } )->spawn; my $rule = Path::Iterator::Rule->new->file->name( qr/[.](pm)$/ ); my $iterator = $rule->iter( $start_dir, { follow_symlinks => 0, depthfirst => 1 } ); $mce->process( $iterator ); -- Output 8: lib/MCE/Core/Input/Generator.pm 5: lib/MCE/Core/Input/Handle.pm 6: lib/MCE/Core/Input/Iterator.pm 2: lib/MCE/Core/Input/Request.pm 3: lib/MCE/Core/Manager.pm 4: lib/MCE/Core/Input/Sequence.pm 7: lib/MCE/Core/Validation.pm 1: lib/MCE/Core/Worker.pm 8: lib/MCE/Flow.pm 5: lib/MCE/Grep.pm 6: lib/MCE/Loop.pm 2: lib/MCE/Map.pm 3: lib/MCE/Queue.pm 4: lib/MCE/Signal.pm 7: lib/MCE/Stream.pm 1: lib/MCE/Subs.pm 8: lib/MCE/Util.pm 5: lib/MCE.pm Although MCE supports arrays, extra measures are needed to use a "lazy" array as input data. The reason for this is that MCE needs the size of the array before processing which may be unknown for lazy arrays. Therefore, closures provides an excellent mechanism for this. The code block belonging to the lazy array must return undef after exhausting its input data. Otherwise, the process will never end. use Tie::Array::Lazy; use MCE; tie my @a, 'Tie::Array::Lazy', [], sub { my $i = $_[0]->index; return ($i < 10) ? $i : undef; }; sub make_iterator { my $i = 0; my $a_ref = shift; return sub { return $a_ref->[$i++]; }; } my $mce = MCE->new( max_workers => 4, input_data => make_iterator(\@a), user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->say($_); } )->run; -- Output 0 1 2 3 4 6 7 8 5 9 The following demonstrates how to retrieve a chunk from the lazy array per each successive call. Here, undef is sent by the iterator block when $i is greater than $max. use Tie::Array::Lazy; use MCE; tie my @a, 'Tie::Array::Lazy', [], sub { $_[0]->index; }; sub make_iterator { my $j = 0; my ($a_ref, $max) = @_; return sub { my $i = $j; $j += MCE->chunk_size; return if $i > $max; return $j <= $max ? @$a_ref[$i .. $j - 1] : @$a_ref[$i .. $max]; }; } my $mce = MCE->new( chunk_size => 15, max_workers => 4, input_data => make_iterator(\@a, 100), user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->say("$chunk_id: " . join(' ', @{ $chunk_ref })); } )->run; -- Output 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 3: 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 4: 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 5: 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 6: 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 7: 90 91 92 93 94 95 96 97 98 99 100 =head2 SYNTAX for SEQUENCE The 1.3 release and above allows workers to loop through a sequence of numbers computed mathematically without the overhead of an array. The sequence can be specified separately per each user_task entry unlike input_data which is applicable to the first task only. See the seq_demo.pl example, included with this distribution, on applying sequences with the user_tasks option. Sequence can be defined using an array or a hash reference. use MCE; my $mce = MCE->new( max_workers => 3, # sequence => [ 10, 19, 0.7, "%4.1f" ], ## up to 4 options sequence => { begin => 10, end => 19, step => 0.7, format => "%4.1f" }, user_func => sub { my ($mce, $n, $chunk_id) = @_; print $n, " from ", MCE->wid, " id ", $chunk_id, "\n"; } ); $mce->run; -- Output (sorted afterwards, notice wid and chunk_id in output) 10.0 from 1 id 1 10.7 from 2 id 2 11.4 from 3 id 3 12.1 from 1 id 4 12.8 from 2 id 5 13.5 from 3 id 6 14.2 from 1 id 7 14.9 from 2 id 8 15.6 from 3 id 9 16.3 from 1 id 10 17.0 from 2 id 11 17.7 from 3 id 12 18.4 from 1 id 13 The 1.5 release includes a new option (bounds_only). This option tells the sequence engine to compute 'begin' and 'end' items only, for the chunk, and not the items in between (hence boundaries only). This option applies to sequence only and has no effect when chunk_size equals 1. The time to run is 0.006s below. This becomes 0.827s without the bounds_only option due to computing all items in between, thus creating a very large array. Basically, specify bounds_only => 1 when boundaries is all you need for looping inside the block; e.g. Monte Carlo simulations. Time was measured using 1 worker to emphasize the difference. use MCE; my $mce = MCE->new( max_workers => 1, chunk_size => 1_250_000, sequence => { begin => 1, end => 10_000_000 }, bounds_only => 1, ## For sequence, the input scalar $_ points to $chunk_ref ## when chunk_size > 1, otherwise $chunk_ref->[0]. ## ## user_func => sub { ## my $begin = $_->[0]; my $end = $_->[-1]; ## ## for ($begin .. $end) { ## ... ## } ## }, user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; ## $chunk_ref contains 2 items, not 1_250_000 my $begin = $chunk_ref->[ 0]; my $end = $chunk_ref->[-1]; ## or $chunk_ref->[1] MCE->printf("%7d .. %8d\n", $begin, $end); } ); $mce->run; -- Output 1 .. 1250000 1250001 .. 2500000 2500001 .. 3750000 3750001 .. 5000000 5000001 .. 6250000 6250001 .. 7500000 7500001 .. 8750000 8750001 .. 10000000 =head2 SYNTAX for USER_BEGIN and USER_END The user_begin and user_end options, if specified, behave similarly to awk 'BEGIN { begin } { func } { func } ... END { end }'. These are called once per worker during each run. MCE 1.510 passes 2 additional parameters ($task_id and $task_name). sub user_begin { ## Called once at the beginning my ($mce, $task_id, $task_name) = @_; $mce->{wk_total_rows} = 0; } sub user_func { ## Called while processing my $mce = shift; $mce->{wk_total_rows} += 1; } sub user_end { ## Called once at the end my ($mce, $task_id, $task_name) = @_; printf "## %d: Processed %d rows\n", MCE->wid, $mce->{wk_total_rows}; } my $mce = MCE->new( user_begin => \&user_begin, user_func => \&user_func, user_end => \&user_end ); $mce->run; =head2 SYNTAX for USER_FUNC with USE_SLURPIO => 0 When processing input data, MCE can pass an array of rows or a slurped chunk. Below, a reference to an array containing the chunk data is processed. e.g. $chunk_ref = [ record1, record2, record3, ... ] sub user_func { my ($mce, $chunk_ref, $chunk_id) = @_; foreach my $row ( @{ $chunk_ref } ) { $mce->{wk_total_rows} += 1; print $row; } } my $mce = MCE->new( chunk_size => 100, input_data => "/path/to/file", user_func => \&user_func, use_slurpio => 0 ); $mce->run; =head2 SYNTAX for USER_FUNC with USE_SLURPIO => 1 Here, a reference to a scalar containing the raw chunk data is processed. sub user_func { my ($mce, $chunk_ref, $chunk_id) = @_; my $count = () = $$chunk_ref =~ /abc/; } my $mce = MCE->new( chunk_size => 16000, input_data => "/path/to/file", user_func => \&user_func, use_slurpio => 1 ); $mce->run; =head2 SYNTAX for USER_ERROR and USER_OUTPUT Output from MCE->sendto('STDERR/STDOUT', ...), MCE->printf, MCE->print, and MCE->say can be intercepted by specifying the user_error and user_output options. MCE on receiving output will forward to user_error or user_output in a serialized fashion. Handy when wanting to filter, modify, and/or direct the output elsewhere. sub user_error { ## Redirect STDERR to STDOUT my $error = shift; print {*STDOUT} $error; } sub user_output { ## Redirect STDOUT to STDERR my $output = shift; print {*STDERR} $output; } sub user_func { my ($mce, $chunk_ref, $chunk_id) = @_; my $count = 0; foreach my $row ( @{ $chunk_ref } ) { MCE->print($row); $count += 1; } MCE->print(\*STDERR, "$chunk_id: processed $count rows\n"); } my $mce = MCE->new( chunk_size => 1000, input_data => "/path/to/file", user_error => \&user_error, user_output => \&user_output, user_func => \&user_func ); $mce->run; =head2 SYNTAX for USER_TASKS and TASK_END This option takes an array of tasks. Each task allows up to 17 options. The init_relay, input_data, RS, and use_slurpio options may be defined inside the first task or at the top level, otherwise ignored under other sub-tasks. max_workers, chunk_size, input_data, interval, sequence, bounds_only, user_args, user_begin, user_end, user_func, gather, task_end, task_name, use_slurpio, use_threads, init_relay, RS Sequence and chunk_size were added in 1.3. User_args was introduced in 1.4. Name and input_data are new options allowed in 1.5. In addition, one can specify task_end at the top level. Task_end also receives 2 additional arguments $task_id and $task_name (shown below). Options not specified here will default to the same option specified at the top level. The task_end option is called by the manager process when all workers for that sub-task have completed processing. Forking and threading can be intermixed among tasks unless running Cygwin. The run method will continue running until all workers have completed processing. use threads; use threads::shared; use MCE; sub parallel_task1 { sleep 2; } sub parallel_task2 { sleep 1; } my $mce = MCE->new( task_end => sub { my ($mce, $task_id, $task_name) = @_; print "Task [$task_id -- $task_name] completed processing\n"; }, user_tasks => [{ task_name => 'foo', max_workers => 2, user_func => \¶llel_task1, use_threads => 0 ## Not using threads },{ task_name => 'bar', max_workers => 4, user_func => \¶llel_task2, use_threads => 1 ## Yes, threads }] ); $mce->run; -- Output Task [1 -- bar] completed processing Task [0 -- foo] completed processing =head1 DEFAULT INPUT SCALAR Beginning with MCE 1.5, the input scalar $_ is localized prior to calling user_func for input_data and sequence of numbers. The following applies. =over 3 =item use_slurpio => 1 $_ is a reference to the buffer e.g. $_ = \$_buffer; $_ is a reference regardless of whether chunk_size is 1 or greater user_func => sub { # my ($mce, $chunk_ref, $chunk_id) = @_; print ${ $_ }; ## $_ is same as $chunk_ref } =item chunk_size is greater than 1, use_slurpio => 0 $_ is a reference to an array. $_ = \@_records; $_ = \@_seq_n; $_ is same as $chunk_ref or $_[CHUNK] user_func => sub { # my ($mce, $chunk_ref, $chunk_id) = @_; for my $row ( @{ $_ } ) { print $row, "\n"; } } use MCE const => 1; user_func => sub { # my ($mce, $chunk_ref, $chunk_id) = @_; for my $row ( @{ $_[CHUNK] } ) { print $row, "\n"; } } =item chunk_size equals 1, use_slurpio => 0 $_ contains the actual value. $_ = $_buffer; $_ = $seq_n; ## Note that $_ and $chunk_ref are not the same below. ## $chunk_ref is a reference to an array. user_func => sub { # my ($mce, $chunk_ref, $chunk_id) = @_; print $_, "\n; ## Same as $chunk_ref->[0]; } $mce->foreach("/path/to/file", sub { # my ($mce, $chunk_ref, $chunk_id) = @_; print $_; ## Same as $chunk_ref->[0]; }); ## However, that is not the case for the forseq method. ## Both $_ and $n_seq are the same when chunk_size => 1. $mce->forseq([ 1, 9 ], sub { # my ($mce, $n_seq, $chunk_id) = @_; print $_, "\n"; ## Same as $n_seq }); Sequence can also be specified using an array reference. The below is the same as the example afterwards. $mce->forseq( { begin => 10, end => 40, step => 2 }, ... ); The code block receives an array containing the next 5 sequences. Chunk 1 (chunk_id 1) contains 10,12,14,16,18. $n_seq is a reference to an array, same as $_, due to chunk_size being greater than 1. $mce->forseq( [ 10, 40000, 2 ], { chunk_size => 5 }, sub { # my ($mce, $n_seq, $chunk_id) = @_; my @result; for my $n ( @{ $_ } ) { ... do work, append to result for 5 } ... do something with result afterwards }); =back =head1 METHODS for the MANAGER PROCESS and WORKERS The methods listed below are callable by the main process and workers. =head2 $mce->abort ( void ) The 'abort' method is applicable when processing input_data only. This causes all workers to abort after processing the current chunk. Workers write the next offset position to the queue socket for the next available worker. In essence, the 'abort' method writes the last offset position. Workers, on requesting the next offset position, will think the end of input_data has been reached and leave the chunking loop. $mce->abort; MCE->abort; =head2 $mce->chunk_id ( void ) Returns the chunk_id for the current chunk. The value starts at 1. Chunking applies to input_data or sequence. The value is 0 for the manager process. my $chunk_id = $mce->chunk_id; my $chunk_id = MCE->chunk_id; =head2 $mce->chunk_size ( void ) Returns the chunk_size used by MCE. my $chunk_size = $mce->chunk_size; my $chunk_size = MCE->chunk_size; =head2 $mce->freeze ( $object_ref ) Calls the internal freeze method to serialize an object. The default serialization routines are handled by Storable. Both freeze and thaw can be overridden when including MCE. my $frozen = $mce->freeze([ 0, 2, 4 ]); my $frozen = MCE->freeze([ 0, 2, 4 ]); =head2 $mce->max_workers ( void ) Returns the value for max_workers used by MCE. my $max_workers = $mce->max_workers; my $max_workers = MCE->max_workers; =head2 $mce->pid ( void ) Returns the Process ID. Threads have thread ID attached to the value. my $pid = $mce->pid; ## 16180 (pid) ; 16180.2 (pid.tid) my $pid = MCE->pid; =head2 $mce->sess_dir ( void ) Returns the session directory used by the MCE instance. This is defined during spawning and removed during shutdown. my $sess_dir = $mce->sess_dir; my $sess_dir = MCE->sess_dir; =head2 $mce->task_id ( void ) Returns the task ID. This applies to the user_tasks option (starts at 0). my $task_id = $mce->task_id; my $task_id = MCE->task_id; =head2 $mce->task_name ( void ) Returns the task_name value specified via the task_name option when configuring MCE. my $task_name = $mce->task_name; my $task_name = MCE->task_name; =head2 $mce->task_wid ( void ) Returns the task worker ID (applies to user_tasks). The value starts at 1 per each task configured within user_tasks. The value is 0 for the manager process. my $task_wid = $mce->task_wid; my $task_wid = MCE->task_wid; =head2 $mce->thaw ( $frozen ) Calls the internal thaw method to un-serialize the frozen object. my $object_ref = $mce->thaw($frozen); my $object_ref = MCE->thaw($frozen); =head2 $mce->tmp_dir ( void ) Returns the temporary directory used by MCE. my $tmp_dir = $mce->tmp_dir; my $tmp_dir = MCE->tmp_dir; =head2 $mce->user_args ( void ) Returns the arguments specified via the user_args option. my ($arg1, $arg2, $arg3) = $mce->user_args; my ($arg1, $arg2, $arg3) = MCE->user_args; =head2 $mce->wid ( void ) Returns the MCE worker ID. Starts at 1 per each MCE instance. The value is 0 for the manager process. my $wid = $mce->wid; my $wid = MCE->wid; =head1 METHODS for the MANAGER PROCESS only Methods listed below are callable by the main process only. =head2 $mce->forchunk ( $input_data [, { options } ], sub { ... } ) =head2 $mce->foreach ( $input_data [, { options } ], sub { ... } ) =head2 $mce->forseq ( $sequence_spec [, { options } ], sub { ... } ) Forchunk, foreach, and forseq are sugar methods and described in L. Stubs exist in MCE which load MCE::Candy automatically. =head2 $mce->process ( $input_data [, { options } ] ) The process method will spawn workers automatically if not already spawned. It will set input_data => $input_data. It calls run(0) to not auto-shutdown workers. Specifying options is optional. Allowable options { key => value, ... } are: chunk_size input_data job_delay spawn_delay submit_delay flush_file flush_stderr flush_stdout stderr_file stdout_file on_post_exit on_post_run sequence user_args user_begin user_end user_func user_error user_output use_slurpio RS Options remain persistent going forward unless changed. Setting user_begin, user_end, or user_func will cause already spawned workers to shut down and re-spawn automatically. Therefore, define these during instantiation. The below will cause workers to re-spawn after running. my $mce = MCE->new( max_workers => 'auto' ); $mce->process( { user_begin => sub { ## connect to DB }, user_func => sub { ## process each row }, user_end => sub { ## close handle to DB }, }, \@input_data ); $mce->process( { user_begin => sub { ## connect to DB }, user_func => sub { ## process each file }, user_end => sub { ## close handle to DB }, }, "/list/of/files" ); Do the following if wanting workers to persist between jobs. use MCE max_workers => 'auto'; my $mce = MCE->new( user_begin => sub { ## connect to DB }, user_func => sub { ## process each chunk or row or host }, user_end => sub { ## close handle to DB }, ); $mce->spawn; ## Spawn early if desired $mce->process("/one/very_big_file/_mce_/will_chunk_in_parallel"); $mce->process(\@array_of_files_to_grep); $mce->process("/path/to/host/list"); $mce->process($array_ref); $mce->process($array_ref, { stdout_file => $output_file }); ## This was not allowed before. Fixed in 1.415. $mce->process({ sequence => { begin => 10, end => 90, step 2 } }); $mce->process({ sequence => [ 10, 90, 2 ] }); $mce->shutdown; =head2 $mce->restart_worker ( void ) One can restart a worker who has died or exited. The job never ends below due to restarting each time. Recommended is to call MCE->exit or $mce->exit instead of the native exit function for better handling, especially under the Windows environment. The $e->{wid} argument is no longer necessary starting with the 1.5 release. Press [ctrl-c] to terminate the script. my $mce = MCE->new( on_post_exit => sub { my ($mce, $e) = @_; print "$e->{wid}: $e->{pid}: status $e->{status}: $e->{msg}"; # $mce->restart_worker($e->{wid}); ## MCE-1.415 and below $mce->restart_worker; ## MCE-1.500 and above }, user_begin => sub { my ($mce, $task_id, $task_name) = @_; ## Not interested in die messages going to STDERR, ## because the die handler calls MCE->exit(255, $_[0]). close STDERR; }, user_tasks => [{ max_workers => 5, user_func => sub { my ($mce) = @_; sleep MCE->wid; MCE->exit(3, "exited from " . MCE->wid . "\n"); } },{ max_workers => 4, user_func => sub { my ($mce) = @_; sleep MCE->wid; die("died from " . MCE->wid . "\n"); } }] ); $mce->run; -- Output 1: PID_85388: status 3: exited from 1 2: PID_85389: status 3: exited from 2 1: PID_85397: status 3: exited from 1 3: PID_85390: status 3: exited from 3 1: PID_85399: status 3: exited from 1 4: PID_85391: status 3: exited from 4 2: PID_85398: status 3: exited from 2 1: PID_85401: status 3: exited from 1 5: PID_85392: status 3: exited from 5 1: PID_85404: status 3: exited from 1 6: PID_85393: status 255: died from 6 3: PID_85400: status 3: exited from 3 2: PID_85403: status 3: exited from 2 1: PID_85406: status 3: exited from 1 7: PID_85394: status 255: died from 7 1: PID_85410: status 3: exited from 1 8: PID_85395: status 255: died from 8 4: PID_85402: status 3: exited from 4 2: PID_85409: status 3: exited from 2 1: PID_85412: status 3: exited from 1 9: PID_85396: status 255: died from 9 3: PID_85408: status 3: exited from 3 1: PID_85416: status 3: exited from 1 ... =head2 $mce->run ( [ $auto_shutdown [, { options } ] ] ) The run method, by default, spawns workers, processes once, and shuts down afterwards. Specify 0 for $auto_shutdown when wanting workers to persist after running (default 1). Specifying options is optional. Valid options are the same as for the process method. my $mce = MCE->new( ... ); ## Disables auto-shutdown $mce->run(0); =head2 $mce->send ( $data_ref ) The 'send' method is useful when wanting to spawn workers early to minimize memory consumption and afterwards send data individually to each worker. One cannot send more than the total workers spawned. Workers store the received data as $mce->{user_data}. The data which can be sent is restricted to an ARRAY, HASH, or PDL reference. Workers begin processing immediately after receiving data. Workers set $mce->{user_data} to undef after processing. One cannot specify input_data, sequence, or user_tasks when using the "send" method. Passing any options e.g. run(0, { options }) is ignored due to workers running immediately after receiving user data. There is no guarantee to which worker will receive data first. It depends on which worker is available awaiting data. use MCE; my $mce = MCE->new( max_workers => 5, user_func => sub { my ($mce) = @_; my $data = $mce->{user_data}; my $first_name = $data->{first_name}; print MCE->wid, ": Hello from $first_name\n"; } ); $mce->spawn; ## Optional, send will spawn if necessary. $mce->send( { first_name => "Theresa" } ); $mce->send( { first_name => "Francis" } ); $mce->send( { first_name => "Padre" } ); $mce->send( { first_name => "Anthony" } ); $mce->run; ## Wait for workers to complete processing. -- Output 2: Hello from Theresa 5: Hello from Anthony 3: Hello from Francis 4: Hello from Padre =head2 $mce->shutdown ( void ) The run method will automatically spawn workers, run once, and shutdown workers automatically. Workers persist after running below. Shutdown may be called as needed or prior to exiting. my $mce = MCE->new( ... ); $mce->spawn; $mce->process(\@input_data_1); ## Processing multiple arrays $mce->process(\@input_data_2); $mce->process(\@input_data_n); $mce->shutdown; $mce->process('input_file_1'); ## Processing multiple files $mce->process('input_file_2'); $mce->process('input_file_n'); $mce->shutdown; =head2 $mce->spawn ( void ) Workers are normally spawned automatically. The spawn method allows one to spawn workers early if so desired. my $mce = MCE->new( ... ); $mce->spawn; =head2 $mce->status ( void ) The greatest exit status is saved among workers while running. Look at the on_post_exit or on_post_run options for callback support. my $mce = MCE->new( ... ); $mce->run; my $exit_status = $mce->status; =head1 METHODS for WORKERS only Methods listed below are callable by workers only. =head2 $mce->do ( 'callback_func' [, $arg1, ... ] ) MCE serializes data transfers from a worker process via helper functions do & sendto to the manager process. The callback function can optionally return a reply. [ $reply = ] MCE->do('callback' [, $arg1, ... ]); Passing args to a callback function using references & scalar. sub callback { my ($array_ref, $hash_ref, $scalar_ref, $scalar) = @_; ... } MCE->do('main::callback', \@a, \%h, \$s, 'foo'); MCE->do('callback', \@a, \%h, \$s, 'foo'); MCE knows if wanting a void, list, hash, or a scalar return value. MCE->do('callback' [, $arg1, ... ]); my @array = MCE->do('callback' [, $arg1, ... ]); my %hash = MCE->do('callback' [, $arg1, ... ]); my $scalar = MCE->do('callback' [, $arg1, ... ]); =head2 $mce->exit ( [ $status [, $message [, $id ] ] ] ) A worker exits from MCE entirely. $id (optional) can be used for passing the primary key or a string along with the message. Look at the on_post_exit or on_post_run options for callback support. MCE->exit; ## default 0 MCE->exit(1); MCE->exit(2, 'chunk failed', $chunk_id); MCE->exit(0, 'msg_foo', 'id_1000'); =head2 $mce->gather ( $arg1, [, $arg2, ... ] ) A worker can submit data to the location specified via the gather option by calling this method. See L and L for additional use-case. use MCE; my @hosts = qw( hosta hostb hostc hostd hoste ); my $mce = MCE->new( chunk_size => 1, max_workers => 3, user_func => sub { # my ($mce, $chunk_ref, $chunk_id) = @_; my ($output, $error, $status); my $host = $_; ## Do something with $host; $output = "Worker ". MCE->wid .": Hello from $host"; if (MCE->chunk_id % 3 == 0) { ## Simulating an error condition local $? = 1; $status = $?; $error = "Error from $host" } else { $status = 0; } ## Ensure unique keys (key, value) when gathering to a ## hash. MCE->gather("$host.out", $output, "$host.sta", $status); MCE->gather("$host.err", $error) if (defined $error); } ); my %h; $mce->process(\@hosts, { gather => \%h }); foreach my $host (@hosts) { print $h{"$host.out"}, "\n"; print $h{"$host.err"}, "\n" if (exists $h{"$host.err"}); print "Exit status: ", $h{"$host.sta"}, "\n\n"; } -- Output Worker 2: Hello from hosta Exit status: 0 Worker 1: Hello from hostb Exit status: 0 Worker 3: Hello from hostc Error from hostc Exit status: 1 Worker 2: Hello from hostd Exit status: 0 Worker 1: Hello from hoste Exit status: 0 =head2 $mce->last ( void ) Worker leaves the chunking loop or user_func block immediately. Callable from inside foreach, forchunk, forseq, and user_func. use MCE; my $mce = MCE->new( max_workers => 5 ); my @list = (1 .. 80); $mce->forchunk(\@list, { chunk_size => 2 }, sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->last if ($chunk_id > 4); my @output = (); foreach my $rec ( @{ $chunk_ref } ) { push @output, $rec, "\n"; } MCE->print(@output); }); -- Output (each chunk above consists of 2 elements) 3 4 1 2 7 8 5 6 =head2 $mce->next ( void ) Worker starts the next iteration of the chunking loop. Callable from inside foreach, forchunk, forseq, and user_func. use MCE; my $mce = MCE->new( max_workers => 5 ); my @list = (1 .. 80); $mce->forchunk(\@list, { chunk_size => 4 }, sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->next if ($chunk_id < 20); my @output = (); foreach my $rec ( @{ $chunk_ref } ) { push @output, $rec, "\n"; } MCE->print(@output); }); -- Output (each chunk above consists of 4 elements) 77 78 79 80 =head2 $mce->printf ( $format, $list [, ... ] ) =head2 $mce->print ( $list [, ... ] ) =head2 $mce->say ( $list [, ... ] ) Use the printf, print, and say methods when wanting to serialize output among workers. These are sugar syntax for the sendto method. These behave similar to the native subroutines in Perl with the exception that barewords must be passed as a reference and require the comma after it including file handles. Say is like print, but implicitly appends a newline. MCE->printf(\*STDOUT, "%s: %d\n", $name, $age); MCE->printf($fh, "%s: %d\n", $name, $age); MCE->printf("%s: %d\n", $name, $age); MCE->print(\*STDERR, "$error_msg\n"); MCE->print($fh, $log_msg."\n"); MCE->print("$output_msg\n"); MCE->say(\*STDERR, $error_msg); MCE->say($fh, $log_msg); MCE->say($output_msg); Caveat: Use the following syntax when passing a reference not a glob or file handle. Otherwise, MCE will error indicating the first argument is not a glob reference. MCE->print(\*STDOUT, \@array, "\n"); MCE->print("", \@array, "\n"); ## ok =head2 $mce->sendto ( $to, $arg1, ... ) The sendto method is called by workers for serializing data to standard output, standard error, or end of file. The action is done by the manager process. Release 1.00x supported 1 data argument, not more. MCE->sendto('file', \@array, '/path/to/file'); MCE->sendto('file', \$scalar, '/path/to/file'); MCE->sendto('file', $scalar, '/path/to/file'); MCE->sendto('STDERR', \@array); MCE->sendto('STDERR', \$scalar); MCE->sendto('STDERR', $scalar); MCE->sendto('STDOUT', \@array); MCE->sendto('STDOUT', \$scalar); MCE->sendto('STDOUT', $scalar); Release 1.100 added the ability to pass multiple arguments. Notice the syntax change for sending to a file. Passing a reference to an array is no longer necessary. MCE->sendto('file:/path/to/file', $arg1 [, $arg2, ... ]); MCE->sendto('STDERR', $arg1 [, $arg2, ... ]); MCE->sendto('STDOUT', $arg1 [, $arg2, ... ]); MCE->sendto('STDOUT', @a, "\n", %h, "\n", $s, "\n"); To retain 1.00x compatibility, sendto outputs the content when a single data reference is specified. Otherwise, the reference for \@array or \$scalar is shown in 1.500, not the content. MCE->sendto('STDERR', \@array); ## 1.00x behavior, content MCE->sendto('STDOUT', \$scalar); MCE->sendto('file:/path/to/file', \@array); ## Output matches the print statement MCE->sendto(\*STDERR, \@array); ## 1.500 behavior, reference MCE->sendto(\*STDOUT, \$scalar); MCE->sendto($fh, \@array); MCE->sendto('STDOUT', \@array, "\n", \$scalar, "\n"); print {*STDOUT} \@array, "\n", \$scalar, "\n"; MCE 1.500 added support for sending to a glob reference, file descriptor, and file handle. MCE->sendto(\*STDERR, "foo\n", \@array, \$scalar, "\n"); MCE->sendto('fd:2', "foo\n", \@array, \$scalar, "\n"); MCE->sendto($fh, "foo\n", \@array, \$scalar, "\n"); =head2 $mce->sync ( void ) A barrier sync operation means any worker must stop at this point until all workers reach this barrier. Barrier syncing is useful for many computer algorithms. Barrier synchronization is supported for task 0 only or omitting user_tasks. All workers assigned task_id 0 must call sync whenever barrier syncing. use MCE; sub user_func { my ($mce) = @_; my $wid = MCE->wid; MCE->sendto("STDOUT", "a: $wid\n"); ## MCE 1.0+ MCE->sync; MCE->sendto(\*STDOUT, "b: $wid\n"); ## MCE 1.5+ MCE->sync; MCE->print("c: $wid\n"); ## MCE 1.5+ MCE->sync; return; } my $mce = MCE->new( max_workers => 4, user_func => \&user_func )->run; -- Output (without barrier synchronization) a: 1 a: 2 b: 1 b: 2 c: 1 c: 2 a: 3 b: 3 c: 3 a: 4 b: 4 c: 4 -- Output (with barrier synchronization) a: 1 a: 2 a: 4 a: 3 b: 2 b: 1 b: 3 b: 4 c: 1 c: 4 c: 2 c: 3 Consider the following example. The MCE->sync operation is done inside a loop along with MCE->do. A stall may occur for workers calling sync the 2nd or 3rd time while other workers are sending results via MCE->do or MCE->sendto. It requires another semaphore lock in MCE to solve this which was not done in order to keep resources low. Therefore, please keep this in mind when mixing MCE->sync with MCE->do or output serialization methods inside a loop. sub user_func { my ($mce) = @_; my @result; for (1 .. 3) { ... compute algorithm ... MCE->sync; ... compute algorithm ... MCE->sync; MCE->do('aggregate_result', \@result); ## or MCE->sendto MCE->sync; ## The sync operation is also needed here to ## prevent MCE from stalling. } } =head2 $mce->yield ( void ) There may be on occasion when the MCE driven app is too fast. The interval option combined with the yield method, both introduced with MCE 1.5, allows one to throttle the app. It adds a "grace" factor to the design. A use case is an app configured with 100 workers running on a 24 logical way box. Data is polled from a database containing over 2.5 million rows. Workers chunk away at 300 rows per chunk performing SNMP gets (300 sockets per worker) polling 25 metrics from each device. With this scenario, the load on the box may rise beyond 90+. In addition, IP_Tables may reach its contention point causing the entire application to fail. The scenario above is solved by simply having workers yield among themselves in a synchronized fashion. A delay of 0.007 seconds between intervals is all that's needed. The load on the box will hover between 23 ~ 27 for the duration of the run. Polling completes in under 17 minutes time. This is quite fast considering the app polls 62.5 million metrics combined. The math equates to 3,676,470 per minute or rather 61,275 per second from a single box. ## Both max_nodes and node_id are optional (default 1). interval => { delay => 0.007, max_nodes => $max_nodes, node_id => $node_id } A 4 node setup can poll 10 million devices without the additional overhead of a distribution agent. The difference between the 4 nodes are simply node_id and the where clause used to query the database. The mac addresses are random such that the data divides equally to any power of 2. The distribution key lies in the mac address itself. In fact, the 2nd character from the right is sufficient for maximizing on the power of randomness for equal distribution. Query NodeID 1: ... AND substr(MAC, -2, 1) IN ('0', '1', '2', '3') Query NodeID 2: ... AND substr(MAC, -2, 1) IN ('4', '5', '6', '7') Query NodeID 3: ... AND substr(MAC, -2, 1) IN ('8', '9', 'A', 'B') Query NodeID 4: ... AND substr(MAC, -2, 1) IN ('C', 'D', 'E', 'F') Below, the user_tasks is configured to simulate 4 nodes. This demonstration uses 2 workers to minimize the output size. Input is from the sequence option. use Time::HiRes qw(time); use MCE; my $d = shift || 0.1; local $| = 1; sub create_task { my ($node_id) = @_; my $seq_size = 6; my $seq_start = ($node_id - 1) * $seq_size + 1; my $seq_end = $seq_start + $seq_size - 1; return { max_workers => 2, sequence => [ $seq_start, $seq_end ], interval => { delay => $d, max_nodes => 4, node_id => $node_id } }; } sub user_begin { my ($mce, $task_id, $task_name) = @_; ## The yield method causes this worker to wait for its next time ## interval slot before running. Yield has no effect without the ## 'interval' option. ## Yielding is beneficial inside a user_begin block. A use case ## is staggering database connections among workers in order ## to not impact the DB server. MCE->yield; MCE->printf( "Node %2d: %0.5f -- Worker %2d: %12s -- Started\n", MCE->task_id + 1, time, MCE->task_wid, '' ); return; } { my $prev_time = time; sub user_func { my ($mce, $seq_n, $chunk_id) = @_; ## Yield simply waits for the next time interval. MCE->yield; ## Calculate how long this worker has waited. my $curr_time = time; my $time_waited = $curr_time - $prev_time; $prev_time = $curr_time; MCE->printf( "Node %2d: %0.5f -- Worker %2d: %12.5f -- Seq_N %3d\n", MCE->task_id + 1, time, MCE->task_wid, $time_waited, $seq_n ); return; } } ## Simulate a 4 node environment passing node_id to create_task. print "Node_ID Current_Time Worker_ID Time_Waited Comment\n"; MCE->new( user_begin => \&user_begin, user_func => \&user_func, user_tasks => [ create_task(1), create_task(2), create_task(3), create_task(4) ] )->run; -- Output (notice Current_Time below, stays 0.10 apart) Node_ID Current_Time Worker_ID Time_Waited Comment Node 1: 1374807976.74634 -- Worker 1: -- Started Node 2: 1374807976.84634 -- Worker 1: -- Started Node 3: 1374807976.94638 -- Worker 1: -- Started Node 4: 1374807977.04639 -- Worker 1: -- Started Node 1: 1374807977.14634 -- Worker 2: -- Started Node 2: 1374807977.24640 -- Worker 2: -- Started Node 3: 1374807977.34649 -- Worker 2: -- Started Node 4: 1374807977.44657 -- Worker 2: -- Started Node 1: 1374807977.54636 -- Worker 1: 0.90037 -- Seq_N 1 Node 2: 1374807977.64638 -- Worker 1: 1.00040 -- Seq_N 7 Node 3: 1374807977.74642 -- Worker 1: 1.10043 -- Seq_N 13 Node 4: 1374807977.84643 -- Worker 1: 1.20045 -- Seq_N 19 Node 1: 1374807977.94636 -- Worker 2: 1.30037 -- Seq_N 2 Node 2: 1374807978.04638 -- Worker 2: 1.40040 -- Seq_N 8 Node 3: 1374807978.14641 -- Worker 2: 1.50042 -- Seq_N 14 Node 4: 1374807978.24644 -- Worker 2: 1.60045 -- Seq_N 20 Node 1: 1374807978.34628 -- Worker 1: 0.79996 -- Seq_N 3 Node 2: 1374807978.44631 -- Worker 1: 0.79996 -- Seq_N 9 Node 3: 1374807978.54634 -- Worker 1: 0.79996 -- Seq_N 15 Node 4: 1374807978.64636 -- Worker 1: 0.79997 -- Seq_N 21 Node 1: 1374807978.74628 -- Worker 2: 0.79996 -- Seq_N 4 Node 2: 1374807978.84632 -- Worker 2: 0.79997 -- Seq_N 10 Node 3: 1374807978.94634 -- Worker 2: 0.79996 -- Seq_N 16 Node 4: 1374807979.04636 -- Worker 2: 0.79996 -- Seq_N 22 Node 1: 1374807979.14628 -- Worker 1: 0.80001 -- Seq_N 5 Node 2: 1374807979.24631 -- Worker 1: 0.80000 -- Seq_N 11 Node 3: 1374807979.34634 -- Worker 1: 0.80001 -- Seq_N 17 Node 4: 1374807979.44636 -- Worker 1: 0.80000 -- Seq_N 23 Node 1: 1374807979.54628 -- Worker 2: 0.80000 -- Seq_N 6 Node 2: 1374807979.64631 -- Worker 2: 0.80000 -- Seq_N 12 Node 3: 1374807979.74633 -- Worker 2: 0.80000 -- Seq_N 18 Node 4: 1374807979.84636 -- Worker 2: 0.80000 -- Seq_N 24 The interval.pl example above is included with MCE. =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Relay.pm0000644000076400007640000003500312511673043013733 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Relay - Extends Many-Core Engine with relay capabilities. ## ############################################################################### package MCE::Relay; use strict; use warnings; ## no critic (Subroutines::ProhibitSubroutinePrototypes) our $VERSION = '1.608'; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use bytes; use constant { OUTPUT_W_RLA => 'W~RLA', ## Worker has relayed }; ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $LF = "\012"; Internals::SvREADONLY($LF, 1); my $_loaded; sub import { my $_class = shift; return if ($_loaded++); if (defined $MCE::VERSION) { _mce_m_init(); } else { $\ = undef; require Carp; Carp::croak( "MCE::Relay cannot be used directly. Please consult the MCE::Relay\n". "documentation for more information.\n\n" ); } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Output routines for the manager process. ## ############################################################################### { my ($_MCE, $_DAU_R_SOCK_REF, $_DAU_R_SOCK, $_rla_chunkid, $_rla_nextid); my %_output_function = ( OUTPUT_W_RLA.$LF => sub { ## Worker has relayed $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; my ($_chunk_id, $_next_id) = split(':', <$_DAU_R_SOCK>); if ($_chunk_id > $_rla_chunkid) { chomp $_next_id; $_rla_chunkid = $_chunk_id; $_rla_nextid = $_next_id; } return; }, ); sub _mce_m_loop_begin { ($_MCE, $_DAU_R_SOCK_REF) = @_; ## Write initial relay data. if (defined $_MCE->{init_relay}) { my $_ref = ref $_MCE->{init_relay}; MCE::_croak("MCE::Relay: (init_relay) is not valid") if ($_ref ne '' && $_ref ne 'HASH' && $_ref ne 'ARRAY'); my $_RLA_W_SOCK = $_MCE->{_rla_w_sock}->[0]; my $_init_relay; if (ref $_MCE->{init_relay} eq '') { $_init_relay = $_MCE->{init_relay} . '0'; } elsif (ref $_MCE->{init_relay} eq 'HASH') { $_init_relay = $_MCE->{freeze}($_MCE->{init_relay}) . '1'; } elsif (ref $_MCE->{init_relay} eq 'ARRAY') { $_init_relay = $_MCE->{freeze}($_MCE->{init_relay}) . '2'; } print {$_RLA_W_SOCK} length($_init_relay) . $LF . $_init_relay; delete $_MCE->{_rla_return} if (exists $_MCE->{_rla_return}); $_rla_chunkid = $_rla_nextid = 0; } return; } sub _mce_m_loop_end { ## Obtain final relay data. if (defined $_MCE->{init_relay}) { my $_RLA_R_SOCK = $_MCE->{_rla_r_sock}->[$_rla_nextid]; my ($_len, $_ret); chomp($_len = <$_RLA_R_SOCK>); read $_RLA_R_SOCK, $_ret, $_len; if (chop $_ret) { $_MCE->{_rla_return} = $_MCE->{thaw}($_ret); } else { $_MCE->{_rla_return} = $_ret; } } ## Clear variables. $_MCE = $_DAU_R_SOCK_REF = $_DAU_R_SOCK = $_rla_chunkid = $_rla_nextid = undef; return; } sub _mce_m_init { MCE::_attach_plugin( \%_output_function, \&_mce_m_loop_begin, \&_mce_m_loop_end ); return; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Relay methods. ## ############################################################################### ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use Scalar::Util qw( weaken ); use bytes; no warnings 'redefine'; sub relay_final { my $x = shift; my $self = ref($x) ? $x : $MCE::MCE; _croak('MCE::relay_final: method cannot be called by the worker process') if ($self->{_wid}); if (exists $self->{_rla_return}) { if (ref $self->{_rla_return} eq '') { return delete $self->{_rla_return}; } elsif (ref $self->{_rla_return} eq 'HASH') { return %{ delete $self->{_rla_return} }; } elsif (ref $self->{_rla_return} eq 'ARRAY') { return @{ delete $self->{_rla_return} }; } } return; } sub relay_recv { my $x = shift; my $self = ref($x) ? $x : $MCE::MCE; _croak('MCE::relay: (init_relay) is not specified') unless (defined $self->{init_relay}); _croak('MCE::relay: method cannot be called by the manager process') unless ($self->{_wid}); _croak('MCE::relay: method cannot be called by this sub task') if ($self->{_task_id} > 0); my $_chn = ($self->{_chunk_id} - 1) % $self->{max_workers}; my $_rdr = $self->{_rla_r_sock}->[$_chn]; my ($_len, $_ref); local $_; chomp($_len = <$_rdr>); read $_rdr, $_, $_len; $_ref = chop $_; if ($_ref == 0) { ## scalar value $self->{_rla_data} = $_; return unless defined wantarray; return $self->{_rla_data}; } elsif ($_ref == 1) { ## hash reference $self->{_rla_data} = $self->{thaw}($_); return unless defined wantarray; return %{ $self->{_rla_data} }; } elsif ($_ref == 2) { ## array reference $self->{_rla_data} = $self->{thaw}($_); return unless defined wantarray; return @{ $self->{_rla_data} }; } return; } sub relay (;&) { my ($self, $_code); if (ref $_[0] eq 'CODE') { ($self, $_code) = ($MCE::MCE, shift); } else { my $x = shift; $self = ref($x) ? $x : $MCE::MCE; $_code = shift; } _croak('MCE::relay: (init_relay) is not specified') unless (defined $self->{init_relay}); _croak('MCE::relay: method cannot be called by the manager process') unless ($self->{_wid}); _croak('MCE::relay: method cannot be called by this sub task') if ($self->{_task_id} > 0); if (ref $_code ne 'CODE') { _croak('MCE::relay: argument is not a code block') if (defined $_code); } else { weaken $_code; } my $_chn = ($self->{_chunk_id} - 1) % $self->{max_workers}; my $_nxt = $_chn + 1; $_nxt = 0 if ($_nxt == $self->{max_workers}); my $_rdr = $self->{_rla_r_sock}->[$_chn]; my $_wtr = $self->{_rla_w_sock}->[$_nxt]; $self->{_rla_return} = $self->{_chunk_id} .':'. $_nxt; if (exists $self->{_rla_data}) { local $_ = delete $self->{_rla_data}; $_code->() if (ref $_code eq 'CODE'); if (ref $_ eq '') { ## scalar value my $_tmp = $_ . '0'; print {$_wtr} length($_tmp) . $LF . $_tmp; } elsif (ref $_ eq 'HASH') { ## hash reference my $_tmp = $self->{freeze}($_) . '1'; print {$_wtr} length($_tmp) . $LF . $_tmp; } elsif (ref $_ eq 'ARRAY') { ## array reference my $_tmp = $self->{freeze}($_) . '2'; print {$_wtr} length($_tmp) . $LF . $_tmp; } } else { my ($_len, $_ref); local $_; chomp($_len = <$_rdr>); read $_rdr, $_, $_len; $_ref = chop $_; if ($_ref == 0) { ## scalar value my $_ret = $_; $_code->() if (ref $_code eq 'CODE'); my $_tmp = $_ . '0'; print {$_wtr} length($_tmp) . $LF . $_tmp; return unless defined wantarray; return $_ret; } elsif ($_ref == 1) { ## hash reference my %_ret = %{ $self->{thaw}($_) }; local $_ = { %_ret }; $_code->() if (ref $_code eq 'CODE'); my $_tmp = $self->{freeze}($_) . '1'; print {$_wtr} length($_tmp) . $LF . $_tmp; return unless defined wantarray; return %_ret; } elsif ($_ref == 2) { ## array reference my @_ret = @{ $self->{thaw}($_) }; local $_ = [ @_ret ]; $_code->() if (ref $_code eq 'CODE'); my $_tmp = $self->{freeze}($_) . '2'; print {$_wtr} length($_tmp) . $LF . $_tmp; return unless defined wantarray; return @_ret; } } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Relay - Extends Many-Core Engine with relay capabilities =head1 VERSION This document describes MCE::Relay version 1.608 =head1 SYNOPSIS use MCE::Flow; my $file = shift || \*STDIN; ## Line Count ####################################### mce_flow_f { use_slurpio => 1, init_relay => 0, }, sub { my ($mce, $slurp_ref, $chunk_id) = @_; my $line_count = ($$slurp_ref =~ tr/\n//); ## Receive and pass on updated information. my $lines_read = MCE::relay { $_ += $line_count }; }, $file; my $total_lines = MCE->relay_final; print {*STDERR} "$total_lines\n"; ## Orderly Action ################################### mce_flow_f { use_slurpio => 1, init_relay => 0, }, sub { my ($mce, $slurp_ref, $chunk_id) = @_; ## Exclusive access to STDOUT. Relays 0. MCE::relay { print $$slurp_ref }; }, $file; =head1 DESCRIPTION This module enables workers to receive and pass on information orderly with zero involvement by the manager process while running. The module is loaded automatically when init_relay is specified. All workers must participate when relaying data. Calling relay more than once is not recommended inside the block. Doing so will stall the application. Relaying is not met for passing big data. The last worker will likely stall if exceeding the buffer size for the socket. Not exceeding 8 KiB is safe across all platforms. =head1 API DOCUMENTATION =over 3 =item MCE->relay ( sub { code } ) =item MCE::relay { code } Relay is enabled by specifying the init_relay option which takes a hash or array reference, or a scalar value. Relaying is orderly and driven by chunk_id when processing data, otherwise task_wid. Omitting the code block (e.g. MCE::relay) relays forward. Below, relaying multiple values via a HASH reference. use MCE::Flow max_workers => 4; mce_flow { init_relay => { p => 0, e => 0 }, }, sub { my $wid = MCE->wid; ## do work my $pass = $wid % 3; my $errs = $wid % 2; ## relay my %last_rpt = MCE::relay { $_->{p} += $pass; $_->{e} += $errs }; MCE->print("$wid: passed $pass, errors $errs\n"); return; }; my %results = MCE->relay_final; print " passed $results{p}, errors $results{e} final\n\n"; -- Output 1: passed 1, errors 1 2: passed 2, errors 0 3: passed 0, errors 1 4: passed 1, errors 0 passed 4, errors 2 final Or multiple values via an ARRAY reference. use MCE::Flow max_workers => 4; mce_flow { init_relay => [ 0, 0 ], }, sub { my $wid = MCE->wid; ## do work my $pass = $wid % 3; my $errs = $wid % 2; ## relay my @last_rpt = MCE::relay { $_->[0] += $pass; $_->[1] += $errs }; MCE->print("$wid: passed $pass, errors $errs\n"); return; }; my ($pass, $errs) = MCE->relay_final; print " passed $pass, errors $errs final\n\n"; -- Output 1: passed 1, errors 1 2: passed 2, errors 0 3: passed 0, errors 1 4: passed 1, errors 0 passed 4, errors 2 final Or simply a scalar value. use MCE::Flow max_workers => 4; mce_flow { init_relay => 0, }, sub { my $wid = MCE->wid; ## do work my $bytes_read = 1000 + ((MCE->wid % 3) * 3); ## relay my $last_offset = MCE::relay { $_ += $bytes_read }; ## output MCE->print("$wid: $bytes_read\n"); return; }; my $total = MCE->relay_final; print " $total size\n\n"; -- Output 1: 1003 2: 1006 3: 1000 4: 1003 4012 size =item MCE->relay_final ( void ) Call this method to obtain the final relay values after running. See included example findnull.pl for another use case. use MCE max_workers => 4; my $mce = MCE->new( init_relay => [ 0, 100 ], ## initial values (two counters) user_func => sub { my ($mce) = @_; ## do work my ($acc1, $acc2) = (10, 20); ## relay to next worker MCE::relay { $_->[0] += $acc1; $_->[1] += $acc2 }; return; } )->run; my ($cnt1, $cnt2) = $mce->relay_final; print "$cnt1 : $cnt2\n"; -- Output 40 : 180 =item MCE->relay_recv ( void ) The relay_recv method allows one to perform an exclusive action prior to relaying. Below, the user_func is taken from the cat.pl example. Relaying is chunk_id driven (or task_wid when not processing input), thus orderly. user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; if ($n_flag) { ## Relays the total lines read. my $output = ''; my $line_count = ($$chunk_ref =~ tr/\n//); my $lines_read = MCE::relay { $_ += $line_count }; open my $fh, '<', $chunk_ref; $output .= sprintf "%6d\t%s", ++$lines_read, $_ while (<$fh>); close $fh; $output .= ":$chunk_id"; MCE->do('display_chunk', $output); } else { ## The following is another way to have ordered output. Workers ## write directly to STDOUT exclusively without any involvement ## from the manager process. The statements between relay_recv ## and relay run serially and most important orderly. ## STDERR/OUT flush automatically inside worker threads and ## processes. Disable buffering on file handles otherwise. MCE->relay_recv; ## my $val = MCE->relay_recv; ## relay simply forwards 0 below print $$chunk_ref; ## exclusive access to STDOUT ## important, flush immediately MCE->relay; } return; } =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Flow.pm0000644000076400007640000010155012511673021013563 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Flow - Parallel flow model for building creative applications. ## ############################################################################### package MCE::Flow; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use Scalar::Util qw( looks_like_number ); use MCE; our $VERSION = '1.608'; our @CARP_NOT = qw( MCE ); ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $MAX_WORKERS = 'auto'; my $CHUNK_SIZE = 'auto'; my ($_params, @_prev_c, @_prev_n, @_prev_w, @_user_tasks); my ($_MCE, $_loaded); my $_tag = 'MCE::Flow'; sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $MAX_WORKERS = shift and next if ( $_arg eq 'max_workers' ); $CHUNK_SIZE = shift and next if ( $_arg eq 'chunk_size' ); $MCE::FREEZE = $MCE::MCE->{freeze} = shift and next if ( $_arg eq 'freeze' ); $MCE::THAW = $MCE::MCE->{thaw} = shift and next if ( $_arg eq 'thaw' ); if ( $_arg eq 'sereal' ) { if (shift eq '1') { local $@; eval 'use Sereal qw(encode_sereal decode_sereal)'; unless ($@) { $MCE::FREEZE = $MCE::MCE->{freeze} = \&encode_sereal; $MCE::THAW = $MCE::MCE->{thaw} = \&decode_sereal; } } next; } if ( $_arg eq 'tmp_dir' ) { $MCE::TMP_DIR = $MCE::MCE->{tmp_dir} = shift; my $_e1 = 'is not a directory or does not exist'; my $_e2 = 'is not writeable'; _croak($_tag."::import: ($MCE::TMP_DIR) $_e1") unless -d $MCE::TMP_DIR; _croak($_tag."::import: ($MCE::TMP_DIR) $_e2") unless -w $MCE::TMP_DIR; next; } _croak($_tag."::import: ($_argument) is not a valid module argument"); } $MAX_WORKERS = MCE::Util::_parse_max_workers($MAX_WORKERS); _validate_number($MAX_WORKERS, 'MAX_WORKERS'); _validate_number($CHUNK_SIZE, 'CHUNK_SIZE') unless ($CHUNK_SIZE eq 'auto'); ## Import functions. no strict 'refs'; no warnings 'redefine'; my $_pkg = caller; *{ $_pkg.'::mce_flow_f' } = \&run_file; *{ $_pkg.'::mce_flow_s' } = \&run_seq; *{ $_pkg.'::mce_flow' } = \&run; return; } END { return if (defined $_MCE && $_MCE->wid); finish(); } ############################################################################### ## ---------------------------------------------------------------------------- ## Init and finish routines. ## ############################################################################### sub init (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Flow'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } finish(); $_params = (ref $_[0] eq 'HASH') ? shift : { @_ }; @_ = (); return; } sub finish () { if (defined $_MCE && $_MCE->{_spawned}) { MCE::_save_state; $_MCE->shutdown(); MCE::_restore_state; } @_user_tasks = (); @_prev_w = (); @_prev_n = (); @_prev_c = (); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel flow with MCE -- file. ## ############################################################################### sub run_file (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Flow'); my ($_file, $_pos); my $_start_pos = (ref $_[0] eq 'HASH') ? 2 : 1; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{sequence} if (exists $_params->{sequence}); } else { $_params = {}; } for my $_i ($_start_pos .. @_ - 1) { my $_r = ref $_[$_i]; if ($_r eq '' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_file = $_[$_i]; $_pos = $_i; last; } } if (defined $_file && ref $_file eq '' && $_file ne '') { _croak("$_tag: ($_file) does not exist") unless (-e $_file); _croak("$_tag: ($_file) is not readable") unless (-r $_file); _croak("$_tag: ($_file) is not a plain file") unless (-f $_file); $_params->{_file} = $_file; } elsif (ref $_file eq 'GLOB' || ref $_file eq 'SCALAR' || ref($_file) =~ /^IO::/) { $_params->{_file} = $_file; } else { _croak("$_tag: (file) is not specified or valid"); } if (defined $_pos) { pop @_ for ($_pos .. @_ - 1); } return run(@_); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel flow with MCE -- sequence. ## ############################################################################### sub run_seq (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Flow'); my ($_begin, $_end, $_pos); my $_start_pos = (ref $_[0] eq 'HASH') ? 2 : 1; if (defined $_params) { delete $_params->{sequence} if (exists $_params->{sequence}); delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{_file} if (exists $_params->{_file}); } else { $_params = {}; } for my $_i ($_start_pos .. @_ - 1) { my $_ref = ref $_[$_i]; if ($_ref eq '' || $_ref eq 'HASH' || $_ref eq 'ARRAY') { $_pos = $_i; if ($_ref eq '') { $_begin = $_[$_pos]; $_end = $_[$_pos + 1]; $_params->{sequence} = [ $_[$_pos], $_[$_pos + 1], $_[$_pos + 2], $_[$_pos + 3] ]; } elsif ($_ref eq 'HASH') { $_begin = $_[$_pos]->{begin}; $_end = $_[$_pos]->{end}; $_params->{sequence} = $_[$_pos]; } elsif ($_ref eq 'ARRAY') { $_begin = $_[$_pos]->[0]; $_end = $_[$_pos]->[1]; $_params->{sequence} = $_[$_pos]; } last; } } _croak("$_tag: (sequence) is not specified or valid") unless (exists $_params->{sequence}); _croak("$_tag: (begin) is not specified for sequence") unless (defined $_begin); _croak("$_tag: (end) is not specified for sequence") unless (defined $_end); $_params->{sequence_run} = 1; if (defined $_pos) { pop @_ for ($_pos .. @_ - 1); } return run(@_); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel flow with MCE. ## ############################################################################### sub run (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Flow'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } if (ref $_[0] eq 'HASH') { $_params = {} unless defined $_params; for my $_p (keys %{ $_[0] }) { $_params->{$_p} = $_[0]->{$_p}; } shift; } ## ------------------------------------------------------------------------- my (@_code, @_name, @_wrks); my $_init_mce = 0; my $_pos = 0; while (ref $_[0] eq 'CODE') { push @_code, $_[0]; push @_name, (defined $_params && ref $_params->{task_name} eq 'ARRAY') ? $_params->{task_name}->[$_pos] : undef; push @_wrks, (defined $_params && ref $_params->{max_workers} eq 'ARRAY') ? $_params->{max_workers}->[$_pos] : undef; $_init_mce = 1 if (!defined $_prev_c[$_pos] || $_prev_c[$_pos] != $_code[$_pos]); { no warnings; $_init_mce = 1 if ($_prev_n[$_pos] ne $_name[$_pos]); $_init_mce = 1 if ($_prev_w[$_pos] ne $_wrks[$_pos]); } $_prev_c[$_pos] = $_code[$_pos]; $_prev_n[$_pos] = $_name[$_pos]; $_prev_w[$_pos] = $_wrks[$_pos]; shift; $_pos++; } if (defined $_prev_c[$_pos]) { pop @_prev_c for ($_pos .. @_prev_c - 1); pop @_prev_n for ($_pos .. @_prev_n - 1); pop @_prev_w for ($_pos .. @_prev_w - 1); $_init_mce = 1; } return unless (scalar @_code); ## ------------------------------------------------------------------------- my $_input_data; my $_max_workers = $MAX_WORKERS; my $_r = ref $_[0]; if ($_r eq 'ARRAY' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_input_data = shift; } if (defined $_params) { my $_p = $_params; $_max_workers = MCE::Util::_parse_max_workers($_p->{max_workers}) if (exists $_p->{max_workers} && ref $_p->{max_workers} ne 'ARRAY'); delete $_p->{sequence} if (defined $_input_data || scalar @_); delete $_p->{user_func} if (exists $_p->{user_func}); delete $_p->{user_tasks} if (exists $_p->{user_tasks}); } if (@_code > 1 && $_max_workers > 1) { $_max_workers = int($_max_workers / @_code + 0.5) + 1; } my $_chunk_size = MCE::Util::_parse_chunk_size( $CHUNK_SIZE, $_max_workers, $_params, $_input_data, scalar @_ ); if (defined $_params) { if (exists $_params->{_file}) { $_input_data = delete $_params->{_file}; } else { $_input_data = $_params->{input_data} if exists $_params->{input_data}; } } MCE::_save_state; ## ------------------------------------------------------------------------- if ($_init_mce) { $_MCE->shutdown() if (defined $_MCE); _gen_user_tasks(\@_code, \@_name, \@_wrks); my %_options = ( max_workers => $_max_workers, task_name => $_tag, user_tasks => \@_user_tasks, ); if (defined $_params) { local $_; my $_p = $_params; for (keys %{ $_p }) { next if ($_ eq 'sequence_run'); next if ($_ eq 'max_workers' && ref $_p->{max_workers} eq 'ARRAY'); next if ($_ eq 'task_name' && ref $_p->{task_name} eq 'ARRAY'); next if ($_ eq 'input_data'); next if ($_ eq 'chunk_size'); _croak("MCE::Flow: ($_) is not a valid constructor argument") unless (exists $MCE::_valid_fields_new{$_}); $_options{$_} = $_p->{$_}; } } $_MCE = MCE->new(%_options); } else { ## Workers may persist after running. Thus, updating the MCE instance. ## These options do not require respawning. if (defined $_params) { for my $_p (qw( RS interval stderr_file stdout_file user_error user_output job_delay submit_delay on_post_exit on_post_run user_args flush_file flush_stderr flush_stdout gather )) { $_MCE->{$_p} = $_params->{$_p} if (exists $_params->{$_p}); } } } ## ------------------------------------------------------------------------- my @_a; my $_wa = wantarray; $_MCE->{gather} = \@_a if (defined $_wa); if (defined $_input_data) { @_ = (); $_MCE->process({ chunk_size => $_chunk_size }, $_input_data); delete $_MCE->{input_data}; } elsif (scalar @_) { $_MCE->process({ chunk_size => $_chunk_size }, \@_); delete $_MCE->{input_data}; } else { if (defined $_params && exists $_params->{sequence}) { $_MCE->run({ chunk_size => $_chunk_size, sequence => $_params->{sequence} }, 0); if (exists $_params->{sequence_run}) { delete $_params->{sequence_run}; delete $_params->{sequence}; } delete $_MCE->{sequence}; } else { $_MCE->run({ chunk_size => $_chunk_size }, 0); } } delete $_MCE->{gather} if (defined $_wa); MCE::_restore_state; if (exists $_MCE->{_rla_return}) { $MCE::MCE->{_rla_return} = delete $_MCE->{_rla_return}; } finish() if ($^S); ## shutdown if in eval state return ((defined $_wa) ? @_a : ()); } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _croak { goto &MCE::_croak; } sub _gen_user_tasks { my ($_code_ref, $_name_ref, $_wrks_ref) = @_; @_user_tasks = (); for (my $_i = 0; $_i < @{ $_code_ref }; $_i++) { push @_user_tasks, { task_name => $_name_ref->[$_i], max_workers => $_wrks_ref->[$_i], user_func => $_code_ref->[$_i] } } return; } sub _validate_number { my ($_n, $_key) = @_; _croak("$_tag: ($_key) is not valid") if (!defined $_n); $_n =~ s/K\z//i; $_n =~ s/M\z//i; if (!looks_like_number($_n) || int($_n) != $_n || $_n < 1) { _croak("$_tag: ($_key) is not valid"); } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Flow - Parallel flow model for building creative applications =head1 VERSION This document describes MCE::Flow version 1.608 =head1 DESCRIPTION MCE::Flow is great for writing custom apps to maximize on all available cores. This module was created to help one harness user_tasks within MCE. It is trivial to parallelize with mce_stream shown below. ## Native map function my @a = map { $_ * 4 } map { $_ * 3 } map { $_ * 2 } 1..10000; ## Same as with MCE::Stream (processing from right to left) @a = mce_stream sub { $_ * 4 }, sub { $_ * 3 }, sub { $_ * 2 }, 1..10000; ## Pass an array reference to have writes occur simultaneously mce_stream \@a, sub { $_ * 4 }, sub { $_ * 3 }, sub { $_ * 2 }, 1..10000; However, let's have MCE::Flow compute the same in parallel. MCE::Queue will be used for data flow among the sub-tasks. use MCE::Flow; use MCE::Queue; This calls for preserving output order. sub preserve_order { my %tmp; my $order_id = 1; my $gather_ref = $_[0]; @{ $gather_ref } = (); ## clear the array (optional) return sub { my ($data_ref, $chunk_id) = @_; $tmp{$chunk_id} = $data_ref; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ delete $tmp{$order_id++} }; } return; }; } Two queues are needed for data flow between the 3 sub-tasks. Notice task_end and how the value from $task_name is used for determining which task has ended. my $b = MCE::Queue->new; my $c = MCE::Queue->new; sub task_end { my ($mce, $task_id, $task_name) = @_; if (defined $mce->{user_tasks}->[$task_id + 1]) { my $n_workers = $mce->{user_tasks}->[$task_id + 1]->{max_workers}; if ($task_name eq 'a') { $b->enqueue((undef) x $n_workers); } elsif ($task_name eq 'b') { $c->enqueue((undef) x $n_workers); } } return; } Next are the 3 sub-tasks. The first one reads input and begins the flow. The 2nd task dequeues, performs the calculation, and enqueues into the next. Finally, the last task calls the gather method. Although serialization is done for you automatically, it is done here to save from double serialization. This is the fastest approach for passing data between sub-tasks. Thus, the least overhead. sub task_a { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 2 } @{ $chunk_ref }; $b->enqueue(MCE->freeze([ \@ans, $chunk_id ])); return; } sub task_b { my ($mce) = @_; while (1) { my @ans; my $chunk = $b->dequeue; last unless defined $chunk; $chunk = MCE->thaw($chunk); push @ans, map { $_ * 3 } @{ $chunk->[0] }; $c->enqueue(MCE->freeze([ \@ans, $chunk->[1] ])); } return; } sub task_c { my ($mce) = @_; while (1) { my @ans; my $chunk = $c->dequeue; last unless defined $chunk; $chunk = MCE->thaw($chunk); push @ans, map { $_ * 4 } @{ $chunk->[0] }; MCE->gather(\@ans, $chunk->[1]); } return; } In summary, MCE::Flow builds out a MCE instance behind the scene and starts running. Both task_name and max_workers (not shown) can take an anonymous array for specifying the values uniquely for each sub-task. my @a; mce_flow { task_name => [ 'a', 'b', 'c' ], task_end => \&task_end, gather => preserve_order(\@a) }, \&task_a, \&task_b, \&task_c, 1..10000; print "@a\n"; If speed is not a concern and wanting to rid of all the MCE->freeze and MCE->thaw statements, simply enqueue and dequeue 2 items at a time. Or better yet, see L introduced in MCE 1.506. First, task_end must be updated. The number of undef(s) must match the number of workers times the dequeue count. Otherwise, the script will stall. sub task_end { ... if ($task_name eq 'a') { # $b->enqueue((undef) x $n_workers); $b->enqueue((undef) x ($n_workers * 2)); } elsif ($task_name eq 'b') { # $c->enqueue((undef) x $n_workers); $c->enqueue((undef) x ($n_workers * 2)); } ... } Next, the 3 sub-tasks enqueuing and dequeuing 2 elements at a time. sub task_a { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 2 } @{ $chunk_ref }; $b->enqueue(\@ans, $chunk_id); return; } sub task_b { my ($mce) = @_; while (1) { my @ans; my ($chunk_ref, $chunk_id) = $b->dequeue(2); last unless defined $chunk_ref; push @ans, map { $_ * 3 } @{ $chunk_ref }; $c->enqueue(\@ans, $chunk_id); } return; } sub task_c { my ($mce) = @_; while (1) { my @ans; my ($chunk_ref, $chunk_id) = $c->dequeue(2); last unless defined $chunk_ref; push @ans, map { $_ * 4 } @{ $chunk_ref }; MCE->gather(\@ans, $chunk_id); } return; } Finally, run as usual. my @a; mce_flow { task_name => [ 'a', 'b', 'c' ], task_end => \&task_end, gather => preserve_order(\@a) }, \&task_a, \&task_b, \&task_c, 1..10000; print "@a\n"; =head1 SYNOPSIS when CHUNK_SIZE EQUALS 1 Although L may be preferred for running using a single code block, the text below also applies to this module, particularly for the first block. All models in MCE default to 'auto' for chunk_size. The arguments for the block are the same as writing a user_func block using the Core API. Beginning with MCE 1.5, the next input item is placed into the input scalar variable $_ when chunk_size equals 1. Otherwise, $_ points to $chunk_ref containing many items. Basically, line 2 below may be omitted from your code when using $_. One can call MCE->chunk_id to obtain the current chunk id. line 1: user_func => sub { line 2: my ($mce, $chunk_ref, $chunk_id) = @_; line 3: line 4: $_ points to $chunk_ref->[0] line 5: in MCE 1.5 when chunk_size == 1 line 6: line 7: $_ points to $chunk_ref line 8: in MCE 1.5 when chunk_size > 1 line 9: } Follow this synopsis when chunk_size equals one. Looping is not required from inside the first block. Hence, the block is called once per each item. ## Exports mce_flow, mce_flow_f, and mce_flow_s use MCE::Flow; MCE::Flow::init { chunk_size => 1 }; ## Array or array_ref mce_flow sub { do_work($_) }, 1..10000; mce_flow sub { do_work($_) }, [ 1..10000 ]; ## File_path, glob_ref, or scalar_ref mce_flow_f sub { chomp; do_work($_) }, "/path/to/file"; mce_flow_f sub { chomp; do_work($_) }, $file_handle; mce_flow_f sub { chomp; do_work($_) }, \$scalar; ## Sequence of numbers (begin, end [, step, format]) mce_flow_s sub { do_work($_) }, 1, 10000, 5; mce_flow_s sub { do_work($_) }, [ 1, 10000, 5 ]; mce_flow_s sub { do_work($_) }, { begin => 1, end => 10000, step => 5, format => undef }; =head1 SYNOPSIS when CHUNK_SIZE is GREATER THAN 1 Follow this synopsis when chunk_size equals 'auto' or greater than 1. This means having to loop through the chunk from inside the first block. use MCE::Flow; MCE::Flow::init { ## Chunk_size defaults to 'auto' when chunk_size => 'auto' ## not specified. Therefore, the init }; ## function may be omitted. ## Syntax is shown for mce_flow for demonstration purposes. ## Looping inside the block is the same for mce_flow_f and ## mce_flow_s. mce_flow sub { do_work($_) for (@{ $_ }) }, 1..10000; ## Same as above, resembles code using the Core API. mce_flow sub { my ($mce, $chunk_ref, $chunk_id) = @_; for (@{ $chunk_ref }) { do_work($_); } }, 1..10000; Chunking reduces the number of IPC calls behind the scene. Think in terms of chunks whenever processing a large amount of data. For relatively small data, choosing 1 for chunk_size is fine. =head1 OVERRIDING DEFAULTS The following list 5 options which may be overridden when loading the module. use Sereal qw( encode_sereal decode_sereal ); use CBOR::XS qw( encode_cbor decode_cbor ); use JSON::XS qw( encode_json decode_json ); use MCE::Flow max_workers => 8, ## Default 'auto' chunk_size => 500, ## Default 'auto' tmp_dir => "/path/to/app/tmp", ## $MCE::Signal::tmp_dir freeze => \&encode_sereal, ## \&Storable::freeze thaw => \&decode_sereal ## \&Storable::thaw ; There is a simpler way to enable Sereal with MCE 1.5. The following will attempt to use Sereal if available, otherwise defaults to Storable for serialization. use MCE::Flow Sereal => 1; MCE::Flow::init { chunk_size => 1 }; ## Serialization is by the Sereal module if available. my %answer = mce_flow sub { MCE->gather( $_, sqrt $_ ) }, 1..10000; =head1 CUSTOMIZING MCE =over 3 =item MCE::Flow->init ( options ) =item MCE::Flow::init { options } The init function accepts a hash of MCE options. Unlike with MCE::Stream, both gather and bounds_only options may be specified when calling init (not shown below). use MCE::Flow; MCE::Flow::init { chunk_size => 1, max_workers => 4, user_begin => sub { print "## ", MCE->wid, " started\n"; }, user_end => sub { print "## ", MCE->wid, " completed\n"; } }; my %a = mce_flow sub { MCE->gather($_, $_ * $_) }, 1..100; print "\n", "@a{1..100}", "\n"; -- Output ## 3 started ## 2 started ## 4 started ## 1 started ## 2 completed ## 4 completed ## 3 completed ## 1 completed 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 1600 1681 1764 1849 1936 2025 2116 2209 2304 2401 2500 2601 2704 2809 2916 3025 3136 3249 3364 3481 3600 3721 3844 3969 4096 4225 4356 4489 4624 4761 4900 5041 5184 5329 5476 5625 5776 5929 6084 6241 6400 6561 6724 6889 7056 7225 7396 7569 7744 7921 8100 8281 8464 8649 8836 9025 9216 9409 9604 9801 10000 =back Like with MCE::Flow::init above, MCE options may be specified using an anonymous hash for the first argument. Notice how both max_workers and task_name can take an anonymous array for setting values uniquely for each code block. Unlike MCE::Stream which processes from right-to-left, MCE::Flow begins with the first code block, thus processing from left-to-right. use MCE::Flow; my @a = mce_flow { task_name => [ 'a', 'b', 'c' ], max_workers => [ 3, 4, 2, ], user_end => sub { my ($mce, $task_id, $task_name) = @_; MCE->print("$task_id - $task_name completed\n"); }, task_end => sub { my ($mce, $task_id, $task_name) = @_; MCE->print("$task_id - $task_name ended\n"); } }, sub { sleep 1; }, ## 3 workers, named a sub { sleep 2; }, ## 4 workers, named b sub { sleep 3; }; ## 2 workers, named c -- Output 0 - a completed 0 - a completed 0 - a completed 0 - a ended 1 - b completed 1 - b completed 1 - b completed 1 - b completed 1 - b ended 2 - c completed 2 - c completed 2 - c ended =head1 API DOCUMENTATION Although input data is optional for MCE::Flow, the following assumes chunk_size equals 1 in order to demonstrate all the possibilities of passing input data into the code block. =over 3 =item MCE::Flow->run ( { input_data => iterator }, sub { code } ) =item mce_flow { input_data => iterator }, sub { code } An iterator reference can by specified for input_data. The only other way is to specify input_data via MCE::Flow::init. This prevents MCE::Flow from configuring the iterator reference as another user task which will not work. Iterators are described under "SYNTAX for INPUT_DATA" at L. MCE::Flow::init { input_data => iterator }; mce_flow sub { $_ }; =item MCE::Flow->run ( sub { code }, list ) =item mce_flow sub { code }, list Input data can be defined using a list. mce_flow sub { $_ }, 1..1000; mce_flow sub { $_ }, [ 1..1000 ]; =item MCE::Flow->run_file ( sub { code }, file ) =item mce_flow_f sub { code }, file The fastest of these is the /path/to/file. Workers communicate the next offset position among themselves with zero interaction by the manager process. mce_flow_f sub { $_ }, "/path/to/file"; mce_flow_f sub { $_ }, $file_handle; mce_flow_f sub { $_ }, \$scalar; =item MCE::Flow->run_seq ( sub { code }, $beg, $end [, $step, $fmt ] ) =item mce_flow_s sub { code }, $beg, $end [, $step, $fmt ] Sequence can be defined as a list, an array reference, or a hash reference. The functions require both begin and end values to run. Step and format are optional. The format is passed to sprintf (% may be omitted below). my ($beg, $end, $step, $fmt) = (10, 20, 0.1, "%4.1f"); mce_flow_s sub { $_ }, $beg, $end, $step, $fmt; mce_flow_s sub { $_ }, [ $beg, $end, $step, $fmt ]; mce_flow_s sub { $_ }, { begin => $beg, end => $end, step => $step, format => $fmt }; =back The sequence engine can compute 'begin' and 'end' items only, for the chunk, and not the items in between (hence boundaries only). This option applies to sequence only and has no effect when chunk_size equals 1. The time to run is 0.006s below. This becomes 0.827s without the bounds_only option due to computing all items in between, thus creating a very large array. Basically, specify bounds_only => 1 when boundaries is all you need for looping inside the block; e.g. Monte Carlo simulations. Time was measured using 1 worker to emphasize the difference. use MCE::Flow; MCE::Flow::init { max_workers => 1, chunk_size => 1_250_000, bounds_only => 1 }; ## For sequence, the input scalar $_ points to $chunk_ref ## when chunk_size > 1, otherwise $chunk_ref->[0]. ## ## mce_flow_s sub { ## my $begin = $_->[0]; my $end = $_->[-1]; ## ## for ($begin .. $end) { ## ... ## } ## ## }, 1, 10_000_000; mce_flow_s sub { my ($mce, $chunk_ref, $chunk_id) = @_; ## $chunk_ref contains 2 items, not 1_250_000 my $begin = $chunk_ref->[ 0]; my $end = $chunk_ref->[-1]; ## or $chunk_ref->[1] MCE->printf("%7d .. %8d\n", $begin, $end); }, 1, 10_000_000; -- Output 1 .. 1250000 1250001 .. 2500000 2500001 .. 3750000 3750001 .. 5000000 5000001 .. 6250000 6250001 .. 7500000 7500001 .. 8750000 8750001 .. 10000000 =head1 GATHERING DATA Unlike MCE::Map where gather and output order are done for you automatically, the gather method is used to have results sent back to the manager process. use MCE::Flow chunk_size => 1; ## Output order is not guaranteed. my @a = mce_flow sub { MCE->gather($_ * 2) }, 1..100; print "@a\n\n"; ## Outputs to a hash instead (key, value). my %h1 = mce_flow sub { MCE->gather($_, $_ * 2) }, 1..100; print "@h1{1..100}\n\n"; ## This does the same thing due to chunk_id starting at one. my %h2 = mce_flow sub { MCE->gather(MCE->chunk_id, $_ * 2) }, 1..100; print "@h2{1..100}\n\n"; The gather method can be called multiple times within the block unlike return which would leave the block. Therefore, think of gather as yielding results immediately to the manager process without actually leaving the block. use MCE::Flow chunk_size => 1, max_workers => 3; my @hosts = qw( hosta hostb hostc hostd hoste ); my %h3 = mce_flow sub { my ($output, $error, $status); my $host = $_; ## Do something with $host; $output = "Worker ". MCE->wid .": Hello from $host"; if (MCE->chunk_id % 3 == 0) { ## Simulating an error condition local $? = 1; $status = $?; $error = "Error from $host" } else { $status = 0; } ## Ensure unique keys (key, value) when gathering to ## a hash. MCE->gather("$host.out", $output); MCE->gather("$host.err", $error) if (defined $error); MCE->gather("$host.sta", $status); }, @hosts; foreach my $host (@hosts) { print $h3{"$host.out"}, "\n"; print $h3{"$host.err"}, "\n" if (exists $h3{"$host.err"}); print "Exit status: ", $h3{"$host.sta"}, "\n\n"; } -- Output Worker 3: Hello from hosta Exit status: 0 Worker 2: Hello from hostb Exit status: 0 Worker 1: Hello from hostc Error from hostc Exit status: 1 Worker 3: Hello from hostd Exit status: 0 Worker 2: Hello from hoste Exit status: 0 The following uses an anonymous array containing 3 elements when gathering data. Serialization is automatic behind the scene. my %h3 = mce_flow sub { ... MCE->gather($host, [$output, $error, $status]); }, @hosts; foreach my $host (@hosts) { print $h3{$host}->[0], "\n"; print $h3{$host}->[1], "\n" if (defined $h3{$host}->[1]); print "Exit status: ", $h3{$host}->[2], "\n\n"; } Although MCE::Map comes to mind, one may want additional control when gathering data such as retaining output order. use MCE::Flow; sub preserve_order { my %tmp; my $order_id = 1; my $gather_ref = $_[0]; return sub { $tmp{ (shift) } = \@_; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ delete $tmp{$order_id++} }; } return; }; } ## Workers persist for the most part after running. Though, not always ## the case and depends on Perl. Pass a reference to a subroutine if ## workers must persist; e.g. mce_flow { ... }, \&foo, 1..100000. MCE::Flow::init { chunk_size => 'auto', max_workers => 'auto' }; for (1..2) { my @m2; mce_flow { gather => preserve_order(\@m2) }, sub { my @a; my ($mce, $chunk_ref, $chunk_id) = @_; ## Compute the entire chunk data at once. push @a, map { $_ * 2 } @{ $chunk_ref }; ## Afterwards, invoke the gather feature, which ## will direct the data to the callback function. MCE->gather(MCE->chunk_id, @a); }, 1..100000; print scalar @m2, "\n"; } MCE::Flow::finish; All 6 models support 'auto' for chunk_size unlike the Core API. Think of the models as the basis for providing JIT for MCE. They create the instance, tune max_workers, and tune chunk_size automatically regardless of the hardware. The following does the same thing using the Core API. Workers persist after running. use MCE; sub preserve_order { ... } my $mce = MCE->new( max_workers => 'auto', chunk_size => 8000, user_func => sub { my @a; my ($mce, $chunk_ref, $chunk_id) = @_; ## Compute the entire chunk data at once. push @a, map { $_ * 2 } @{ $chunk_ref }; ## Afterwards, invoke the gather feature, which ## will direct the data to the callback function. MCE->gather(MCE->chunk_id, @a); } ); for (1..2) { my @m2; $mce->process({ gather => preserve_order(\@m2) }, [1..100000]); print scalar @m2, "\n"; } $mce->shutdown; =head1 MANUAL SHUTDOWN =over 3 =item MCE::Flow->finish =item MCE::Flow::finish Workers remain persistent as much as possible after running. Shutdown occurs automatically when the script terminates. Call finish when workers are no longer needed. use MCE::Flow; MCE::Flow::init { chunk_size => 20, max_workers => 'auto' }; mce_flow sub { ... }, 1..100; MCE::Flow::finish; =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Subs.pm0000644000076400007640000002445212511673057013606 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Subs - Exports functions mapped directly to MCE methods. ## ############################################################################### package MCE::Subs; use strict; use warnings; ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use MCE; use MCE::Relay; our $VERSION = '1.608'; ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $_loaded; sub import { my $_class = shift; return if ($_loaded++); my $_g_flg = 0; my $_m_flg = 0; my $_w_flg = 0; my $_flag = sub { 1 }; my $_package = caller; ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $_g_flg = $_flag->() and next if ( $_arg eq ':getter' ); $_m_flg = $_flag->() and next if ( $_arg eq ':manager' ); $_w_flg = $_flag->() and next if ( $_arg eq ':worker' ); _croak("MCE::Subs::import: ($_argument) is not a valid module argument"); } $_m_flg = $_w_flg = 1 if ($_m_flg + $_w_flg == 0); _export_subs($_package, $_g_flg, $_m_flg, $_w_flg); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Define functions. ## ############################################################################### ## Callable by the manager process only. sub mce_restart_worker (@) { return $MCE::MCE->restart_worker(@_); } sub mce_forchunk (@) { return $MCE::MCE->forchunk(@_); } sub mce_foreach (@) { return $MCE::MCE->foreach(@_); } sub mce_forseq (@) { return $MCE::MCE->forseq(@_); } sub mce_process (@) { return $MCE::MCE->process(@_); } sub mce_relay_final ( ) { return $MCE::MCE->relay_final(); } sub mce_run (@) { return $MCE::MCE->run(@_); } sub mce_send (@) { return $MCE::MCE->send(@_); } sub mce_shutdown ( ) { return $MCE::MCE->shutdown(); } sub mce_spawn ( ) { return $MCE::MCE->spawn(); } sub mce_status ( ) { return $MCE::MCE->status(); } ## Callable by the worker process only. sub mce_do (@) { return $MCE::MCE->do(@_); } sub mce_exit (@) { return $MCE::MCE->exit(@_); } sub mce_gather (@) { return $MCE::MCE->gather(@_); } sub mce_last ( ) { return $MCE::MCE->last(); } sub mce_next ( ) { return $MCE::MCE->next(); } sub mce_relay (;&) { return $MCE::MCE->relay(@_); } sub mce_relay_recv ( ) { return $MCE::MCE->relay_recv(); } sub mce_sendto (;*@) { return $MCE::MCE->sendto(@_); } sub mce_sync ( ) { return $MCE::MCE->sync(); } sub mce_yield ( ) { return $MCE::MCE->yield(); } ## Callable by both the manager and worker processes. sub mce_abort ( ) { return $MCE::MCE->abort(); } sub mce_freeze (@) { return $MCE::MCE->{freeze}(@_); } sub mce_print (;*@) { return $MCE::MCE->print(@_); } sub mce_printf (;*@) { return $MCE::MCE->printf(@_); } sub mce_say (;*@) { return $MCE::MCE->say(@_); } sub mce_thaw (@) { return $MCE::MCE->{thaw}(@_); } ## Callable by both the manager and worker processes. sub mce_chunk_id ( ) { return $MCE::MCE->chunk_id(); } sub mce_chunk_size ( ) { return $MCE::MCE->chunk_size(); } sub mce_max_workers ( ) { return $MCE::MCE->max_workers(); } sub mce_pid ( ) { return $MCE::MCE->pid(); } sub mce_sess_dir ( ) { return $MCE::MCE->sess_dir(); } sub mce_task_id ( ) { return $MCE::MCE->task_id(); } sub mce_task_name ( ) { return $MCE::MCE->task_name(); } sub mce_task_wid ( ) { return $MCE::MCE->task_wid(); } sub mce_tmp_dir ( ) { return $MCE::MCE->tmp_dir(); } sub mce_user_args ( ) { return $MCE::MCE->user_args(); } sub mce_wid ( ) { return $MCE::MCE->wid(); } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _croak { goto &MCE::_croak; } sub _export_subs { my ($_package, $_g_flg, $_m_flg, $_w_flg) = @_; no strict 'refs'; no warnings 'redefine'; ## Callable by the manager process only. if ($_m_flg) { *{ $_package . '::mce_restart_worker' } = \&mce_restart_worker; *{ $_package . '::mce_forchunk' } = \&mce_forchunk; *{ $_package . '::mce_foreach' } = \&mce_foreach; *{ $_package . '::mce_forseq' } = \&mce_forseq; *{ $_package . '::mce_process' } = \&mce_process; *{ $_package . '::mce_relay_final' } = \&mce_relay_final; *{ $_package . '::mce_run' } = \&mce_run; *{ $_package . '::mce_send' } = \&mce_send; *{ $_package . '::mce_shutdown' } = \&mce_shutdown; *{ $_package . '::mce_spawn' } = \&mce_spawn; *{ $_package . '::mce_status' } = \&mce_status; } ## Callable by the worker process only. if ($_w_flg) { *{ $_package . '::mce_do' } = \&mce_do; *{ $_package . '::mce_exit' } = \&mce_exit; *{ $_package . '::mce_gather' } = \&mce_gather; *{ $_package . '::mce_last' } = \&mce_last; *{ $_package . '::mce_next' } = \&mce_next; *{ $_package . '::mce_relay' } = \&mce_relay; *{ $_package . '::mce_relay_recv' } = \&mce_relay_recv; *{ $_package . '::mce_sendto' } = \&mce_sendto; *{ $_package . '::mce_sync' } = \&mce_sync; *{ $_package . '::mce_yield' } = \&mce_yield; } ## Callable by both the manager and worker processes. if ($_m_flg || $_w_flg) { *{ $_package . '::mce_abort' } = \&mce_abort; *{ $_package . '::mce_freeze' } = \&mce_freeze; *{ $_package . '::mce_print' } = \&mce_print; *{ $_package . '::mce_printf' } = \&mce_printf; *{ $_package . '::mce_say' } = \&mce_say; *{ $_package . '::mce_thaw' } = \&mce_thaw; } if ($_g_flg) { *{ $_package . '::mce_chunk_id' } = \&mce_chunk_id; *{ $_package . '::mce_chunk_size' } = \&mce_chunk_size; *{ $_package . '::mce_max_workers' } = \&mce_max_workers; *{ $_package . '::mce_pid' } = \&mce_pid; *{ $_package . '::mce_sess_dir' } = \&mce_sess_dir; *{ $_package . '::mce_task_id' } = \&mce_task_id; *{ $_package . '::mce_task_name' } = \&mce_task_name; *{ $_package . '::mce_task_wid' } = \&mce_task_wid; *{ $_package . '::mce_tmp_dir' } = \&mce_tmp_dir; *{ $_package . '::mce_user_args' } = \&mce_user_args; *{ $_package . '::mce_wid' } = \&mce_wid; } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Subs - Exports functions mapped directly to MCE methods =head1 VERSION This document describes MCE::Subs version 1.608 =head1 SYNOPSIS use MCE::Subs; ## Exports manager and worker functions only ## Getter functions are not exported by default use MCE::Subs qw( :getter ); ## All, including getter functions use MCE::Subs qw( :manager ); ## Exports manager functions only use MCE::Subs qw( :worker ); ## Exports worker functions only use MCE::Subs qw( :getter :worker ); ## Excludes manager functions =head1 DESCRIPTION This module exports functions mapped to MCE methods. All exported functions are prototyped, therefore allowing one to call them without using parentheses. use MCE::Subs qw( :worker ); sub user_func { my $wid = MCE->wid; mce_say "A: $wid"; mce_sync; mce_say "B: $wid"; mce_sync; mce_say "C: $wid"; mce_sync; return; } MCE->new( max_workers => 24, user_func => \&user_func ); mce_run 0 for (1..100); ## 0 means do not shutdown after running For the next example, we only want the worker functions to be exported due to using MCE::Map, which takes care of creating a MCE instance and running. use MCE::Map; use MCE::Subs qw( :worker ); ## The following serializes output to STDOUT and gathers $_ to @a. ## mce_say displays $_ when called without arguments. my @a = mce_map { mce_say; $_ } 1 .. 100; print scalar @a, "\n"; Unlike the native Perl functions, printf, print, and say methods require the comma after the glob reference or file handle. MCE->printf(\*STDERR, "%s\n", $error_msg); MCE->print(\*STDERR, $error_msg, "\n"); MCE->say(\*STDERR, $error_msg); MCE->say($fh, $error_msg); mce_printf \*STDERR, "%s\n", $error_msg; mce_print \*STDERR, $error_msg, "\n"; mce_say \*STDERR, $error_msg; mce_say $fh, $error_msg; =head1 FUNCTIONS for the MANAGER PROCESS via ( :manager ) MCE methods are described in L. =over 3 =item mce_abort =item mce_forchunk =item mce_foreach =item mce_forseq =item mce_freeze =item mce_process =item mce_relay_final =item mce_restart_worker =item mce_run =item mce_print =item mce_printf =item mce_say =item mce_send =item mce_shutdown =item mce_spawn =item mce_status =item mce_thaw =back =head1 FUNCTIONS for MCE WORKERS via ( :worker ) MCE methods are described in L. =over 3 =item mce_abort =item mce_do =item mce_exit =item mce_freeze =item mce_gather =item mce_last =item mce_next =item mce_print =item mce_printf =item mce_relay =item mce_relay_recv =item mce_say =item mce_sendto =item mce_sync =item mce_thaw =item mce_yield =back =head1 GETTERS for MCE ATTRIBUTES via ( :getter ) MCE methods are described in L. =over 3 =item mce_chunk_id =item mce_chunk_size =item mce_max_workers =item mce_pid =item mce_sess_dir =item mce_task_id =item mce_task_name =item mce_task_wid =item mce_tmp_dir =item mce_user_args =item mce_wid =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Grep.pm0000644000076400007640000004712312511673024013561 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Grep - Parallel grep model similar to the native grep function. ## ############################################################################### package MCE::Grep; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use Scalar::Util qw( looks_like_number ); use MCE; our $VERSION = '1.608'; our @CARP_NOT = qw( MCE ); ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $MAX_WORKERS = 'auto'; my $CHUNK_SIZE = 'auto'; my ($_MCE, $_loaded); my ($_params, $_prev_c); my $_tag = 'MCE::Grep'; sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $MAX_WORKERS = shift and next if ( $_arg eq 'max_workers' ); $CHUNK_SIZE = shift and next if ( $_arg eq 'chunk_size' ); $MCE::FREEZE = $MCE::MCE->{freeze} = shift and next if ( $_arg eq 'freeze' ); $MCE::THAW = $MCE::MCE->{thaw} = shift and next if ( $_arg eq 'thaw' ); if ( $_arg eq 'sereal' ) { if (shift eq '1') { local $@; eval 'use Sereal qw(encode_sereal decode_sereal)'; unless ($@) { $MCE::FREEZE = $MCE::MCE->{freeze} = \&encode_sereal; $MCE::THAW = $MCE::MCE->{thaw} = \&decode_sereal; } } next; } if ( $_arg eq 'tmp_dir' ) { $MCE::TMP_DIR = $MCE::MCE->{tmp_dir} = shift; my $_e1 = 'is not a directory or does not exist'; my $_e2 = 'is not writeable'; _croak($_tag."::import: ($MCE::TMP_DIR) $_e1") unless -d $MCE::TMP_DIR; _croak($_tag."::import: ($MCE::TMP_DIR) $_e2") unless -w $MCE::TMP_DIR; next; } _croak($_tag."::import: ($_argument) is not a valid module argument"); } $MAX_WORKERS = MCE::Util::_parse_max_workers($MAX_WORKERS); _validate_number($MAX_WORKERS, 'MAX_WORKERS'); _validate_number($CHUNK_SIZE, 'CHUNK_SIZE') unless ($CHUNK_SIZE eq 'auto'); ## Import functions. no strict 'refs'; no warnings 'redefine'; my $_pkg = caller; *{ $_pkg.'::mce_grep_f' } = \&run_file; *{ $_pkg.'::mce_grep_s' } = \&run_seq; *{ $_pkg.'::mce_grep' } = \&run; return; } END { return if (defined $_MCE && $_MCE->wid); finish(); } ############################################################################### ## ---------------------------------------------------------------------------- ## Gather callback for storing by chunk_id => chunk_ref into a hash. ## ############################################################################### my ($_total_chunks, %_tmp); sub _gather { my ($_chunk_id, $_data_ref) = @_; $_tmp{$_chunk_id} = $_data_ref; $_total_chunks++; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Init and finish routines. ## ############################################################################### sub init (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } finish(); $_params = (ref $_[0] eq 'HASH') ? shift : { @_ }; @_ = (); return; } sub finish () { if (defined $_MCE && $_MCE->{_spawned}) { MCE::_save_state; $_MCE->shutdown(); MCE::_restore_state; } $_prev_c = $_total_chunks = undef; undef %_tmp; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel grep with MCE -- file. ## ############################################################################### sub run_file (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); my $_code = shift; my $_file = shift; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{sequence} if (exists $_params->{sequence}); } else { $_params = {}; } if (defined $_file && ref $_file eq '' && $_file ne '') { _croak("$_tag: ($_file) does not exist") unless (-e $_file); _croak("$_tag: ($_file) is not readable") unless (-r $_file); _croak("$_tag: ($_file) is not a plain file") unless (-f $_file); $_params->{_file} = $_file; } elsif (ref $_file eq 'GLOB' || ref $_file eq 'SCALAR' || ref($_file) =~ /^IO::/) { $_params->{_file} = $_file; } else { _croak("$_tag: (file) is not specified or valid"); } @_ = (); return run($_code); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel grep with MCE -- sequence. ## ############################################################################### sub run_seq (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); my $_code = shift; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{_file} if (exists $_params->{_file}); } else { $_params = {}; } my ($_begin, $_end); if (ref $_[0] eq 'HASH') { $_begin = $_[0]->{begin}; $_end = $_[0]->{end}; $_params->{sequence} = $_[0]; } elsif (ref $_[0] eq 'ARRAY') { $_begin = $_[0]->[0]; $_end = $_[0]->[1]; $_params->{sequence} = $_[0]; } elsif (ref $_[0] eq '') { $_begin = $_[0]; $_end = $_[1]; $_params->{sequence} = [ @_ ]; } else { _croak("$_tag: (sequence) is not specified or valid"); } _croak("$_tag: (begin) is not specified for sequence") unless (defined $_begin); _croak("$_tag: (end) is not specified for sequence") unless (defined $_end); $_params->{sequence_run} = 1; @_ = (); return run($_code); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel grep with MCE. ## ############################################################################### sub run (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Grep'); my $_code = shift; $_total_chunks = 0; undef %_tmp; if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } my $_input_data; my $_max_workers = $MAX_WORKERS; my $_r = ref $_[0]; if ($_r eq 'ARRAY' || $_r eq 'CODE' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_input_data = shift; } if (defined $_params) { my $_p = $_params; $_max_workers = MCE::Util::_parse_max_workers($_p->{max_workers}) if (exists $_p->{max_workers}); delete $_p->{sequence} if (defined $_input_data || scalar @_); delete $_p->{user_func} if (exists $_p->{user_func}); delete $_p->{user_tasks} if (exists $_p->{user_tasks}); delete $_p->{use_slurpio} if (exists $_p->{use_slurpio}); delete $_p->{bounds_only} if (exists $_p->{bounds_only}); delete $_p->{gather} if (exists $_p->{gather}); } my $_chunk_size = MCE::Util::_parse_chunk_size( $CHUNK_SIZE, $_max_workers, $_params, $_input_data, scalar @_ ); if (defined $_params) { if (exists $_params->{_file}) { $_input_data = delete $_params->{_file}; } else { $_input_data = $_params->{input_data} if exists $_params->{input_data}; } } MCE::_save_state; ## ------------------------------------------------------------------------- if (!defined $_prev_c || $_prev_c != $_code) { $_MCE->shutdown() if (defined $_MCE); $_prev_c = $_code; my %_options = ( max_workers => $_max_workers, task_name => $_tag, user_func => sub { my ($_mce, $_chunk_ref, $_chunk_id) = @_; my $_wantarray = $_mce->{user_args}[0]; if ($_wantarray) { my @_a; if (ref $_chunk_ref eq 'SCALAR') { local $/ = $_mce->{RS} if defined $_mce->{RS}; open my $_MEM_FH, '<', $_chunk_ref; while ( <$_MEM_FH> ) { push (@_a, $_) if &{ $_code }; } close $_MEM_FH; } else { if (ref $_chunk_ref) { push @_a, grep { &{ $_code } } @{ $_chunk_ref }; } else { push @_a, grep { &{ $_code } } $_chunk_ref; } } MCE->gather($_chunk_id, \@_a); } else { my $_cnt = 0; if (ref $_chunk_ref eq 'SCALAR') { local $/ = $_mce->{RS} if defined $_mce->{RS}; open my $_MEM_FH, '<', $_chunk_ref; while ( <$_MEM_FH> ) { $_cnt++ if &{ $_code }; } close $_MEM_FH; } else { if (ref $_chunk_ref) { $_cnt += grep { &{ $_code } } @{ $_chunk_ref }; } else { $_cnt += grep { &{ $_code } } $_chunk_ref; } } MCE->gather($_cnt) if defined $_wantarray; } }, ); if (defined $_params) { for my $_p (keys %{ $_params }) { next if ($_p eq 'sequence_run'); next if ($_p eq 'input_data'); next if ($_p eq 'chunk_size'); _croak("MCE::Grep: ($_p) is not a valid constructor argument") unless (exists $MCE::_valid_fields_new{$_p}); $_options{$_p} = $_params->{$_p}; } } $_MCE = MCE->new(%_options); } ## ------------------------------------------------------------------------- my $_cnt = 0; my $_wantarray = wantarray; $_MCE->{use_slurpio} = ($_chunk_size > MCE::MAX_RECS_SIZE) ? 1 : 0; $_MCE->{user_args} = [ $_wantarray ]; $_MCE->{gather} = $_wantarray ? \&_gather : sub { $_cnt += $_[0]; return; }; if (defined $_input_data) { @_ = (); $_MCE->process({ chunk_size => $_chunk_size }, $_input_data); delete $_MCE->{input_data}; } elsif (scalar @_) { $_MCE->process({ chunk_size => $_chunk_size }, \@_); delete $_MCE->{input_data}; } else { if (defined $_params && exists $_params->{sequence}) { $_MCE->run({ chunk_size => $_chunk_size, sequence => $_params->{sequence} }, 0); if (exists $_params->{sequence_run}) { delete $_params->{sequence_run}; delete $_params->{sequence}; } delete $_MCE->{sequence}; } } MCE::_restore_state; if (exists $_MCE->{_rla_return}) { $MCE::MCE->{_rla_return} = delete $_MCE->{_rla_return}; } finish() if ($^S); ## shutdown if in eval state if ($_wantarray) { return map { @{ $_ } } delete @_tmp{ 1 .. $_total_chunks }; } elsif (defined $_wantarray) { return $_cnt; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _croak { goto &MCE::_croak; } sub _validate_number { my ($_n, $_key) = @_; _croak("$_tag: ($_key) is not valid") if (!defined $_n); $_n =~ s/K\z//i; $_n =~ s/M\z//i; if (!looks_like_number($_n) || int($_n) != $_n || $_n < 1) { _croak("$_tag: ($_key) is not valid"); } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Grep - Parallel grep model similar to the native grep function =head1 VERSION This document describes MCE::Grep version 1.608 =head1 SYNOPSIS ## Exports mce_grep, mce_grep_f, and mce_grep_s use MCE::Grep; ## Array or array_ref my @a = mce_grep { $_ % 5 == 0 } 1..10000; my @b = mce_grep { $_ % 5 == 0 } [ 1..10000 ]; ## File_path, glob_ref, or scalar_ref my @c = mce_grep_f { /pattern/ } "/path/to/file"; my @d = mce_grep_f { /pattern/ } $file_handle; my @e = mce_grep_f { /pattern/ } \$scalar; ## Sequence of numbers (begin, end [, step, format]) my @f = mce_grep_s { %_ * 3 == 0 } 1, 10000, 5; my @g = mce_grep_s { %_ * 3 == 0 } [ 1, 10000, 5 ]; my @h = mce_grep_s { %_ * 3 == 0 } { begin => 1, end => 10000, step => 5, format => undef }; =head1 DESCRIPTION This module provides a parallel grep implementation via Many-Core Engine. MCE incurs a small overhead due to passing of data. A fast code block will run faster natively. However, the overhead will likely diminish as the complexity increases for the code. my @m1 = grep { $_ % 5 == 0 } 1..1000000; ## 0.065 secs my @m2 = mce_grep { $_ % 5 == 0 } 1..1000000; ## 0.194 secs Chunking, enabled by default, greatly reduces the overhead behind the scene. The time for mce_grep below also includes the time for data exchanges between the manager and worker processes. More parallelization will be seen when the code incurs additional CPU time. my @m1 = grep { /[2357][1468][9]/ } 1..1000000; ## 0.353 secs my @m2 = mce_grep { /[2357][1468][9]/ } 1..1000000; ## 0.218 secs Even faster is mce_grep_s; useful when input data is a range of numbers. Workers generate sequences mathematically among themselves without any interaction from the manager process. Two arguments are required for mce_grep_s (begin, end). Step defaults to 1 if begin is smaller than end, otherwise -1. my @m3 = mce_grep_s { /[2357][1468][9]/ } 1, 1000000; ## 0.165 secs Although this document is about MCE::Grep, the L module can write results immediately without waiting for all chunks to complete. This is made possible by passing the reference to an array (in this case @m4 and @m5). use MCE::Stream default_mode => 'grep'; my @m4; mce_stream \@m4, sub { /[2357][1468][9]/ }, 1..1000000; ## Completed in 0.203 secs. This is amazing considering the ## overhead for passing data between the manager and workers. my @m5; mce_stream_s \@m5, sub { /[2357][1468][9]/ }, 1, 1000000; ## Completed in 0.120 secs. Like with mce_grep_s, specifying a ## sequence specification turns out to be faster due to lesser ## overhead for the manager process. A common scenario is grepping for pattern(s) inside a massive log file. Notice how parallelism increases as complexity increases for the pattern. Testing was done against a 300 MB file containing 250k lines. use MCE::Grep; my @m; open my $LOG, "<", "/path/to/log/file" or die "$!\n"; @m = grep { /pattern/ } <$LOG>; ## 0.756 secs @m = grep { /foobar|[2357][1468][9]/ } <$LOG>; ## 24.681 secs ## Parallelism with mce_grep. This involves the manager process ## due to processing a file handle. @m = mce_grep { /pattern/ } <$LOG>; ## 0.997 secs @m = mce_grep { /foobar|[2357][1468][9]/ } <$LOG>; ## 7.439 secs ## Even faster with mce_grep_f. Workers access the file directly ## with zero interaction from the manager process. my $LOG = "/path/to/file"; @m = mce_grep_f { /pattern/ } $LOG; ## 0.112 secs @m = mce_grep_f { /foobar|[2357][1468][9]/ } $LOG; ## 6.840 secs =head1 OVERRIDING DEFAULTS The following list 5 options which may be overridden when loading the module. use Sereal qw( encode_sereal decode_sereal ); use CBOR::XS qw( encode_cbor decode_cbor ); use JSON::XS qw( encode_json decode_json ); use MCE::Grep max_workers => 4, ## Default 'auto' chunk_size => 100, ## Default 'auto' tmp_dir => "/path/to/app/tmp", ## $MCE::Signal::tmp_dir freeze => \&encode_sereal, ## \&Storable::freeze thaw => \&decode_sereal ## \&Storable::thaw ; There is a simpler way to enable Sereal with MCE 1.5. The following will attempt to use Sereal if available, otherwise defaults to Storable for serialization. use MCE::Grep Sereal => 1; ## Serialization is by the Sereal module if available. my @m2 = mce_grep { $_ % 5 == 0 } 1..10000; =head1 CUSTOMIZING MCE =over 3 =item MCE::Grep->init ( options ) =item MCE::Grep::init { options } The init function accepts a hash of MCE options. The gather option, if specified, is ignored due to being used internally by the module. use MCE::Grep; MCE::Grep::init { chunk_size => 1, max_workers => 4, user_begin => sub { print "## ", MCE->wid, " started\n"; }, user_end => sub { print "## ", MCE->wid, " completed\n"; } }; my @a = mce_grep { $_ % 5 == 0 } 1..100; print "\n", "@a", "\n"; -- Output ## 2 started ## 3 started ## 1 started ## 4 started ## 3 completed ## 4 completed ## 1 completed ## 2 completed 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 =back =head1 API DOCUMENTATION =over 3 =item MCE::Grep->run ( sub { code }, iterator ) =item mce_grep { code } iterator An iterator reference can by specified for input_data. Iterators are described under "SYNTAX for INPUT_DATA" at L. my @a = mce_grep { $_ % 3 == 0 } make_iterator(10, 30, 2); =item MCE::Grep->run ( sub { code }, list ) =item mce_grep { code } list Input data can be defined using a list. my @a = mce_grep { /[2357]/ } 1..1000; my @b = mce_grep { /[2357]/ } [ 1..1000 ]; =item MCE::Grep->run_file ( sub { code }, file ) =item mce_grep_f { code } file The fastest of these is the /path/to/file. Workers communicate the next offset position among themselves without any interaction from the manager process. my @c = mce_grep_f { /pattern/ } "/path/to/file"; my @d = mce_grep_f { /pattern/ } $file_handle; my @e = mce_grep_f { /pattern/ } \$scalar; =item MCE::Grep->run_seq ( sub { code }, $beg, $end [, $step, $fmt ] ) =item mce_grep_s { code } $beg, $end [, $step, $fmt ] Sequence can be defined as a list, an array reference, or a hash reference. The functions require both begin and end values to run. Step and format are optional. The format is passed to sprintf (% may be omitted below). my ($beg, $end, $step, $fmt) = (10, 20, 0.1, "%4.1f"); my @f = mce_grep_s { /[1234]\.[5678]/ } $beg, $end, $step, $fmt; my @g = mce_grep_s { /[1234]\.[5678]/ } [ $beg, $end, $step, $fmt ]; my @h = mce_grep_s { /[1234]\.[5678]/ } { begin => $beg, end => $end, step => $step, format => $fmt }; =back =head1 MANUAL SHUTDOWN =over 3 =item MCE::Grep->finish =item MCE::Grep::finish Workers remain persistent as much as possible after running. Shutdown occurs automatically when the script terminates. Call finish when workers are no longer needed. use MCE::Grep; MCE::Grep::init { chunk_size => 20, max_workers => 'auto' }; my @a = mce_grep { ... } 1..100; MCE::Grep::finish; =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Core/0000755000076400007640000000000012511673554013217 5ustar mariomarioMCE-1.608/lib/MCE/Core/Validation.pm0000644000076400007640000001762312511672724015656 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Validation - Core validation methods for Many-Core Engine. ## ## This package provides validation methods used internally by the manager ## process. ## ## There is no public API. ## ############################################################################### package MCE::Core::Validation; use strict; use warnings; our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; ############################################################################### ## ---------------------------------------------------------------------------- ## Validation method (attributes allowed for top-level). ## ############################################################################### sub _validate_args { my $_s = $_[0]; @_ = (); my $_tag = 'MCE::_validate_args'; if (defined $_s->{input_data} && ref $_s->{input_data} eq '') { _croak("$_tag: ($_s->{input_data}) does not exist") unless (-e $_s->{input_data}); } _croak("$_tag: (use_slurpio) is not 0 or 1") if ($_s->{use_slurpio} && $_s->{use_slurpio} !~ /\A[01]\z/); _croak("$_tag: (parallel_io) is not 0 or 1") if ($_s->{parallel_io} && $_s->{parallel_io} !~ /\A[01]\z/); _croak("$_tag: (job_delay) is not valid") if ($_s->{job_delay} && (!looks_like_number($_s->{job_delay}) || $_s->{job_delay} < 0)); _croak("$_tag: (spawn_delay) is not valid") if ($_s->{spawn_delay} && (!looks_like_number($_s->{spawn_delay}) || $_s->{spawn_delay} < 0)); _croak("$_tag: (submit_delay) is not valid") if ($_s->{submit_delay} && (!looks_like_number($_s->{submit_delay}) || $_s->{submit_delay} < 0)); _croak("$_tag: (freeze) is not a CODE reference") if ($_s->{freeze} && ref $_s->{freeze} ne 'CODE'); _croak("$_tag: (thaw) is not a CODE reference") if ($_s->{thaw} && ref $_s->{thaw} ne 'CODE'); _croak("$_tag: (on_post_exit) is not a CODE reference") if ($_s->{on_post_exit} && ref $_s->{on_post_exit} ne 'CODE'); _croak("$_tag: (on_post_run) is not a CODE reference") if ($_s->{on_post_run} && ref $_s->{on_post_run} ne 'CODE'); _croak("$_tag: (user_error) is not a CODE reference") if ($_s->{user_error} && ref $_s->{user_error} ne 'CODE'); _croak("$_tag: (user_output) is not a CODE reference") if ($_s->{user_output} && ref $_s->{user_output} ne 'CODE'); _croak("$_tag: (flush_file) is not 0 or 1") if ($_s->{flush_file} && $_s->{flush_file} !~ /\A[01]\z/); _croak("$_tag: (flush_stderr) is not 0 or 1") if ($_s->{flush_stderr} && $_s->{flush_stderr} !~ /\A[01]\z/); _croak("$_tag: (flush_stdout) is not 0 or 1") if ($_s->{flush_stdout} && $_s->{flush_stdout} !~ /\A[01]\z/); _validate_args_s($_s); if (defined $_s->{user_tasks}) { for my $_t (@{ $_s->{user_tasks} }) { _validate_args_s($_s, $_t); _croak("$_tag: (task_end) is not a CODE reference") if ($_t->{task_end} && ref $_t->{task_end} ne 'CODE'); } } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Validation method (top-level and sub-tasks). ## ############################################################################### sub _validate_args_s { my $self = $_[0]; my $_s = $_[1] || $self; @_ = (); my $_tag = 'MCE::_validate_args_s'; if (defined $_s->{max_workers}) { $_s->{max_workers} = _parse_max_workers($_s->{max_workers}); _croak("$_tag: (max_workers) is not valid") if ($_s->{max_workers} !~ /\A\d+\z/); } if (defined $_s->{chunk_size}) { if ($_s->{chunk_size} =~ /([0-9\.]+)K\z/i) { $_s->{chunk_size} = int($1 * 1024 + 0.5); } elsif ($_s->{chunk_size} =~ /([0-9\.]+)M\z/i) { $_s->{chunk_size} = int($1 * 1024 * 1024 + 0.5); } _croak("$_tag: (chunk_size) is not valid") if ($_s->{chunk_size} !~ /\A\d+\z/ or $_s->{chunk_size} == 0); $_s->{chunk_size} = MAX_CHUNK_SIZE if ($_s->{chunk_size} > MAX_CHUNK_SIZE); } _croak("$_tag: (RS) is not valid") if ($_s->{RS} && ref $_s->{RS} ne ''); _croak("$_tag: (use_threads) is not 0 or 1") if ($_s->{use_threads} && $_s->{use_threads} !~ /\A[01]\z/); _croak("$_tag: (user_begin) is not a CODE reference") if ($_s->{user_begin} && ref $_s->{user_begin} ne 'CODE'); _croak("$_tag: (user_func) is not a CODE reference") if ($_s->{user_func} && ref $_s->{user_func} ne 'CODE'); _croak("$_tag: (user_end) is not a CODE reference") if ($_s->{user_end} && ref $_s->{user_end} ne 'CODE'); if (defined $_s->{gather}) { my $_ref = ref $_s->{gather}; _croak("$_tag: (gather) is not a valid reference") if ( $_ref ne 'MCE::Queue' && $_ref ne 'Thread::Queue' && $_ref ne 'ARRAY' && $_ref ne 'HASH' && $_ref ne 'CODE' ); } if (defined $_s->{sequence}) { my $_seq = $_s->{sequence}; if (ref $_seq eq 'ARRAY') { my ($_begin, $_end, $_step, $_fmt) = @{ $_seq }; $_seq = { begin => $_begin, end => $_end, step => $_step, format => $_fmt }; } else { _croak("$_tag: (sequence) is not a HASH or ARRAY reference") if (ref $_seq ne 'HASH'); } _croak("$_tag: (begin) is not defined for sequence") unless (defined $_seq->{begin}); _croak("$_tag: (end) is not defined for sequence") unless (defined $_seq->{end}); for my $_p (qw(begin end step)) { _croak("$_tag: ($_p) is not valid for sequence") if (defined $_seq->{$_p} && !looks_like_number($_seq->{$_p})); } unless (defined $_seq->{step}) { $_seq->{step} = ($_seq->{begin} < $_seq->{end}) ? 1 : -1; if (ref $_s->{sequence} eq 'ARRAY') { $_s->{sequence}->[2] = $_seq->{step}; } } if ( ($_seq->{step} < 0 && $_seq->{begin} < $_seq->{end}) || ($_seq->{step} > 0 && $_seq->{begin} > $_seq->{end}) || ($_seq->{step} == 0) ) { _croak("$_tag: impossible (step size) for sequence"); } } if (defined $_s->{interval}) { if (ref $_s->{interval} eq '') { $_s->{interval} = { delay => $_s->{interval} }; } my $_i = $_s->{interval}; _croak("$_tag: (interval) is not a HASH reference") if (ref $_i ne 'HASH'); _croak("$_tag: (delay) is not defined for interval") unless (defined $_i->{delay}); _croak("$_tag: (delay) is not valid for interval") if (!looks_like_number($_i->{delay}) || $_i->{delay} < 0); for my $_p (qw(max_nodes node_id)) { _croak("$_tag: ($_p) is not valid for interval") if (defined $_i->{$_p} && ( !looks_like_number($_i->{$_p}) || int($_i->{$_p}) != $_i->{$_p} || $_i->{$_p} < 1 )); } $_i->{max_nodes} = 1 unless (exists $_i->{max_nodes}); $_i->{node_id} = 1 unless (exists $_i->{node_id}); $_i->{_time} = time; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Validation method (run state). ## ############################################################################### sub _validate_runstate { my $self = $_[0]; my $_tag = $_[1]; @_ = (); _croak("$_tag: method cannot be called by the worker process") if ($self->{_wid}); _croak("$_tag: method cannot be called while processing") if ($self->{_send_cnt}); _croak("$_tag: method cannot be called while running") if ($self->{_total_running}); return; } 1; MCE-1.608/lib/MCE/Core/Input/0000755000076400007640000000000012511673554014316 5ustar mariomarioMCE-1.608/lib/MCE/Core/Input/Iterator.pm0000644000076400007640000000533212511672745016451 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Input::Iterator - Iterator reader. ## ## This package, used interally by the worker process, provides support for ## user specified iterators assigned to input_data. ## ## There is no public API. ## ############################################################################### package MCE::Core::Input::Iterator; use strict; use warnings; our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- User Iterator. ## ############################################################################### sub _worker_user_iterator { my ($self) = @_; @_ = (); _croak('MCE::_worker_user_iterator: (user_func) is not specified') unless (defined $self->{user_func}); my $_chn = $self->{_chn}; my $_DAT_LOCK = $self->{_dat_lock}; my $_DAT_W_SOCK = $self->{_dat_w_sock}->[0]; my $_DAU_W_SOCK = $self->{_dat_w_sock}->[$_chn]; my $_lock_chn = $self->{_lock_chn}; my $_chunk_size = $self->{chunk_size}; my $_I_FLG = (!$/ || $/ ne $LF); my $_wuf = $self->{_wuf}; my ($_chunk_id, $_len, $_is_ref); ## ------------------------------------------------------------------------- $self->{_next_jmp} = sub { goto _WORKER_USER_ITERATOR__NEXT; }; $self->{_last_jmp} = sub { goto _WORKER_USER_ITERATOR__LAST; }; local $_; _WORKER_USER_ITERATOR__NEXT: while (1) { undef $_ if (length > MAX_CHUNK_SIZE); $_ = ''; ## Obtain the next chunk of data. { local $\ = undef if (defined $\); local $/ = $LF if ($_I_FLG); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_U_ITR . $LF . $_chn . $LF; chomp($_len = <$_DAU_W_SOCK>); if ($_len < 0) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } $_is_ref = chop $_len; chomp($_chunk_id = <$_DAU_W_SOCK>); read $_DAU_W_SOCK, $_, $_len; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } ## Call user function. if ($_is_ref) { my $_chunk_ref = $self->{thaw}($_); undef $_; $_ = ($_chunk_size == 1) ? $_chunk_ref->[0] : $_chunk_ref; $_wuf->($self, $_chunk_ref, $_chunk_id); } else { $_wuf->($self, [ $_ ], $_chunk_id); } } _WORKER_USER_ITERATOR__LAST: return; } 1; MCE-1.608/lib/MCE/Core/Input/Handle.pm0000644000076400007640000001752112511672742016053 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Input::Handle - File_path and Scalar_ref input reader. ## ## This package provides the read handle method used internally by the worker ## process. Distribution follows a bank-queuing model. ## ## There is no public API. ## ############################################################################### package MCE::Core::Input::Handle; use strict; use warnings; our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use bytes; my $_que_read_size = $MCE::_que_read_size; my $_que_template = $MCE::_que_template; ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- Read handle. ## ############################################################################### sub _worker_read_handle { my ($self, $_proc_type, $_input_data) = @_; @_ = (); _croak('MCE::_worker_read_handle: (user_func) is not specified') unless (defined $self->{user_func}); my $_QUE_R_SOCK = $self->{_que_r_sock}; my $_QUE_W_SOCK = $self->{_que_w_sock}; my $_chunk_size = $self->{chunk_size}; my $_use_slurpio = $self->{use_slurpio}; my $_parallel_io = $self->{parallel_io}; my $_RS = $self->{RS} || $/; my $_RS_FLG = (!$_RS || $_RS ne $LF); my $_wuf = $self->{_wuf}; my ($_data_size, $_next, $_chunk_id, $_offset_pos, $_IN_FILE, $_tmp_cs); my ($_chop_len, $_chop_str, $_p); if (length $_RS > 1 && substr($_RS, 0, 1) eq "\n") { $_chop_str = substr($_RS, 1); $_chop_len = length $_chop_str; } else { $_chop_str = ''; $_chop_len = 0; } $_data_size = ($_proc_type == READ_MEMORY) ? length ${ $_input_data } : -s $_input_data; $_chunk_id = $_offset_pos = 0; if ($_chunk_size <= MAX_RECS_SIZE || $_proc_type == READ_MEMORY) { open $_IN_FILE, '<', $_input_data or die "$_input_data: $!\n"; binmode $_IN_FILE; } else { sysopen $_IN_FILE, $_input_data, O_RDONLY or die "$_input_data: $!\n"; } ## ------------------------------------------------------------------------- $self->{_next_jmp} = sub { goto _WORKER_READ_HANDLE__NEXT; }; $self->{_last_jmp} = sub { goto _WORKER_READ_HANDLE__LAST; }; local $_; _WORKER_READ_HANDLE__NEXT: while (1) { my @_recs; undef $_ if (length > MAX_CHUNK_SIZE); $_ = ''; ## Obtain the next chunk_id and offset position. sysread $_QUE_R_SOCK, $_next, $_que_read_size; ($_chunk_id, $_offset_pos) = unpack($_que_template, $_next); if ($_offset_pos >= $_data_size) { syswrite $_QUE_W_SOCK, pack($_que_template, 0, $_offset_pos); close $_IN_FILE; undef $_IN_FILE; return; } if (++$_chunk_id > 1 && $_chop_len) { $_p = $_chop_len; $_ = $_chop_str; } else { $_p = 0; } ## Read data. if ($_chunk_size <= MAX_RECS_SIZE) { ## One or many records. local $/ = $_RS if ($_RS_FLG); seek $_IN_FILE, $_offset_pos, 0; if ($_chunk_size == 1) { if ($_p) { $_ .= <$_IN_FILE>; } else { $_ = <$_IN_FILE>; } } else { if ($_use_slurpio) { for my $i (0 .. $_chunk_size - 1) { $_ .= <$_IN_FILE>; } } else { if ($_chop_len) { $_recs[0] = ($_chunk_id > 1) ? $_chop_str : ''; $_recs[0] .= <$_IN_FILE>; for my $i (1 .. $_chunk_size - 1) { $_recs[$i] = $_chop_str; $_recs[$i] .= <$_IN_FILE>; if (length $_recs[$i] == $_chop_len) { delete $_recs[$i]; last; } } } else { for my $i (0 .. $_chunk_size - 1) { $_recs[$i] = <$_IN_FILE>; unless (defined $_recs[$i]) { delete $_recs[$i]; last; } } } } } syswrite $_QUE_W_SOCK, pack($_que_template, $_chunk_id, tell $_IN_FILE); } else { ## Large chunk. local $/ = $_RS if ($_RS_FLG); if ($_proc_type == READ_MEMORY) { if ($_parallel_io && ! $_RS_FLG) { syswrite $_QUE_W_SOCK, pack($_que_template, $_chunk_id, $_offset_pos + $_chunk_size); $_tmp_cs = $_chunk_size; seek $_IN_FILE, $_offset_pos, 0; if ($_offset_pos) { $_tmp_cs -= length <$_IN_FILE> || 0; } if (read($_IN_FILE, $_, $_tmp_cs, $_p) == $_tmp_cs) { $_ .= <$_IN_FILE>; } } else { seek $_IN_FILE, $_offset_pos, 0; if (read($_IN_FILE, $_, $_chunk_size, $_p) == $_chunk_size) { $_ .= <$_IN_FILE>; } syswrite $_QUE_W_SOCK, pack($_que_template, $_chunk_id, tell $_IN_FILE); } } else { if ($_parallel_io && ! $_RS_FLG) { syswrite $_QUE_W_SOCK, pack($_que_template, $_chunk_id, $_offset_pos + $_chunk_size); $_tmp_cs = $_chunk_size; if ($_offset_pos) { seek $_IN_FILE, $_offset_pos, 0; $_tmp_cs -= length <$_IN_FILE> || 0; sysseek $_IN_FILE, tell $_IN_FILE, 0; } else { sysseek $_IN_FILE, $_offset_pos, 0; } if (sysread($_IN_FILE, $_, $_tmp_cs, $_p) == $_tmp_cs) { seek $_IN_FILE, sysseek($_IN_FILE, 0, 1), 0; $_ .= <$_IN_FILE>; } } else { sysseek $_IN_FILE, $_offset_pos, 0; if (sysread($_IN_FILE, $_, $_chunk_size, $_p) == $_chunk_size) { seek $_IN_FILE, sysseek($_IN_FILE, 0, 1), 0; $_ .= <$_IN_FILE>; } else { seek $_IN_FILE, sysseek($_IN_FILE, 0, 1), 0; } syswrite $_QUE_W_SOCK, pack($_que_template, $_chunk_id, tell $_IN_FILE); } } } ## Call user function. if ($_use_slurpio) { if ($_chop_len && substr($_, -$_chop_len) eq $_chop_str) { substr($_, -$_chop_len, $_chop_len, ''); } local $_ = \$_; $_wuf->($self, $_, $_chunk_id); } else { if ($_chunk_size == 1) { if ($_chop_len && substr($_, -$_chop_len) eq $_chop_str) { substr($_, -$_chop_len, $_chop_len, ''); } $_wuf->($self, [ $_ ], $_chunk_id); } else { if ($_chunk_size > MAX_RECS_SIZE) { local $/ = $_RS if ($_RS_FLG); _sync_buffer_to_array(\$_, \@_recs, $_chop_str); undef $_; } if ($_chop_len) { for my $i (0 .. @_recs - 1) { if (substr($_recs[$i], -$_chop_len) eq $_chop_str) { substr($_recs[$i], -$_chop_len, $_chop_len, ''); } } } local $_ = \@_recs; $_wuf->($self, \@_recs, $_chunk_id); } } } _WORKER_READ_HANDLE__LAST: close $_IN_FILE; undef $_IN_FILE; return; } 1; MCE-1.608/lib/MCE/Core/Input/Generator.pm0000644000076400007640000001441712511672740016605 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Input::Generator - Sequence of numbers (for task_id > 0). ## ## This package provides a sequence of numbers used internally by the worker ## process. Distribution is divided equally among workers. This allows sequence ## to be configured independently among multiple user tasks. ## ## There is no public API. ## ############################################################################### package MCE::Core::Input::Generator; use strict; use warnings; our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- Sequence Generator (equal distribution among workers). ## ############################################################################### sub _worker_sequence_generator { my ($self) = @_; @_ = (); _croak('MCE::_worker_sequence_generator: (user_func) is not specified') unless (defined $self->{user_func}); my $_bounds_only = $self->{bounds_only} || 0; my $_max_workers = $self->{max_workers}; my $_chunk_size = $self->{chunk_size}; my $_wuf = $self->{_wuf}; my ($_begin, $_end, $_step, $_fmt); if (ref $self->{sequence} eq 'ARRAY') { ($_begin, $_end, $_step, $_fmt) = @{ $self->{sequence} }; } else { $_begin = $self->{sequence}->{begin}; $_end = $self->{sequence}->{end}; $_step = $self->{sequence}->{step}; $_fmt = $self->{sequence}->{format}; } my $_wid = $self->{_task_wid} || $self->{_wid}; my $_next = ($_wid - 1) * $_chunk_size * $_step + $_begin; my $_chunk_id = $_wid; $_fmt =~ s/%// if (defined $_fmt); ## ------------------------------------------------------------------------- local $_; $self->{_last_jmp} = sub { goto _WORKER_SEQ_GEN__LAST; }; if ($_begin == $_end) { ## Identical, yes. if ($_wid == 1) { $self->{_next_jmp} = sub { goto _WORKER_SEQ_GEN__LAST; }; $_ = (defined $_fmt) ? sprintf("%$_fmt", $_next) : $_next; if ($_chunk_size > 1) { $_ = ($_bounds_only) ? [ $_, $_ ] : [ $_ ]; } $_wuf->($self, $_, $_chunk_id); } } elsif ($_chunk_size == 1) { ## Chunking, no. $self->{_next_jmp} = sub { goto _WORKER_SEQ_GEN__NEXT_A; }; my $_flag = ($_begin < $_end); while (1) { return if ( $_flag && $_next > $_end); return if (!$_flag && $_next < $_end); $_ = (defined $_fmt) ? sprintf("%$_fmt", $_next) : $_next; $_wuf->($self, $_, $_chunk_id); _WORKER_SEQ_GEN__NEXT_A: $_chunk_id += $_max_workers; $_next = ($_chunk_id - 1) * $_step + $_begin; } } else { ## Chunking, yes. $self->{_next_jmp} = sub { goto _WORKER_SEQ_GEN__NEXT_B; }; while (1) { my @_n = (); my $_n_begin = $_next; ## ------------------------------------------------------------------- if ($_bounds_only) { my $_tmp_b = $_next; my $_tmp_e; if ($_begin < $_end) { if ($_step * $_chunk_size + $_n_begin <= $_end) { $_tmp_e = $_step * ($_chunk_size - 1) + $_n_begin; } else { my $_start = int( ($_end - $_next) / $_chunk_size / $_step * $_chunk_size ); $_start = 1 if ($_start < 1); for my $_i ($_start .. $_chunk_size) { last if ($_next > $_end); $_tmp_e = $_next; $_next = $_step * $_i + $_n_begin; } } } else { if ($_step * $_chunk_size + $_n_begin >= $_end) { $_tmp_e = $_step * ($_chunk_size - 1) + $_n_begin; } else { my $_start = int( ($_next - $_end) / $_chunk_size / $_step * $_chunk_size ); $_start = 1 if ($_start < 1); for my $_i ($_start .. $_chunk_size) { last if ($_next < $_end); $_tmp_e = $_next; $_next = $_step * $_i + $_n_begin; } } } return unless (defined $_tmp_e); if (defined $_fmt) { @_n = (sprintf("%$_fmt", $_tmp_b), sprintf("%$_fmt", $_tmp_e)); } else { @_n = ($_tmp_b, $_tmp_e); } } ## ------------------------------------------------------------------- else { if ($_begin < $_end) { if (!defined $_fmt && $_step == 1) { if ($_next + $_chunk_size <= $_end) { @_n = ($_next .. $_next + $_chunk_size - 1); } else { @_n = ($_next .. $_end); } } else { for my $_i (1 .. $_chunk_size) { last if ($_next > $_end); push @_n, (defined $_fmt) ? sprintf("%$_fmt", $_next) : $_next; $_next = $_step * $_i + $_n_begin; } } } else { for my $_i (1 .. $_chunk_size) { last if ($_next < $_end); push @_n, (defined $_fmt) ? sprintf("%$_fmt", $_next) : $_next; $_next = $_step * $_i + $_n_begin; } } return unless (scalar @_n); } ## ------------------------------------------------------------------- $_ = \@_n; $_wuf->($self, \@_n, $_chunk_id); _WORKER_SEQ_GEN__NEXT_B: $_chunk_id += $_max_workers; $_next = ($_chunk_id - 1) * $_chunk_size * $_step + $_begin; } } _WORKER_SEQ_GEN__LAST: return; } 1; MCE-1.608/lib/MCE/Core/Input/Sequence.pm0000644000076400007640000001350412511672755016431 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Input::Sequence - Sequence of numbers (for task_id == 0). ## ## This package provides a sequence of numbers used internally by the worker ## process. Distribution follows a bank-queuing model. ## ## There is no public API. ## ############################################################################### package MCE::Core::Input::Sequence; use strict; use warnings; our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; my $_que_read_size = $MCE::_que_read_size; my $_que_template = $MCE::_que_template; ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- Sequence Queue (distribution via bank queuing model). ## ############################################################################### sub _worker_sequence_queue { my ($self) = @_; @_ = (); _croak('MCE::_worker_sequence_queue: (user_func) is not specified') unless (defined $self->{user_func}); my $_QUE_R_SOCK = $self->{_que_r_sock}; my $_QUE_W_SOCK = $self->{_que_w_sock}; my $_bounds_only = $self->{bounds_only} || 0; my $_chunk_size = $self->{chunk_size}; my $_wuf = $self->{_wuf}; my ($_next, $_chunk_id, $_seq_n, $_begin, $_end, $_step, $_fmt); my ($_abort, $_offset); if (ref $self->{sequence} eq 'ARRAY') { ($_begin, $_end, $_step, $_fmt) = @{ $self->{sequence} }; } else { $_begin = $self->{sequence}->{begin}; $_end = $self->{sequence}->{end}; $_step = $self->{sequence}->{step}; $_fmt = $self->{sequence}->{format}; } $_abort = $self->{_abort_msg}; $_chunk_id = $_offset = 0; $_fmt =~ s/%// if (defined $_fmt); ## ------------------------------------------------------------------------- $self->{_next_jmp} = sub { goto _WORKER_SEQUENCE__NEXT; }; $self->{_last_jmp} = sub { goto _WORKER_SEQUENCE__LAST; }; local $_; _WORKER_SEQUENCE__NEXT: while (1) { ## Obtain the next chunk_id and sequence number. sysread $_QUE_R_SOCK, $_next, $_que_read_size; ($_chunk_id, $_offset) = unpack($_que_template, $_next); if ($_offset >= $_abort) { syswrite $_QUE_W_SOCK, pack($_que_template, 0, $_offset); return; } syswrite $_QUE_W_SOCK, pack($_que_template, $_chunk_id + 1, $_offset + 1); $_chunk_id++; ## Call user function. if ($_chunk_size == 1) { $_ = $_offset * $_step + $_begin; $_ = sprintf("%$_fmt", $_) if (defined $_fmt); $_wuf->($self, $_, $_chunk_id); } else { my $_n_begin = ($_offset * $_chunk_size) * $_step + $_begin; my @_n = (); $_seq_n = $_n_begin; ## ------------------------------------------------------------------- if ($_bounds_only) { my $_tmp_b = $_seq_n; my $_tmp_e; if ($_begin < $_end) { if ($_step * $_chunk_size + $_n_begin <= $_end) { $_tmp_e = $_step * ($_chunk_size - 1) + $_n_begin; } else { my $_start = int( ($_end - $_seq_n) / $_chunk_size / $_step * $_chunk_size ); $_start = 1 if ($_start < 1); for my $_i ($_start .. $_chunk_size) { last if ($_seq_n > $_end); $_tmp_e = $_seq_n; $_seq_n = $_step * $_i + $_n_begin; } } } else { if ($_step * $_chunk_size + $_n_begin >= $_end) { $_tmp_e = $_step * ($_chunk_size - 1) + $_n_begin; } else { my $_start = int( ($_seq_n - $_end) / $_chunk_size / $_step * $_chunk_size ); $_start = 1 if ($_start < 1); for my $_i ($_start .. $_chunk_size) { last if ($_seq_n < $_end); $_tmp_e = $_seq_n; $_seq_n = $_step * $_i + $_n_begin; } } } if (defined $_fmt) { @_n = (sprintf("%$_fmt", $_tmp_b), sprintf("%$_fmt", $_tmp_e)); } else { @_n = ($_tmp_b, $_tmp_e); } } ## ------------------------------------------------------------------- else { if ($_begin < $_end) { if (!defined $_fmt && $_step == 1) { $_ = ($_seq_n + $_chunk_size <= $_end) ? [ $_seq_n .. $_seq_n + $_chunk_size - 1 ] : [ $_seq_n .. $_end ]; $_wuf->($self, $_, $_chunk_id); next; } else { for my $_i (1 .. $_chunk_size) { last if ($_seq_n > $_end); push @_n, (defined $_fmt) ? sprintf("%$_fmt", $_seq_n) : $_seq_n; $_seq_n = $_step * $_i + $_n_begin; } } } else { for my $_i (1 .. $_chunk_size) { last if ($_seq_n < $_end); push @_n, (defined $_fmt) ? sprintf("%$_fmt", $_seq_n) : $_seq_n; $_seq_n = $_step * $_i + $_n_begin; } } } ## ------------------------------------------------------------------- $_ = \@_n; $_wuf->($self, \@_n, $_chunk_id); } } _WORKER_SEQUENCE__LAST: return; } 1; MCE-1.608/lib/MCE/Core/Input/Request.pm0000644000076400007640000001125412511672747016312 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Input::Request - Array_ref and Glob_ref input reader. ## ## This package provides the request chunk method used internally by the worker ## process. Distribution follows a bank-queuing model. ## ## There is no public API. ## ############################################################################### package MCE::Core::Input::Request; use strict; use warnings; our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- Request chunk. ## ############################################################################### sub _worker_request_chunk { my ($self, $_proc_type) = @_; @_ = (); _croak('MCE::_worker_request_chunk: (user_func) is not specified') unless (defined $self->{user_func}); my $_chn = $self->{_chn}; my $_DAT_LOCK = $self->{_dat_lock}; my $_DAT_W_SOCK = $self->{_dat_w_sock}->[0]; my $_DAU_W_SOCK = $self->{_dat_w_sock}->[$_chn]; my $_lock_chn = $self->{_lock_chn}; my $_single_dim = $self->{_single_dim}; my $_chunk_size = $self->{chunk_size}; my $_use_slurpio = $self->{use_slurpio}; my $_RS = $self->{RS} || $/; my $_RS_FLG = (!$_RS || $_RS ne $LF); my $_I_FLG = (!$/ || $/ ne $LF); my $_wuf = $self->{_wuf}; my ($_chunk_id, $_len, $_output_tag); my ($_chop_len, $_chop_str, $_p); if ($_proc_type == REQUEST_ARRAY) { $_output_tag = OUTPUT_A_ARY; $_chop_len = 0; } else { $_output_tag = OUTPUT_S_GLB; if (length $_RS > 1 && substr($_RS, 0, 1) eq "\n") { $_chop_str = substr($_RS, 1); $_chop_len = length $_chop_str; } else { $_chop_str = ''; $_chop_len = 0; } } ## ------------------------------------------------------------------------- $self->{_next_jmp} = sub { goto _WORKER_REQUEST_CHUNK__NEXT; }; $self->{_last_jmp} = sub { goto _WORKER_REQUEST_CHUNK__LAST; }; local $_; _WORKER_REQUEST_CHUNK__NEXT: while (1) { undef $_ if (length > MAX_CHUNK_SIZE); $_ = ''; ## Obtain the next chunk of data. { local $\ = undef if (defined $\); local $/ = $LF if ($_I_FLG); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_output_tag . $LF . $_chn . $LF; chomp($_len = <$_DAU_W_SOCK>); unless ($_len) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } chomp($_chunk_id = <$_DAU_W_SOCK>); if ($_chunk_id > 1 && $_chop_len) { $_p = $_chop_len; $_ = $_chop_str; } else { $_p = 0; } read $_DAU_W_SOCK, $_, $_len, $_p; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } ## Call user function. if ($_proc_type == REQUEST_ARRAY) { if ($_single_dim && $_chunk_size == 1) { $_wuf->($self, [ $_ ], $_chunk_id); } else { my $_chunk_ref = $self->{thaw}($_); undef $_; $_ = ($_chunk_size == 1) ? $_chunk_ref->[0] : $_chunk_ref; $_wuf->($self, $_chunk_ref, $_chunk_id); } } else { if ($_use_slurpio) { if ($_chop_len && substr($_, -$_chop_len) eq $_chop_str) { substr($_, -$_chop_len, $_chop_len, ''); } local $_ = \$_; $_wuf->($self, $_, $_chunk_id); } else { if ($_chunk_size == 1) { if ($_chop_len && substr($_, -$_chop_len) eq $_chop_str) { substr($_, -$_chop_len, $_chop_len, ''); } $_wuf->($self, [ $_ ], $_chunk_id); } else { my @_recs; { local $/ = $_RS if ($_RS_FLG); _sync_buffer_to_array(\$_, \@_recs, $_chop_str); undef $_; } if ($_chop_len) { for my $i (0 .. @_recs - 1) { if (substr($_recs[$i], -$_chop_len) eq $_chop_str) { substr($_recs[$i], -$_chop_len, $_chop_len, ''); } } } local $_ = \@_recs; $_wuf->($self, \@_recs, $_chunk_id); } } } } _WORKER_REQUEST_CHUNK__LAST: return; } 1; MCE-1.608/lib/MCE/Core/Manager.pm0000644000076400007640000006107012511672721015126 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Manager - Core methods for the manager process. ## ## This package provides the loop and relevant methods used internally by the ## manager process. ## ## There is no public API. ## ############################################################################### package MCE::Core::Manager; use strict; use warnings; ## no critic (TestingAndDebugging::ProhibitNoStrict) our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use bytes; ############################################################################### ## ---------------------------------------------------------------------------- ## Call on task_end after task completion. ## ############################################################################### sub _task_end { my ($self, $_task_id) = @_; @_ = (); if (defined $self->{user_tasks}) { my $_task_end = (exists $self->{user_tasks}->[$_task_id]->{task_end}) ? $self->{user_tasks}->[$_task_id]->{task_end} : $self->{task_end}; if (defined $_task_end) { my $_task_name = (exists $self->{user_tasks}->[$_task_id]->{task_name}) ? $self->{user_tasks}->[$_task_id]->{task_name} : $self->{task_name}; $_task_end->($self, $_task_id, $_task_name); } } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Process output. ## ## Awaits and processes events from workers. The sendto/do methods tag the ## output accordingly. The hash structure below is key-driven. ## ############################################################################### sub _output_loop { my ( $self, $_input_data, $_input_glob, $_plugin_function, $_plugin_loop_begin, $_plugin_loop_end ) = @_; @_ = (); my ( $_aborted, $_eof_flag, $_syn_flag, %_sendto_fhs, $_want_id, $_callback, $_chunk_id, $_chunk_size, $_fd, $_file, $_flush_file, @_is_c_ref, @_is_h_ref, @_is_q_ref, $_on_post_exit, $_on_post_run, $_has_user_tasks, $_sess_dir, $_task_id, $_user_error, $_user_output, $_input_size, $_offset_pos, $_single_dim, @_gather, $_cs_one_flag, $_exit_id, $_exit_pid, $_exit_status, $_exit_wid, $_len, $_sync_cnt, $_BSB_W_SOCK, $_BSE_W_SOCK, $_DAT_R_SOCK, $_DAU_R_SOCK, $_MCE_STDERR, $_I_FLG, $_O_FLG, $_I_SEP, $_O_SEP, $_RS, $_RS_FLG, $_MCE_STDOUT ); ## ------------------------------------------------------------------------- ## Callback return. my $_cb_ret_a = sub { ## CBK return array my $_buf = $self->{freeze}($_[0]); $_len = length $_buf; local $\ = undef if (defined $\); if ($_len < FAST_SEND_SIZE) { print {$_DAU_R_SOCK} $_len . $LF . $_buf; } else { print {$_DAU_R_SOCK} $_len . $LF; print {$_DAU_R_SOCK} $_buf; } return; }; my $_cb_ret_r = sub { ## CBK return reference my $_buf = $self->{freeze}($_[0]); $_len = length $_buf; local $\ = undef if (defined $\); if ($_len < FAST_SEND_SIZE) { print {$_DAU_R_SOCK} WANTS_REF . $LF . $_len . $LF . $_buf; } else { print {$_DAU_R_SOCK} WANTS_REF . $LF . $_len . $LF; print {$_DAU_R_SOCK} $_buf; } return; }; my $_cb_ret_s = sub { ## CBK return scalar $_len = (defined $_[0]) ? length $_[0] : -1; local $\ = undef if (defined $\); if ($_len < FAST_SEND_SIZE) { print {$_DAU_R_SOCK} WANTS_SCALAR . $LF . $_len . $LF . $_[0]; } else { print {$_DAU_R_SOCK} WANTS_SCALAR . $LF . $_len . $LF; print {$_DAU_R_SOCK} $_[0]; } return; }; ## ------------------------------------------------------------------------- ## Create hash structure containing various output functions. my %_core_output_function = ( OUTPUT_W_ABT.$LF => sub { ## Worker has aborted $_aborted = 1; return; }, OUTPUT_W_DNE.$LF => sub { ## Worker has completed chomp($_task_id = <$_DAU_R_SOCK>); $self->{_total_running} -= 1; if ($_has_user_tasks && $_task_id >= 0) { $self->{_task}->[$_task_id]->{_total_running} -= 1; } my $_total_running = ($_has_user_tasks) ? $self->{_task}->[$_task_id]->{_total_running} : $self->{_total_running}; if ($_task_id == 0 && defined $_syn_flag && $_sync_cnt) { if ($_sync_cnt == $_total_running) { syswrite $_BSB_W_SOCK, $LF for (1 .. $_total_running); undef $_syn_flag; } } _task_end($self, $_task_id) unless ($_total_running); return; }, ## ---------------------------------------------------------------------- OUTPUT_W_EXT.$LF => sub { ## Worker has exited chomp($_task_id = <$_DAU_R_SOCK>); $self->{_total_exited} += 1; $self->{_total_running} -= 1; $self->{_total_workers} -= 1; if ($_has_user_tasks && $_task_id >= 0) { $self->{_task}->[$_task_id]->{_total_running} -= 1; $self->{_task}->[$_task_id]->{_total_workers} -= 1; } my $_total_running = ($_has_user_tasks) ? $self->{_task}->[$_task_id]->{_total_running} : $self->{_total_running}; if ($_task_id == 0 && defined $_syn_flag && $_sync_cnt) { if ($_sync_cnt == $_total_running) { syswrite $_BSB_W_SOCK, $LF for (1 .. $_total_running); undef $_syn_flag; } } my $_exit_msg = ''; chomp($_exit_wid = <$_DAU_R_SOCK>); chomp($_exit_pid = <$_DAU_R_SOCK>); chomp($_exit_status = <$_DAU_R_SOCK>); chomp($_exit_id = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read($_DAU_R_SOCK, $_exit_msg, $_len) if ($_len); if (abs($_exit_status) > abs($self->{_wrk_status})) { $self->{_wrk_status} = $_exit_status; } ## Reap child/thread. Note: Win32 uses negative PIDs. if ($_exit_pid =~ /^PID_(-?\d+)/) { my $_pid = $1; my $_list = $self->{_pids}; for my $i (0 .. @{ $_list }) { if ($_list->[$i] && $_list->[$i] == $_pid) { waitpid $_pid, 0; $self->{_pids}->[$i] = undef; last; } } } elsif ($_exit_pid =~ /^TID_(\d+)/) { my $_tid = $1; my $_list = $self->{_tids}; for my $i (0 .. @{ $_list }) { if ($_list->[$i] && $_list->[$i] == $_tid) { ${ $self->{_thrs}->[$i] }->join(); $self->{_thrs}->[$i] = undef; $self->{_tids}->[$i] = undef; last; } } } ## Call on_post_exit callback if defined. Otherwise, append status ## information if on_post_run is defined for later retrieval. if (defined $_on_post_exit) { $self->{_exited_wid} = $_exit_wid; $_on_post_exit->($self, { wid => $_exit_wid, pid => $_exit_pid, status => $_exit_status, msg => $_exit_msg, id => $_exit_id }); delete $self->{_exited_wid}; } elsif (defined $_on_post_run) { push @{ $self->{_status} }, { wid => $_exit_wid, pid => $_exit_pid, status => $_exit_status, msg => $_exit_msg, id => $_exit_id }; } _task_end($self, $_task_id) unless ($_total_running); return; }, ## ---------------------------------------------------------------------- OUTPUT_A_ARY.$LF => sub { ## Array << Array my $_buf; if ($_offset_pos >= $_input_size || $_aborted) { local $\ = undef if (defined $\); print {$_DAU_R_SOCK} '0' . $LF; return; } if ($_single_dim && $_cs_one_flag) { $_buf = $_input_data->[$_offset_pos]; } else { if ($_offset_pos + $_chunk_size - 1 < $_input_size) { $_buf = $self->{freeze}( [ @{ $_input_data }[ $_offset_pos .. $_offset_pos + $_chunk_size - 1 ] ] ); } else { $_buf = $self->{freeze}( [ @{ $_input_data }[ $_offset_pos .. $_input_size - 1 ] ] ); } } $_len = length $_buf; local $\ = undef if (defined $\); if ($_len < FAST_SEND_SIZE) { print {$_DAU_R_SOCK} $_len . $LF . (++$_chunk_id) . $LF . $_buf; } else { print {$_DAU_R_SOCK} $_len . $LF . (++$_chunk_id) . $LF; print {$_DAU_R_SOCK} $_buf; } $_offset_pos += $_chunk_size; return; }, OUTPUT_S_GLB.$LF => sub { ## Scalar << Glob FH my $_buf = ''; ## The logic below honors ('Ctrl/Z' in Windows, 'Ctrl/D' in Unix) ## when reading from standard input. No output will be lost as ## far as what was previously read into the buffer. if ($_eof_flag || $_aborted) { local $\ = undef if (defined $\); print {$_DAU_R_SOCK} '0' . $LF; return; } { local $/ = $_RS if ($_RS_FLG); if ($_chunk_size <= MAX_RECS_SIZE) { if ($_chunk_size == 1) { $_buf = <$_input_glob>; $_eof_flag = 1 unless (length $_buf); } else { my $_last_len = 0; for (1 .. $_chunk_size) { $_buf .= <$_input_glob>; $_len = length $_buf; if ($_len == $_last_len) { $_eof_flag = 1; last; } $_last_len = $_len; } } } else { if (read($_input_glob, $_buf, $_chunk_size) == $_chunk_size) { $_buf .= <$_input_glob>; $_eof_flag = 1 if (length $_buf == $_chunk_size); } else { $_eof_flag = 1; } } } $_len = length $_buf; local $\ = undef if (defined $\); if ($_len) { if ($_len < FAST_SEND_SIZE) { print {$_DAU_R_SOCK} $_len . $LF . (++$_chunk_id) . $LF . $_buf; } else { print {$_DAU_R_SOCK} $_len . $LF . (++$_chunk_id) . $LF; print {$_DAU_R_SOCK} $_buf; } } else { print {$_DAU_R_SOCK} '0' . $LF; } return; }, OUTPUT_U_ITR.$LF => sub { ## User << Iterator my $_buf; if ($_aborted) { local $\ = undef if (defined $\); print {$_DAU_R_SOCK} '-1' . $LF; return; } my @_ret_a = $_input_data->($_chunk_size); if (scalar @_ret_a > 1 || ref $_ret_a[0]) { $_buf = $self->{freeze}( [ @_ret_a ] ); $_len = length $_buf; local $\ = undef if (defined $\); if ($_len < FAST_SEND_SIZE) { print {$_DAU_R_SOCK} $_len . '1' . $LF . (++$_chunk_id) . $LF . $_buf; } else { print {$_DAU_R_SOCK} $_len . '1' . $LF . (++$_chunk_id) . $LF; print {$_DAU_R_SOCK} $_buf; } return; } elsif (defined $_ret_a[0]) { $_len = length $_ret_a[0]; local $\ = undef if (defined $\); if ($_len < FAST_SEND_SIZE) { print {$_DAU_R_SOCK} $_len . '0' . $LF . (++$_chunk_id) . $LF . $_ret_a[0]; } else { print {$_DAU_R_SOCK} $_len . '0' . $LF . (++$_chunk_id) . $LF; print {$_DAU_R_SOCK} $_ret_a[0]; } return; } local $\ = undef if (defined $\); print {$_DAU_R_SOCK} '-1' . $LF; $_aborted = 1; return; }, ## ---------------------------------------------------------------------- OUTPUT_A_CBK.$LF => sub { ## Callback w/ multiple args my ($_buf, $_data_ref); chomp($_want_id = <$_DAU_R_SOCK>); chomp($_callback = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_data_ref = $self->{thaw}($_buf); undef $_buf; local $\ = $_O_SEP if ($_O_FLG); local $/ = $_I_SEP if ($_I_FLG); no strict 'refs'; if ($_want_id == WANTS_UNDEF) { $_callback->(@{ $_data_ref }); } elsif ($_want_id == WANTS_ARRAY) { my @_ret_a = $_callback->(@{ $_data_ref }); $_cb_ret_a->(\@_ret_a); } else { my $_ret_s = $_callback->(@{ $_data_ref }); ref $_ret_s ? $_cb_ret_r->($_ret_s) : $_cb_ret_s->($_ret_s); } return; }, OUTPUT_S_CBK.$LF => sub { ## Callback w/ 1 scalar arg my $_buf; chomp($_want_id = <$_DAU_R_SOCK>); chomp($_callback = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; local $\ = $_O_SEP if ($_O_FLG); local $/ = $_I_SEP if ($_I_FLG); no strict 'refs'; if ($_want_id == WANTS_UNDEF) { $_callback->($_buf); } elsif ($_want_id == WANTS_ARRAY) { my @_ret_a = $_callback->($_buf); $_cb_ret_a->(\@_ret_a); } else { my $_ret_s = $_callback->($_buf); ref $_ret_s ? $_cb_ret_r->($_ret_s) : $_cb_ret_s->($_ret_s); } return; }, OUTPUT_N_CBK.$LF => sub { ## Callback w/ no args chomp($_want_id = <$_DAU_R_SOCK>); chomp($_callback = <$_DAU_R_SOCK>); local $\ = $_O_SEP if ($_O_FLG); local $/ = $_I_SEP if ($_I_FLG); no strict 'refs'; if ($_want_id == WANTS_UNDEF) { $_callback->(); } elsif ($_want_id == WANTS_ARRAY) { my @_ret_a = $_callback->(); $_cb_ret_a->(\@_ret_a); } else { my $_ret_s = $_callback->(); ref $_ret_s ? $_cb_ret_r->($_ret_s) : $_cb_ret_s->($_ret_s); } return; }, ## ---------------------------------------------------------------------- OUTPUT_A_GTR.$LF => sub { ## Gather w/ multiple args my $_buf; chomp($_task_id = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; if ($_is_c_ref[$_task_id]) { local $_ = $self->{thaw}($_buf); $_gather[$_task_id]->(@{ $_ }); } elsif ($_is_h_ref[$_task_id]) { local $_ = $self->{thaw}($_buf); while (1) { my $_key = shift @{ $_ }; my $_val = shift @{ $_ }; $_gather[$_task_id]->{$_key} = $_val; last unless (@{ $_ }); } } elsif ($_is_q_ref[$_task_id]) { $_gather[$_task_id]->enqueue(@{ $self->{thaw}($_buf) }); } else { push @{ $_gather[$_task_id] }, @{ $self->{thaw}($_buf) }; } return; }, OUTPUT_R_GTR.$LF => sub { ## Gather w/ 1 reference arg my $_buf; chomp($_task_id = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; if ($_is_c_ref[$_task_id]) { local $_ = $self->{thaw}($_buf); $_gather[$_task_id]->($_); } elsif ($_is_h_ref[$_task_id]) { local $_ = $self->{thaw}($_buf); $_gather[$_task_id]->{$_} = undef; } elsif ($_is_q_ref[$_task_id]) { $_gather[$_task_id]->enqueue($self->{thaw}($_buf)); } else { push @{ $_gather[$_task_id] }, $self->{thaw}($_buf); } return; }, OUTPUT_S_GTR.$LF => sub { ## Gather w/ 1 scalar arg local $_; chomp($_task_id = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_, $_len if ($_len >= 0); if ($_is_c_ref[$_task_id]) { $_gather[$_task_id]->($_); } elsif ($_is_h_ref[$_task_id]) { $_gather[$_task_id]->{$_} = undef; } elsif ($_is_q_ref[$_task_id]) { $_gather[$_task_id]->enqueue($_); } else { push @{ $_gather[$_task_id] }, $_; } return; }, ## ---------------------------------------------------------------------- OUTPUT_O_SND.$LF => sub { ## Send >> STDOUT my $_buf; chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; if (defined $_user_output) { $_user_output->($_buf); } else { print {$_MCE_STDOUT} $_buf; } return; }, OUTPUT_E_SND.$LF => sub { ## Send >> STDERR my $_buf; chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; if (defined $_user_error) { $_user_error->($_buf); } else { print {$_MCE_STDERR} $_buf; } return; }, OUTPUT_F_SND.$LF => sub { ## Send >> File my ($_buf, $_OUT_FILE); chomp($_file = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; unless (exists $_sendto_fhs{$_file}) { open $_sendto_fhs{$_file}, '>>', $_file or _croak "Cannot open file for writing ($_file): $!"; binmode $_sendto_fhs{$_file}; ## Select new FH, turn on autoflush, restore the old FH. if ($_flush_file) { select((select($_sendto_fhs{$_file}), $| = 1)[0]); } } $_OUT_FILE = $_sendto_fhs{$_file}; print {$_OUT_FILE} $_buf; return; }, OUTPUT_D_SND.$LF => sub { ## Send >> File descriptor my ($_buf, $_OUT_FILE); chomp($_fd = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; unless (exists $_sendto_fhs{$_fd}) { require IO::Handle unless (defined $IO::Handle::VERSION); $_sendto_fhs{$_fd} = IO::Handle->new(); $_sendto_fhs{$_fd}->fdopen($_fd, 'w') or _croak "Cannot open file descriptor ($_fd): $!"; binmode $_sendto_fhs{$_fd}; ## Select new FH, turn on autoflush, restore the old FH. if ($_flush_file) { select((select($_sendto_fhs{$_fd}), $| = 1)[0]); } } $_OUT_FILE = $_sendto_fhs{$_fd}; print {$_OUT_FILE} $_buf; return; }, ## ---------------------------------------------------------------------- OUTPUT_B_SYN.$LF => sub { ## Barrier sync - begin if (!defined $_sync_cnt || $_sync_cnt == 0) { $_syn_flag = 1; $_sync_cnt = 0; } my $_total_running = ($_has_user_tasks) ? $self->{_task}->[0]->{_total_running} : $self->{_total_running}; if (++$_sync_cnt == $_total_running) { syswrite $_BSB_W_SOCK, $LF for (1 .. $_total_running); undef $_syn_flag; } return; }, OUTPUT_E_SYN.$LF => sub { ## Barrier sync - end if (--$_sync_cnt == 0) { my $_total_running = ($_has_user_tasks) ? $self->{_task}->[0]->{_total_running} : $self->{_total_running}; syswrite $_BSE_W_SOCK, $LF for (1 .. $_total_running); } return; }, ); ## ------------------------------------------------------------------------- local ($!, $_); $_has_user_tasks = (defined $self->{user_tasks}) ? 1 : 0; $_cs_one_flag = ($self->{chunk_size} == 1) ? 1 : 0; $_aborted = $_chunk_id = $_eof_flag = 0; $_on_post_exit = $self->{on_post_exit}; $_on_post_run = $self->{on_post_run}; $_chunk_size = $self->{chunk_size}; $_flush_file = $self->{flush_file}; $_user_output = $self->{user_output}; $_user_error = $self->{user_error}; $_single_dim = $self->{_single_dim}; $_sess_dir = $self->{_sess_dir}; if ($_has_user_tasks && $self->{user_tasks}->[0]->{chunk_size}) { $_chunk_size = $self->{user_tasks}->[0]->{chunk_size}; } if ($_has_user_tasks) { for my $_i (0 .. @{ $self->{user_tasks} } - 1) { $_gather[$_i] = (defined $self->{user_tasks}->[$_i]->{gather}) ? $self->{user_tasks}->[$_i]->{gather} : $self->{gather}; $_is_c_ref[$_i] = ( ref $_gather[$_i] eq 'CODE' ) ? 1 : 0; $_is_h_ref[$_i] = ( ref $_gather[$_i] eq 'HASH' ) ? 1 : 0; $_is_q_ref[$_i] = ( ref $_gather[$_i] eq 'MCE::Queue' || ref $_gather[$_i] eq 'Thread::Queue' ) ? 1 : 0; } } if (defined $self->{gather}) { $_gather[0] = $self->{gather}; $_is_c_ref[0] = ( ref $_gather[0] eq 'CODE' ) ? 1 : 0; $_is_h_ref[0] = ( ref $_gather[0] eq 'HASH' ) ? 1 : 0; $_is_q_ref[0] = ( ref $_gather[0] eq 'MCE::Queue' || ref $_gather[0] eq 'Thread::Queue' ) ? 1 : 0; } if (defined $_input_data && ref $_input_data eq 'ARRAY') { $_input_size = @{ $_input_data }; $_offset_pos = 0; } else { $_input_size = $_offset_pos = 0; } ## Set STDOUT/STDERR to user parameters. if (defined $self->{stdout_file}) { open $_MCE_STDOUT, '>>', $self->{stdout_file} or die $self->{stdout_file} . ": $!\n"; binmode $_MCE_STDOUT; } else { open $_MCE_STDOUT, '>&=STDOUT'; binmode $_MCE_STDOUT; } if (defined $self->{stderr_file}) { open $_MCE_STDERR, '>>', $self->{stderr_file} or die $self->{stderr_file} . ": $!\n"; binmode $_MCE_STDERR; } else { open $_MCE_STDERR, '>&=STDERR'; binmode $_MCE_STDERR; } ## Make MCE_STDOUT the default handle. ## Flush STDERR/STDOUT handles if requested. my $_old_hndl = select $_MCE_STDOUT; $| = 1 if ($self->{flush_stdout}); if ($self->{flush_stderr}) { select $_MCE_STDERR; $| = 1; select $_MCE_STDOUT; } ## ------------------------------------------------------------------------- ## Output event loop. my $_func; my $_channels = $self->{_dat_r_sock}; $_BSB_W_SOCK = $self->{_bsb_w_sock}; ## For IPC $_BSE_W_SOCK = $self->{_bse_w_sock}; $_DAT_R_SOCK = $self->{_dat_r_sock}->[0]; $_RS = $self->{RS} || $/; $_O_SEP = $\; local $\ = undef; $_I_SEP = $/; local $/ = $LF; $_RS_FLG = (!$_RS || $_RS ne $LF) ? 1 : 0; $_O_FLG = (defined $_O_SEP) ? 1 : 0; $_I_FLG = (!$_I_SEP || $_I_SEP ne $LF) ? 1 : 0; ## Call module's loop_begin routine for modules plugged into MCE. for my $_p (@{ $_plugin_loop_begin }) { $_p->($self, \$_DAU_R_SOCK); } ## Call on hash function. Exit loop when workers have completed. while (1) { $_func = <$_DAT_R_SOCK>; next unless (defined $_func); $_DAU_R_SOCK = $_channels->[ <$_DAT_R_SOCK> ]; if (exists $_core_output_function{$_func}) { $_core_output_function{$_func}(); } elsif (exists $_plugin_function->{$_func}) { $_plugin_function->{$_func}(); } last unless ($self->{_total_running}); } ## Call module's loop_end routine for modules plugged into MCE. for my $_p (@{ $_plugin_loop_end }) { $_p->($self); } ## Call on_post_run callback. $_on_post_run->($self, $self->{_status}) if (defined $_on_post_run); ## Close opened sendto file handles. for my $_p (keys %_sendto_fhs) { close $_sendto_fhs{$_p}; undef $_sendto_fhs{$_p}; delete $_sendto_fhs{$_p}; } ## Restore the default handle. Close MCE STDOUT/STDERR handles. select $_old_hndl; close $_MCE_STDOUT; undef $_MCE_STDOUT; close $_MCE_STDERR; undef $_MCE_STDERR; return; } 1; MCE-1.608/lib/MCE/Core/Worker.pm0000644000076400007640000005206512511672735015036 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Core::Worker - Core methods for the worker process. ## ## This package provides main, loop, and relevant methods used internally by ## the worker process. ## ## There is no public API. ## ############################################################################### package MCE::Core::Worker; use strict; use warnings; our $VERSION = '1.608'; ## Items below are folded into MCE. package MCE; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use bytes; ############################################################################### ## ---------------------------------------------------------------------------- ## Internal do, gather and send related functions for serializing data to ## destination. User functions for handling gather, queue or void. ## ############################################################################### { my ( $_dest, $_len, $_tag, $_task_id, $_user_func, $_value, $_want_id, $_DAT_LOCK, $_DAT_W_SOCK, $_DAU_W_SOCK, $_lock_chn, $_chn ); ## Create array structure containing various send functions. my @_dest_function = (); $_dest_function[SENDTO_FILEV2] = sub { ## Content >> File return unless (defined $_value); local $\ = undef if (defined $\); if (length ${ $_[0] }) { flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_F_SND . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_value . $LF . length(${ $_[0] }) . $LF; print {$_DAU_W_SOCK} ${ $_[0] }; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return; }; $_dest_function[SENDTO_FD] = sub { ## Content >> File descriptor return unless (defined $_value); local $\ = undef if (defined $\); if (length ${ $_[0] }) { flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_D_SND . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_value . $LF . length(${ $_[0] }) . $LF; print {$_DAU_W_SOCK} ${ $_[0] }; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return; }; $_dest_function[SENDTO_STDOUT] = sub { ## Content >> STDOUT local $\ = undef if (defined $\); if (length ${ $_[0] }) { flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_O_SND . $LF . $_chn . $LF; print {$_DAU_W_SOCK} length(${ $_[0] }) . $LF; print {$_DAU_W_SOCK} ${ $_[0] }; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return; }; $_dest_function[SENDTO_STDERR] = sub { ## Content >> STDERR local $\ = undef if (defined $\); if (length ${ $_[0] }) { flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_E_SND . $LF . $_chn . $LF; print {$_DAU_W_SOCK} length(${ $_[0] }) . $LF; print {$_DAU_W_SOCK} ${ $_[0] }; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return; }; ## ------------------------------------------------------------------------- sub _do_callback { my ($self, $_buf, $_aref); ($self, $_value, $_aref) = @_; unless (defined wantarray) { $_want_id = WANTS_UNDEF; } elsif (wantarray) { $_want_id = WANTS_ARRAY; } else { $_want_id = WANTS_SCALAR; } ## Crossover: Send arguments if (scalar @{ $_aref } > 0) { ## Multiple Args >> Callback if (scalar @{ $_aref } > 1 || ref $_aref->[0]) { $_tag = OUTPUT_A_CBK; $_buf = $self->{freeze}($_aref); $_len = length $_buf; local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_tag . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_want_id . $LF . $_value . $LF . $_len . $LF; print {$_DAU_W_SOCK} $_buf; } else { ## Scalar >> Callback $_tag = OUTPUT_S_CBK; $_len = length $_aref->[0]; local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_tag . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_want_id . $LF . $_value . $LF . $_len . $LF; print {$_DAU_W_SOCK} $_aref->[0]; } } else { ## No Args >> Callback $_tag = OUTPUT_N_CBK; local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_tag . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_want_id . $LF . $_value . $LF; } ## Crossover: Receive return value if ($_want_id == WANTS_UNDEF) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } elsif ($_want_id == WANTS_ARRAY) { local $/ = $LF if (!$/ || $/ ne $LF); chomp($_len = <$_DAU_W_SOCK>); read($_DAU_W_SOCK, $_buf, $_len || 0); flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return @{ $self->{thaw}($_buf) }; } else { local $/ = $LF if (!$/ || $/ ne $LF); chomp($_want_id = <$_DAU_W_SOCK>); chomp($_len = <$_DAU_W_SOCK>); if ($_len >= 0) { read($_DAU_W_SOCK, $_buf, $_len || 0); flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return $_buf if ($_want_id == WANTS_SCALAR); return $self->{thaw}($_buf); } else { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } } } ## ------------------------------------------------------------------------- sub _do_gather { my $_buf; my ($self, $_aref) = @_; return unless (scalar @{ $_aref }); if (scalar @{ $_aref } > 1) { $_tag = OUTPUT_A_GTR; $_buf = $self->{freeze}($_aref); $_len = length $_buf; } elsif (ref $_aref->[0]) { $_tag = OUTPUT_R_GTR; $_buf = $self->{freeze}($_aref->[0]); $_len = length $_buf; } else { $_tag = OUTPUT_S_GTR; if (defined $_aref->[0]) { $_len = length $_aref->[0]; local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_tag . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_task_id . $LF . $_len . $LF; print {$_DAU_W_SOCK} $_aref->[0]; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } else { $_buf = ''; $_len = -1; } } local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_tag . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_task_id . $LF . $_len . $LF; print {$_DAU_W_SOCK} $_buf if (length $_buf); flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } ## ------------------------------------------------------------------------- sub _do_send { my $_data_ref; my $self = shift; $_dest = shift; $_value = shift; if (scalar @_ > 1) { $_data_ref = \join('', @_); } elsif (my $_ref = ref $_[0]) { if ($_ref eq 'SCALAR') { $_data_ref = $_[0]; } elsif ($_ref eq 'ARRAY') { $_data_ref = \join('', @{ $_[0] }); } elsif ($_ref eq 'HASH') { $_data_ref = \join('', %{ $_[0] }); } else { $_data_ref = \join('', @_); } } else { $_data_ref = \$_[0]; } $_dest_function[$_dest]($_data_ref); return; } sub _do_send_glob { my ($self, $_glob, $_fd, $_data_ref) = @_; if ($self->{_wid} > 0) { if ($_fd == 1) { _do_send($self, SENDTO_STDOUT, undef, $_data_ref); } elsif ($_fd == 2) { _do_send($self, SENDTO_STDERR, undef, $_data_ref); } else { _do_send($self, SENDTO_FD, $_fd, $_data_ref); } } else { my $_fh = qualify_to_ref($_glob, caller); local $\ = undef if (defined $\); print {$_fh} ${ $_data_ref }; } return; } ## ------------------------------------------------------------------------- sub _do_send_init { my ($self) = @_; $_chn = $self->{_chn}; $_DAT_LOCK = $self->{_dat_lock}; $_DAT_W_SOCK = $self->{_dat_w_sock}->[0]; $_DAU_W_SOCK = $self->{_dat_w_sock}->[$_chn]; $_lock_chn = $self->{_lock_chn}; $_task_id = $self->{_task_id}; local ($!, $@); eval { select STDERR; $| = 1 }; eval { select STDOUT; $| = 1 }; return; } sub _do_send_clear { my ($self) = @_; $_dest = $_len = $_task_id = $_user_func = $_value = $_want_id = undef; $_DAT_LOCK = $_DAT_W_SOCK = $_DAU_W_SOCK = $_lock_chn = $_chn = undef; $_tag = undef; return; } ## ------------------------------------------------------------------------- sub _do_user_func { my ($self, $_chunk, $_chunk_id) = @_; $self->{_chunk_id} = $_chunk_id; $_user_func->($self, $_chunk, $_chunk_id); return; } sub _do_user_func_init { my ($self) = @_; $_user_func = $self->{user_func}; return; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- Do. ## ############################################################################### sub _worker_do { my ($self, $_params_ref) = @_; @_ = (); ## Set options. $self->{_running} = 1; $self->{_abort_msg} = $_params_ref->{_abort_msg}; $self->{_run_mode} = $_params_ref->{_run_mode}; $self->{_single_dim} = $_params_ref->{_single_dim}; $self->{use_slurpio} = $_params_ref->{_use_slurpio}; $self->{parallel_io} = $_params_ref->{_parallel_io}; $self->{RS} = $_params_ref->{_RS}; _do_user_func_init($self); ## Init local vars. my $_chn = $self->{_chn}; my $_DAT_LOCK = $self->{_dat_lock}; my $_DAT_W_SOCK = $self->{_dat_w_sock}->[0]; my $_DAU_W_SOCK = $self->{_dat_w_sock}->[$_chn]; my $_lock_chn = $self->{_lock_chn}; my $_run_mode = $self->{_run_mode}; my $_task_id = $self->{_task_id}; my $_task_name = $self->{task_name}; ## Do not override params if defined in user_tasks during instantiation. for my $_p (qw(bounds_only chunk_size interval sequence user_args)) { if (defined $_params_ref->{"_${_p}"}) { $self->{$_p} = $_params_ref->{"_${_p}"} unless (defined $self->{_task}->{$_p}); } } ## Assign user function. $self->{_wuf} = \&_do_user_func; ## Set time_block & start_time values for interval. if (defined $self->{interval}) { my $_i = $self->{interval}; my $_delay = $_i->{delay} * $_i->{max_nodes}; $self->{_i_app_tb} = $_delay * $self->{max_workers}; $self->{_i_app_st} = $_i->{_time} + ($_delay / $_i->{max_nodes} * $_i->{node_id}); $self->{_i_wrk_st} = ($self->{_task_wid} - 1) * $_delay + $self->{_i_app_st}; } ## Call user_begin if defined. if (defined $self->{user_begin}) { $self->{user_begin}($self, $_task_id, $_task_name); } ## Call worker function. if ($_run_mode eq 'sequence') { require MCE::Core::Input::Sequence unless (defined $MCE::Core::Input::Sequence::VERSION); _worker_sequence_queue($self); } elsif (defined $self->{_task}->{sequence}) { require MCE::Core::Input::Generator unless (defined $MCE::Core::Input::Generator::VERSION); _worker_sequence_generator($self); } elsif ($_run_mode eq 'array') { require MCE::Core::Input::Request unless (defined $MCE::Core::Input::Request::VERSION); _worker_request_chunk($self, REQUEST_ARRAY); } elsif ($_run_mode eq 'glob') { require MCE::Core::Input::Request unless (defined $MCE::Core::Input::Request::VERSION); _worker_request_chunk($self, REQUEST_GLOB); } elsif ($_run_mode eq 'iterator') { require MCE::Core::Input::Iterator unless (defined $MCE::Core::Input::Iterator::VERSION); _worker_user_iterator($self); } elsif ($_run_mode eq 'file') { require MCE::Core::Input::Handle unless (defined $MCE::Core::Input::Handle::VERSION); _worker_read_handle($self, READ_FILE, $_params_ref->{_input_file}); } elsif ($_run_mode eq 'memory') { require MCE::Core::Input::Handle unless (defined $MCE::Core::Input::Handle::VERSION); _worker_read_handle($self, READ_MEMORY, $self->{input_data}); } elsif (defined $self->{user_func}) { $self->{_chunk_id} = $self->{_task_wid}; $self->{user_func}->($self); } undef $self->{_next_jmp} if (defined $self->{_next_jmp}); undef $self->{_last_jmp} if (defined $self->{_last_jmp}); undef $self->{user_data} if (defined $self->{user_data}); ## Call user_end if defined. if (defined $self->{user_end}) { $self->{user_end}($self, $_task_id, $_task_name); } ## Notify the main process a worker has completed. local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); if (exists $self->{_rla_return}) { print {$_DAT_W_SOCK} OUTPUT_W_RLA . $LF . $_chn . $LF; print {$_DAU_W_SOCK} (delete $self->{_rla_return}) . $LF; } print {$_DAT_W_SOCK} OUTPUT_W_DNE . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_task_id . $LF; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); $self->{_running} = 0; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- Loop. ## ############################################################################### sub _worker_loop { my ($self) = @_; @_ = (); my ($_response, $_len, $_buf, $_params_ref); my $_COM_LOCK = $self->{_com_lock}; my $_COM_W_SOCK = $self->{_com_w_sock}; my $_job_delay = $self->{job_delay}; my $_wid = $self->{_wid}; while (1) { { local $\ = undef; local $/ = $LF; flock $_COM_LOCK, LOCK_EX; ## Wait until next job request. $_response = <$_COM_W_SOCK>; print {$_COM_W_SOCK} $_wid . $LF; last unless (defined $_response); chomp $_response; ## End loop if an invalid reply. last if ($_response !~ /\A(?:\d+|_data|_exit)\z/); if ($_response eq '_data') { ## Acquire and process user data. chomp($_len = <$_COM_W_SOCK>); read $_COM_W_SOCK, $_buf, $_len; print {$_COM_W_SOCK} $_wid . $LF; flock $_COM_LOCK, LOCK_UN; $self->{user_data} = $self->{thaw}($_buf); undef $_buf; if (defined $_job_delay && $_job_delay > 0.0) { sleep $_job_delay * $_wid; } _worker_do($self, { }); } else { ## Return to caller if instructed to exit. if ($_response eq '_exit') { flock $_COM_LOCK, LOCK_UN; return; } ## Retrieve params data. chomp($_len = <$_COM_W_SOCK>); read $_COM_W_SOCK, $_buf, $_len; print {$_COM_W_SOCK} $_wid . $LF; flock $_COM_LOCK, LOCK_UN; $_params_ref = $self->{thaw}($_buf); undef $_buf; } } ## Start over if the last response was for processing user data. next if ($_response eq '_data'); ## Wait until MCE completes params submission to all workers. sysread $self->{_bse_r_sock}, (my $_c), 1; if (defined $_job_delay && $_job_delay > 0.0) { sleep $_job_delay * $_wid; } _worker_do($self, $_params_ref); undef $_params_ref; } ## Notify the main process a worker has ended. The following is executed ## when an invalid reply was received above (not likely to occur). flock $_COM_LOCK, LOCK_UN; die "worker $self->{_wid} has ended prematurely"; } ############################################################################### ## ---------------------------------------------------------------------------- ## Worker process -- Main. ## ############################################################################### sub _worker_main { my ( $self, $_wid, $_task, $_task_id, $_task_wid, $_params, $_plugin_worker_init, $_is_winenv ) = @_; @_ = (); if (exists $self->{input_data}) { my $_ref = ref $self->{input_data}; delete $self->{input_data} if ($_ref && $_ref ne 'SCALAR'); } $self->{_task_id} = (defined $_task_id ) ? $_task_id : 0; $self->{_task_wid} = (defined $_task_wid) ? $_task_wid : $_wid; $self->{_task} = $_task; $self->{_wid} = $_wid; $self->{_running} = 0; ## Define exit pid and DIE handler. my $_use_threads = (defined $_task->{use_threads}) ? $_task->{use_threads} : $self->{use_threads}; if ($INC{'threads.pm'} && $_use_threads) { $self->{_exit_pid} = 'TID_' . threads->tid(); } else { $self->{_exit_pid} = 'PID_' . $$; } local $SIG{__DIE__} = sub { if (!defined $^S || $^S) { if ( ($INC{'threads.pm'} && threads->tid() != 0) || $ENV{'PERL_IPERL_RUNNING'} ) { # thread env or running inside IPerl, check stack trace my $_t = Carp::longmess(); $_t =~ s/\teval [^\n]+\n$//; if ( $_t =~ /^(?:[^\n]+\n){1,7}\teval / || $_t =~ /\n\teval [^\n]+\n\t(?:eval|Try)/ ) { CORE::die(@_); } } else { # normal env, trust $^S CORE::die(@_); } } my $_die_msg = (defined $_[0]) ? $_[0] : ''; local $SIG{__DIE__} = sub { }; local $\ = undef; print {*STDERR} $_die_msg; $self->exit(255, $_die_msg); }; ## Use options from user_tasks if defined. $self->{max_workers} = $_task->{max_workers} if ($_task->{max_workers}); $self->{chunk_size} = $_task->{chunk_size} if ($_task->{chunk_size}); $self->{gather} = $_task->{gather} if ($_task->{gather}); $self->{interval} = $_task->{interval} if ($_task->{interval}); $self->{sequence} = $_task->{sequence} if ($_task->{sequence}); $self->{task_name} = $_task->{task_name} if ($_task->{task_name}); $self->{user_args} = $_task->{user_args} if ($_task->{user_args}); $self->{user_begin} = $_task->{user_begin} if ($_task->{user_begin}); $self->{user_func} = $_task->{user_func} if ($_task->{user_func}); $self->{user_end} = $_task->{user_end} if ($_task->{user_end}); ## Init runtime vars. Obtain handle to lock files. my ($_COM_LOCK, $_DAT_LOCK, $_chn); my $_mce_sid = $self->{_mce_sid}; my $_sess_dir = $self->{_sess_dir}; if (defined $_params && exists $_params->{_chn}) { $_chn = $self->{_chn} = delete $_params->{_chn}; } else { $_chn = $self->{_chn} = $_wid % $self->{_data_channels} + 1; } ## Unset the need for channel locking if only worker on the channel. if ($self->{_init_total_workers} < DATA_CHANNELS * 2) { if ($_wid > $self->{_init_total_workers} % DATA_CHANNELS) { $self->{_lock_chn} = 0 if ($_wid <= DATA_CHANNELS); } } ## Choose locks for DATA channels. if ($self->{_lock_chn}) { open $_DAT_LOCK, '+>>:raw:stdio', "$_sess_dir/_dat.lock.$_chn" or die "(W) open error $_sess_dir/_dat.lock.$_chn: $!\n"; } open $_COM_LOCK, '+>>:raw:stdio', "$_sess_dir/_com.lock" or die "(W) open error $_sess_dir/_com.lock: $!\n"; $self->{_dat_lock} = $_DAT_LOCK; $self->{_com_lock} = $_COM_LOCK; ## Delete attributes no longer required after being spawned. delete @{ $self }{ qw( flush_file flush_stderr flush_stdout stderr_file stdout_file on_post_exit on_post_run user_data user_error user_output _pids _state _status _thrs _tids ) }; MCE::_clean_sessions($_mce_sid); ## Call module's worker_init routine for modules plugged into MCE. for my $_p (@{ $_plugin_worker_init }) { $_p->($self); } _do_send_init($self); ## Begin processing if worker was added during processing. Otherwise, ## respond back to the main process if the last worker spawned. if (defined $_params) { sleep 0.002; _worker_do($self, $_params); undef $_params; } else { lock $MCE::_WIN_LOCK if ($_is_winenv); } ## Enter worker loop. Clear worker session after running. _worker_loop($self); _do_send_clear($self); $self->{_com_lock} = undef; $self->{_dat_lock} = undef; MCE::_clear_session($_mce_sid); ## Wait until MCE completes exit notification. $SIG{__DIE__} = $SIG{__WARN__} = sub { }; local $@; eval { sysread $self->{_bse_r_sock}, (my $_c), 1; }; if ($self->{_lock_chn}) { close $_DAT_LOCK; undef $_DAT_LOCK; } close $_COM_LOCK; undef $_COM_LOCK; return; } 1; MCE-1.608/lib/MCE/Examples.pod0000644000076400007640000004675712511673010014617 0ustar mariomario =head1 NAME MCE::Examples - A list of examples demonstrating Many-Core Engine =head1 VERSION This document describes MCE::Examples version 1.608 =head1 DESCRIPTION MCE comes with various examples showing real-world scenarios on parallelizing something as small as cat (try with -n) to searching for patterns and word count aggregation. MCE 1.522 adds sampledb to the list demonstrating DBI with MCE. MCE 1.600 adds biofasta (folder), mutex.pl, and relay.pl. =head1 INCLUDED WITH THE DISTRIBUTION A wrapper script for parallelizing the grep binary. Hence, processing is done by the binary, not Perl. This wrapper resides under the bin directory. mce_grep A wrapper script with support for the following C binaries. agrep, grep, egrep, fgrep, and tre-agrep Chunking may be applied either at the [file] level, for large file(s), or at the [list] level when parsing many files recursively. The gain in performance is noticeable for expensive patterns, especially with agrep and tre-agrep. The following scripts are located under the examples directory. cat.pl, egrep.pl, wc.pl Concatenation, egrep, and word count scripts similar to the cat, egrep, and wc binaries respectively. files_flow.pl, files_mce.pl, files_thr.pl Demonstrates MCE::Flow, MCE::Queue, and Thread::Queue. See MCE::Queue synopsis for another variation. findnull.pl A parallel script for reporting lines containing null fields. It is many times faster than the egrep binary. Try this against a large file containing very long lines. flow_demo.pl, flow_model.pl Demonstrates MCE::Flow, MCE::Queue, and MCE->gather. foreach.pl, forseq.pl, forchunk.pl These examples demonstrate the sqrt example from Parallel::Loops (Parallel::Loops v0.07 utilizing Parallel::ForkManager v1.07). Testing was on a Linux VM; Perl v5.20.1; Haswell i7 at 2.6 GHz. The number indicates the size of input displayed in 1 second. Output was directed to >/dev/null. Parallel::Loops: 1,600 Forking each @input is expensive MCE->foreach...: 23,000 Workers persist between each @input MCE->forseq....: 200,000 Uses sequence of numbers as input MCE->forchunk..: 800,000 IPC overhead is greatly reduced interval.pl, mutex.pl, relay.pl Demonstration of the interval option appearing in MCE 1.5. Mutex locking and relaying data among workers. iterator.pl Similar to forseq.pl. Specifies an iterator for input_data. A factory function is called which returns a closure. pipe1.pl, pipe2.pl Process STDIN or FILE in parallel. Processing is via Perl for pipe1.pl, whereas an external command for pipe2.pl. seq_demo.pl, step_demo.pl Demonstration of the new sequence option appearing in MCE 1.3. Run with seq_demo.pl | sort Transparent use of MCE::Queue with MCE::Step. sync.pl, utf8.pl Barrier synchronization demonstration. Process input containing unicode data. The rest are organized into various sub directories. biofasta/fasta_aidx.pl, fasta_rdr*.pl Parallel demonstration for Bioinformatics. matmult/matmult_base*.pl, matmult_mce*.pl, strassen_mce*.pl Various matrix multiplication demonstrations benchmarking PDL, PDL + MCE, as well as parallelizing Strassen's divide-and-conquer algorithm. Included are 2 plain Perl examples. sampledb/create.pl, query*.pl, update*.pl Examples demonstrating DBI (SQLite) with MCE. tbray/wf_mce1.pl, wf_mce2.pl, wf_mce3.pl An implementation of wide finder utilizing MCE. As fast as MMAP IO when file resides in OS FS cache. 2x ~ 3x faster when reading directly from disk. =head1 CHUNK_SIZE => 1 (in essence, wanting no chunking on input data) Imagine a long running process and wanting to parallelize an array against a pool of workers. The sequence option may be used if simply wanting to loop through a sequence of numbers instead. Below, a callback function is used for displaying results. The logic shows how one can output results immediately while still preserving output order as if processing serially. The %tmp hash is a temporary cache for out-of-order results. use MCE; ## Return an iterator for preserving output order. sub preserve_order { my (%result_n, %result_d); my $order_id = 1; return sub { my ($chunk_id, $n, $data) = @_; $result_n{ $chunk_id } = $n; $result_d{ $chunk_id } = $data; while (1) { last unless exists $result_d{$order_id}; printf "n: %5d sqrt(n): %7.3f\n", $result_n{$order_id}, $result_d{$order_id}; delete $result_n{$order_id}; delete $result_d{$order_id}; $order_id++; } return; }; } ## Use $chunk_ref->[0] or $_ to retrieve the element. my @input_data = (0 .. 18000 - 1); my $mce = MCE->new( gather => preserve_order, input_data => \@input_data, chunk_size => 1, max_workers => 3, user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->gather($chunk_id, $_, sqrt($_)); } ); $mce->run; This does the same thing using the foreach "sugar" method. use MCE; sub preserve_order { ... } my $mce = MCE->new( chunk_size => 1, max_workers => 3, gather => preserve_order ); ## Use $chunk_ref->[0] or $_ to retrieve the element. my @input_data = (0 .. 18000 - 1); $mce->foreach( \@input_data, sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->gather($chunk_id, $_, sqrt($_)); }); The 2 examples described above were done using the Core API. MCE 1.5 comes with several models. The L model is used below. use MCE::Loop; sub preserve_order { ... } MCE::Loop::init { chunk_size => 1, max_workers => 3, gather => preserve_order }; ## Use $chunk_ref->[0] or $_ to retrieve the element. my @input_data = (0 .. 18000 - 1); mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->gather($chunk_id, $_, sqrt($_)); } @input_data; MCE::Loop::finish; =head1 CHUNKING INPUT_DATA Chunking has the effect of reducing IPC overhead by many folds. A chunk containing $chunk_size items is sent to the next available worker. use MCE; ## Return an iterator for preserving output order. sub preserve_order { my (%result_n, %result_d, $size); my $order_id = 1; return sub { my ($chunk_id, $n_ref, $data_ref) = @_; $result_n{ $chunk_id } = $n_ref; $result_d{ $chunk_id } = $data_ref; while (1) { last unless exists $result_d{$order_id}; $size = @{ $result_d{$order_id} }; for (0 .. $size - 1) { printf "n: %5d sqrt(n): %7.3f\n", $result_n{$order_id}->[$_], $result_d{$order_id}->[$_]; } delete $result_n{$order_id}; delete $result_d{$order_id}; $order_id++; } return; }; } ## Chunking requires one to loop inside the code block. my @input_data = (0 .. 18000 - 1); my $mce = MCE->new( gather => preserve_order, input_data => \@input_data, chunk_size => 500, max_workers => 3, user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; my (@n, @result); foreach ( @{ $chunk_ref } ) { push @n, $_; push @result, sqrt($_); } MCE->gather($chunk_id, \@n, \@result); } ); $mce->run; This does the same thing using the forchunk "sugar" method. use MCE; sub preserve_order { ... } my $mce = MCE->new( chunk_size => 500, max_workers => 3, gather => preserve_order ); ## Chunking requires one to loop inside the code block. my @input_data = (0 .. 18000 - 1); $mce->forchunk( \@input_data, sub { my ($mce, $chunk_ref, $chunk_id) = @_; my (@n, @result); foreach ( @{ $chunk_ref } ) { push @n, $_; push @result, sqrt($_); } MCE->gather($chunk_id, \@n, \@result); }); Finally, chunking with the L model. use MCE::Loop; sub preserve_order { ... } MCE::Loop::init { chunk_size => 500, max_workers => 3, gather => preserve_order }; ## Chunking requires one to loop inside the code block. my @input_data = (0 .. 18000 - 1); mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; my (@n, @result); foreach ( @{ $chunk_ref } ) { push @n, $_; push @result, sqrt($_); } MCE->gather($chunk_id, \@n, \@result); } @input_data; MCE::Loop::finish; =head1 DEMO APPLYING SEQUENCES WITH USER_TASKS The following is an extract from the seq_demo.pl example included with MCE. Think of having several MCEs running in parallel. The sequence and chunk_size options may be specified uniquely per each task. The input scalar $_ (not shown below) contains the same value as $seq_n in user_func. use MCE; use Time::HiRes 'sleep'; ## Run with seq_demo.pl | sort sub user_func { my ($mce, $seq_n, $chunk_id) = @_; my $wid = MCE->wid; my $task_id = MCE->task_id; my $task_wid = MCE->task_wid; if (ref $seq_n eq 'ARRAY') { ## seq_n or $_ is an array reference when chunk_size > 1 foreach (@{ $seq_n }) { MCE->printf( "task_id %d: seq_n %s: chunk_id %d: wid %d: task_wid %d\n", $task_id, $_, $chunk_id, $wid, $task_wid ); } } else { MCE->printf( "task_id %d: seq_n %s: chunk_id %d: wid %d: task_wid %d\n", $task_id, $seq_n, $chunk_id, $wid, $task_wid ); } sleep 0.003; return; } ## Each task can be configured uniquely. my $mce = MCE->new( user_tasks => [{ max_workers => 2, chunk_size => 1, sequence => { begin => 11, end => 19, step => 1 }, user_func => \&user_func },{ max_workers => 2, chunk_size => 5, sequence => { begin => 21, end => 29, step => 1 }, user_func => \&user_func },{ max_workers => 2, chunk_size => 3, sequence => { begin => 31, end => 39, step => 1 }, user_func => \&user_func }] ); $mce->run; -- Output task_id 0: seq_n 11: chunk_id 1: wid 2: task_wid 2 task_id 0: seq_n 12: chunk_id 2: wid 1: task_wid 1 task_id 0: seq_n 13: chunk_id 3: wid 2: task_wid 2 task_id 0: seq_n 14: chunk_id 4: wid 1: task_wid 1 task_id 0: seq_n 15: chunk_id 5: wid 2: task_wid 2 task_id 0: seq_n 16: chunk_id 6: wid 1: task_wid 1 task_id 0: seq_n 17: chunk_id 7: wid 2: task_wid 2 task_id 0: seq_n 18: chunk_id 8: wid 1: task_wid 1 task_id 0: seq_n 19: chunk_id 9: wid 2: task_wid 2 task_id 1: seq_n 21: chunk_id 1: wid 3: task_wid 1 task_id 1: seq_n 22: chunk_id 1: wid 3: task_wid 1 task_id 1: seq_n 23: chunk_id 1: wid 3: task_wid 1 task_id 1: seq_n 24: chunk_id 1: wid 3: task_wid 1 task_id 1: seq_n 25: chunk_id 1: wid 3: task_wid 1 task_id 1: seq_n 26: chunk_id 2: wid 4: task_wid 2 task_id 1: seq_n 27: chunk_id 2: wid 4: task_wid 2 task_id 1: seq_n 28: chunk_id 2: wid 4: task_wid 2 task_id 1: seq_n 29: chunk_id 2: wid 4: task_wid 2 task_id 2: seq_n 31: chunk_id 1: wid 5: task_wid 1 task_id 2: seq_n 32: chunk_id 1: wid 5: task_wid 1 task_id 2: seq_n 33: chunk_id 1: wid 5: task_wid 1 task_id 2: seq_n 34: chunk_id 2: wid 6: task_wid 2 task_id 2: seq_n 35: chunk_id 2: wid 6: task_wid 2 task_id 2: seq_n 36: chunk_id 2: wid 6: task_wid 2 task_id 2: seq_n 37: chunk_id 3: wid 5: task_wid 1 task_id 2: seq_n 38: chunk_id 3: wid 5: task_wid 1 task_id 2: seq_n 39: chunk_id 3: wid 5: task_wid 1 =head1 GLOBALLY SCOPED VARIABLES AND MCE MODELS It is possible that Perl may create a new code ref on subsequent runs causing MCE models to re-spawn. One solution to this is to declare global variables, referenced by workers, with "our" instead of "my". Let's take a look. The $i variable is declared with my and being reference in both user_begin and mce_loop blocks. This will cause Perl to create a new code ref for mce_loop on subsequent runs. use MCE::Loop; my $i = 0; ## <-- this is the reason, try our instead MCE::Loop::init { user_begin => sub { print "process_id: $$\n" if MCE->wid == 1; $i++; }, chunk_size => 1, max_workers => 'auto', }; for (1..2) { ## Perl creates another code block ref causing workers ## to re-spawn on subsequent runs. print "\n"; mce_loop { print "$i: $_\n" } 1..4; } MCE::Loop::finish; -- Output process_id: 51380 1: 1 1: 2 1: 3 1: 4 process_id: 51388 1: 1 1: 2 1: 3 1: 4 By making the one line change, we see that workers persist for the duration of the script. use MCE::Loop; our $i = 0; ## <-- changed my to our MCE::Loop::init { user_begin => sub { print "process_id: $$\n" if MCE->wid == 1; $i++; }, chunk_size => 1, max_workers => 'auto', }; for (1..2) { ## Workers persist between runs. No re-spawning. print "\n"; mce_loop { print "$i: $_\n" } 1..4; } -- Output process_id: 51457 1: 1 1: 2 1: 4 1: 3 process_id: 51457 2: 1 2: 2 2: 3 2: 4 One may alternatively specify a code reference to existing routines for user_begin and mce_loop. Take notice of the comma after \&_func though. use MCE::Loop; my $i = 0; ## my (ok) sub _begin { print "process_id: $$\n" if MCE->wid == 1; $i++; } sub _func { print "$i: $_\n"; } MCE::Loop::init { user_begin => \&_begin, chunk_size => 1, max_workers => 'auto', }; for (1..2) { print "\n"; mce_loop \&_func, 1..4; } MCE::Loop::finish; -- Output process_id: 51626 1: 1 1: 2 1: 3 1: 4 process_id: 51626 2: 1 2: 2 2: 3 2: 4 =head1 MONTE CARLO SIMULATION There is an article on the web (search for comp.lang.perl.misc MCE) suggesting that MCE::Examples does not cover a simple simulation scenario. This section demonstrates just that. The serial code is based off the one by "gamo". A sleep is added to imitate extra CPU time. The while loop is wrapped within a for loop to run 10 times. The random number generator is seeded as well. use Time::HiRes qw/sleep time/; srand 5906; my ($var, $foo, $bar) = (1, 2, 3); my ($r, $a, $b); my $start = time; for (1..10) { while (1) { $r = rand; $a = $r * ($var + $foo + $bar); $b = sqrt($var + $foo + $bar); last if ($a < $b + 0.001 && $a > $b - 0.001); sleep 0.002; } print "$r -> $a\n"; } my $end = time; printf {*STDERR} "\n## compute time: %0.03f secs\n\n", $end - $start; -- Output 0.408246276657106 -> 2.44947765994264 0.408099657137821 -> 2.44859794282693 0.408285842931324 -> 2.44971505758794 0.408342292008765 -> 2.45005375205259 0.408333076522673 -> 2.44999845913604 0.408344266898869 -> 2.45006560139321 0.408084104120526 -> 2.44850462472316 0.408197400014714 -> 2.44918440008828 0.408344783704855 -> 2.45006870222913 0.408248062985479 -> 2.44948837791287 ## compute time: 93.049 secs Next, we'd do the same with MCE. The demonstration requires at least MCE 1.509 to run properly. Folks on prior releases (1.505 - 1.508) will not see output for the 2nd run and beyond. use Time::HiRes qw/sleep time/; use MCE::Loop; srand 5906; ## Configure MCE. Move common variables inside the user_begin ## block when not needed by the manager process. MCE::Loop::init { user_begin => sub { use vars qw($var $foo $bar); our ($var, $foo, $bar) = (1, 2, 3); }, chunk_size => 1, max_workers => 'auto', input_data => \&_input, gather => \&_gather }; ## Callback functions. my ($done, $r, $a); sub _input { return if $done; return rand; } sub _gather { my ($_r, $_a, $_b) = @_; return if $done; if ($_a < $_b + 0.001 && $_a > $_b - 0.001) { ($done, $r, $a) = (1, $_r, $_a); } return; } ## Compute in parallel. my $start = time; for (1..10) { $done = 0; ## Reset $done before running mce_loop { # my ($mce, $chunk_ref, $chunk_id) = @_; # my $r = $chunk_ref->[0]; my $r = $_; ## Valid due to chunk_size => 1 my $a = $r * ($var + $foo + $bar); my $b = sqrt($var + $foo + $bar); MCE->gather($r, $a, $b); sleep 0.002; }; print "$r -> $a\n"; } printf "\n## compute time: %0.03f secs\n\n", time - $start; -- Output 0.408246276657106 -> 2.44947765994264 0.408099657137821 -> 2.44859794282693 0.408285842931324 -> 2.44971505758794 0.408342292008765 -> 2.45005375205259 0.408333076522673 -> 2.44999845913604 0.408344266898869 -> 2.45006560139321 0.408084104120526 -> 2.44850462472316 0.408197400014714 -> 2.44918440008828 0.408344783704855 -> 2.45006870222913 0.408248062985479 -> 2.44948837791287 ## compute time: 12.990 secs Well, there you have it. MCE is able to complete the same simulation many times faster. =head1 MANY WORKERS RUNNING IN PARALLEL There are occasions when one wants several workers to run in parallel without having to specify input_data or seqeunce. These two options are optional in MCE. The "do" and "sendto" methods, for sending data to the manager process, are demonstrated below. Both process serially by the manager process on a first come, first serve basis. use MCE::Flow max_workers => 4; sub report_stats { my ($wid, $msg, $h_ref) = @_; print "Worker $wid says $msg: ", $h_ref->{"counter"}, "\n"; } mce_flow sub { my ($mce) = @_; my $wid = MCE->wid; if ($wid == 1) { my %h = ("counter" => 0); while (1) { $h{"counter"} += 1; MCE->do("report_stats", $wid, "Hey there", \%h); last if ($h{"counter"} == 4); sleep 2; } } else { my %h = ("counter" => 0); while (1) { $h{"counter"} += 1; MCE->do("report_stats", $wid, "Welcome..", \%h); last if ($h{"counter"} == 2); sleep 4; } } MCE->print(\*STDERR, "Worker $wid is exiting\n"); }; -- Output Note how worker 2 comes first in the 2nd run below. $ ./demo.pl Worker 1 says Hey there: 1 Worker 2 says Welcome..: 1 Worker 3 says Welcome..: 1 Worker 4 says Welcome..: 1 Worker 1 says Hey there: 2 Worker 2 says Welcome..: 2 Worker 3 says Welcome..: 2 Worker 1 says Hey there: 3 Worker 2 is exiting Worker 3 is exiting Worker 4 says Welcome..: 2 Worker 4 is exiting Worker 1 says Hey there: 4 Worker 1 is exiting $ ./demo.pl Worker 2 says Welcome..: 1 Worker 1 says Hey there: 1 Worker 4 says Welcome..: 1 Worker 3 says Welcome..: 1 Worker 1 says Hey there: 2 Worker 2 says Welcome..: 2 Worker 4 says Welcome..: 2 Worker 3 says Welcome..: 2 Worker 2 is exiting Worker 4 is exiting Worker 1 says Hey there: 3 Worker 3 is exiting Worker 1 says Hey there: 4 Worker 1 is exiting =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Queue.pm0000644000076400007640000016073212511673040013750 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Queue - Hybrid (normal and priority) queues for Many-Core Engine. ## ############################################################################### package MCE::Queue; use strict; use warnings; ## no critic (Subroutines::ProhibitExplicitReturnUndef) ## no critic (TestingAndDebugging::ProhibitNoStrict) no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use Fcntl qw( :flock O_RDONLY ); use Scalar::Util qw( looks_like_number ); use MCE::Util qw( $LF ); use bytes; our $VERSION = '1.608'; ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### our ($HIGHEST, $LOWEST, $FIFO, $LIFO, $LILO, $FILO) = (1, 0, 1, 0, 1, 0); my ($FAST, $PORDER, $TYPE) = (0, $HIGHEST, $FIFO); my $_loaded; sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; if ( $_arg eq 'fast' ) { _croak('MCE::Queue::import: (FAST) must be 1 or 0') if (!defined $_[0] || ($_[0] ne '1' && $_[0] ne '0')); $FAST = shift ; next; } if ( $_arg eq 'porder' ) { _croak('MCE::Queue::import: (PORDER) must be 1 or 0') if (!defined $_[0] || ($_[0] ne '1' && $_[0] ne '0')); $PORDER = shift ; next; } if ( $_arg eq 'type' ) { _croak('MCE::Queue::import: (TYPE) must be 1 or 0') if (!defined $_[0] || ($_[0] ne '1' && $_[0] ne '0')); $TYPE = shift ; next; } _croak("MCE::Queue::import: ($_argument) is not a valid module argument"); } ## Define public methods to internal methods. no strict 'refs'; no warnings 'redefine'; if (defined $MCE::VERSION && MCE->wid == 0) { _mce_m_init(); } else { *{ 'MCE::Queue::clear' } = \&_clear; *{ 'MCE::Queue::enqueue' } = \&_enqueue; *{ 'MCE::Queue::enqueuep' } = \&_enqueuep; *{ 'MCE::Queue::dequeue' } = \&_dequeue; *{ 'MCE::Queue::dequeue_nb' } = \&_dequeue; *{ 'MCE::Queue::pending' } = \&_pending; *{ 'MCE::Queue::insert' } = \&_insert; *{ 'MCE::Queue::insertp' } = \&_insertp; *{ 'MCE::Queue::peek' } = \&_peek; *{ 'MCE::Queue::peekp' } = \&_peekp; *{ 'MCE::Queue::peekh' } = \&_peekh; *{ 'MCE::Queue::heap' } = \&_heap; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Define constants & variables. ## ############################################################################### use constant { MAX_DQ_DEPTH => 192, ## Maximum dequeue notifications allowed OUTPUT_C_QUE => 'C~QUE', ## Clear the queue OUTPUT_A_QUE => 'A~QUE', ## Enqueue into queue (array) OUTPUT_A_QUP => 'A~QUP', ## Enqueue into queue (array (p)) OUTPUT_R_QUE => 'R~QUE', ## Enqueue into queue (reference) OUTPUT_R_QUP => 'R~QUP', ## Enqueue into queue (reference (p)) OUTPUT_S_QUE => 'S~QUE', ## Enqueue into queue (scalar) OUTPUT_S_QUP => 'S~QUP', ## Enqueue into queue (scalar (p)) OUTPUT_D_QUE => 'D~QUE', ## Dequeue from queue (blocking) OUTPUT_D_QUN => 'D~QUN', ## Dequeue from queue (non-blocking) OUTPUT_N_QUE => 'N~QUE', ## Return the number of items OUTPUT_I_QUE => 'I~QUE', ## Insert into queue OUTPUT_I_QUP => 'I~QUP', ## Insert into queue (p) OUTPUT_P_QUE => 'P~QUE', ## Peek into queue OUTPUT_P_QUP => 'P~QUP', ## Peek into queue (p) OUTPUT_P_QUH => 'P~QUH', ## Peek into heap OUTPUT_H_QUE => 'H~QUE' ## Return the heap }; ## ** Attributes used internally and listed here. ## _qr_sock _qw_sock _datp _datq _heap _id _nb_flag _porder _type _standalone my %_valid_fields_new = map { $_ => 1 } qw( fast gather porder queue type ); my $_all = {}; my $_qid = 0; sub DESTROY { my ($_Q) = @_; delete $_all->{ $_Q->{_id} } if (exists $_Q->{_id}); undef $_Q->{_datp}; undef $_Q->{_datq}; undef $_Q->{_heap}; return if (defined $MCE::VERSION && !defined $MCE::MCE->{_wid}); return if (defined $MCE::MCE && $MCE::MCE->{_wid}); MCE::Util::_destroy_sockets($_Q, qw(_aw_sock _ar_sock _qw_sock _qr_sock)); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## New instance instantiation. ## ############################################################################### sub new { my ($_class, %_argv) = @_; @_ = (); local $!; my $_Q = {}; bless($_Q, ref($_class) || $_class); for my $_p (keys %_argv) { _croak("MCE::Queue::new: ($_p) is not a valid constructor argument") unless (exists $_valid_fields_new{$_p}); } $_Q->{_datp} = {}; ## Priority data { p1 => [ ], p2 => [ ], pN => [ ] } $_Q->{_heap} = []; ## Priority heap [ pN, p2, p1 ] ## in heap order ## fyi, _datp will always dequeue before _datq $_Q->{_fast} = (exists $_argv{fast} && defined $_argv{fast}) ? $_argv{fast} : $FAST; $_Q->{_porder} = (exists $_argv{porder} && defined $_argv{porder}) ? $_argv{porder} : $PORDER; $_Q->{_type} = (exists $_argv{type} && defined $_argv{type}) ? $_argv{type} : $TYPE; ## ------------------------------------------------------------------------- _croak('MCE::Queue::new: (fast) must be 1 or 0') if ($_Q->{_fast} ne '1' && $_Q->{_fast} ne '0'); _croak('MCE::Queue::new: (porder) must be 1 or 0') if ($_Q->{_porder} ne '1' && $_Q->{_porder} ne '0'); _croak('MCE::Queue::new: (type) must be 1 or 0') if ($_Q->{_type} ne '1' && $_Q->{_type} ne '0'); if (exists $_argv{queue}) { _croak('MCE::Queue::new: (queue) is not an ARRAY reference') if (ref $_argv{queue} ne 'ARRAY'); $_Q->{_datq} = $_argv{queue}; } else { $_Q->{_datq} = []; } if (exists $_argv{gather}) { _croak('MCE::Queue::new: (gather) is not a CODE reference') if (ref $_argv{gather} ne 'CODE'); $_Q->{gather} = $_argv{gather}; } ## ------------------------------------------------------------------------- if (defined $MCE::VERSION) { if (MCE->wid == 0) { $_Q->{_id} = ++$_qid; $_all->{$_qid} = $_Q; $_Q->{_dsem} = 0 if ($_Q->{_fast}); MCE::Util::_make_socket_pair($_Q, qw(_qr_sock _qw_sock)); syswrite $_Q->{_qw_sock}, $LF if (exists $_argv{queue} && scalar @{ $_argv{queue} }); } else { $_Q->{_standalone} = 1; } } return $_Q; } ############################################################################### ## ---------------------------------------------------------------------------- ## Clear method. ## ############################################################################### sub _clear { my ($_Q) = @_; %{ $_Q->{_datp} } = (); @{ $_Q->{_datq} } = (); @{ $_Q->{_heap} } = (); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Enqueue methods. ## ############################################################################### ## Add items to the tail of the queue. sub _enqueue { my $_Q = shift; ## Append item(s) into the queue. push @{ $_Q->{_datq} }, @_; return; } ## Add items to the tail of the queue with priority level. sub _enqueuep { my ($_Q, $_p) = (shift, shift); _croak('MCE::Queue::enqueuep: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); return unless (scalar @_); ## Enlist priority into the heap. if (!exists $_Q->{_datp}->{$_p} || @{ $_Q->{_datp}->{$_p} } == 0) { unless (scalar @{ $_Q->{_heap} }) { push @{ $_Q->{_heap} }, $_p; } elsif ($_Q->{_porder}) { $_Q->_heap_insert_high($_p); } else { $_Q->_heap_insert_low($_p); } } ## Append item(s) into the queue. push @{ $_Q->{_datp}->{$_p} }, @_; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Dequeue and pending methods. ## ############################################################################### ## Return item(s) from the queue. sub _dequeue { my ($_Q, $_cnt) = @_; if (defined $_cnt && $_cnt ne '1') { _croak('MCE::Queue::dequeue: (count argument) is not valid') if (!looks_like_number($_cnt) || int($_cnt) != $_cnt || $_cnt < 1); my @_items; push(@_items, $_Q->_dequeue()) for (1 .. $_cnt); return @_items; } ## Return item from the non-priority queue. unless (scalar @{ $_Q->{_heap} }) { return ($_Q->{_type}) ? shift @{ $_Q->{_datq} } : pop @{ $_Q->{_datq} }; } my $_p = $_Q->{_heap}->[0]; ## Delist priority from the heap when 1 item remains. shift @{ $_Q->{_heap} } if (@{ $_Q->{_datp}->{$_p} } == 1); ## Return item from the priority queue. return ($_Q->{_type}) ? shift @{ $_Q->{_datp}->{$_p} } : pop @{ $_Q->{_datp}->{$_p} }; } ## Return the number of items in the queue. sub _pending { my $_pending = 0; my ($_Q) = @_; for my $_h (@{ $_Q->{_heap} }) { $_pending += @{ $_Q->{_datp}->{$_h} }; } $_pending += @{ $_Q->{_datq} }; return $_pending; } ############################################################################### ## ---------------------------------------------------------------------------- ## Insert methods. ## ############################################################################### ## Insert items anywhere into the queue. sub _insert { my ($_Q, $_i) = (shift, shift); _croak('MCE::Queue::insert: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return unless (scalar @_); if (abs($_i) > scalar @{ $_Q->{_datq} }) { if ($_i >= 0) { if ($_Q->{_type}) { push @{ $_Q->{_datq} }, @_; } else { unshift @{ $_Q->{_datq} }, @_; } } else { if ($_Q->{_type}) { unshift @{ $_Q->{_datq} }, @_; } else { push @{ $_Q->{_datq} }, @_; } } } else { if (!$_Q->{_type}) { $_i = ($_i >= 0) ? scalar(@{ $_Q->{_datq} }) - $_i : abs($_i); } splice @{ $_Q->{_datq} }, $_i, 0, @_; } return; } ## Insert items anywhere into the queue with priority level. sub _insertp { my ($_Q, $_p, $_i) = (shift, shift, shift); _croak('MCE::Queue::insertp: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); _croak('MCE::Queue::insertp: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return unless (scalar @_); if (exists $_Q->{_datp}->{$_p} && scalar @{ $_Q->{_datp}->{$_p} }) { if (abs($_i) > scalar @{ $_Q->{_datp}->{$_p} }) { if ($_i >= 0) { if ($_Q->{_type}) { push @{ $_Q->{_datp}->{$_p} }, @_; } else { unshift @{ $_Q->{_datp}->{$_p} }, @_; } } else { if ($_Q->{_type}) { unshift @{ $_Q->{_datp}->{$_p} }, @_; } else { push @{ $_Q->{_datp}->{$_p} }, @_; } } } else { if (!$_Q->{_type}) { $_i = ($_i >=0) ? scalar(@{ $_Q->{_datp}->{$_p} }) - $_i : abs($_i); } splice @{ $_Q->{_datp}->{$_p} }, $_i, 0, @_; } } else { $_Q->_enqueuep($_p, @_); } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Peek and heap methods. ## ## Below, do not change 'return undef' statements to 'return'. ## ############################################################################### ## Return an item without removing it from the queue. sub _peek { my $_Q = shift; my $_i = shift || 0; _croak('MCE::Queue::peek: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return undef if (abs($_i) > scalar @{ $_Q->{_datq} }); if (!$_Q->{_type}) { $_i = ($_i >= 0) ? scalar(@{ $_Q->{_datq} }) - ($_i + 1) : abs($_i + 1); } return $_Q->{_datq}->[$_i]; } ## Return an item without removing it from the queue with priority level. sub _peekp { my ($_Q, $_p) = (shift, shift); my $_i = shift || 0; _croak('MCE::Queue::peekp: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); _croak('MCE::Queue::peekp: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return undef unless (exists $_Q->{_datp}->{$_p}); return undef if (abs($_i) > scalar @{ $_Q->{_datp}->{$_p} }); if (!$_Q->{_type}) { $_i = ($_i >= 0) ? scalar(@{ $_Q->{_datp}->{$_p} }) - ($_i + 1) : abs($_i + 1); } return $_Q->{_datp}->{$_p}->[$_i]; } ## Return a priority level without removing it from the heap. sub _peekh { my $_Q = shift; my $_i = shift || 0; _croak('MCE::Queue::peekh: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return undef if (abs($_i) > scalar @{ $_Q->{_heap} }); return $_Q->{_heap}->[$_i]; } ## Return a list of priority levels in the heap. sub _heap { return @{ shift->{_heap} }; } ############################################################################### ## ---------------------------------------------------------------------------- ## Internal methods. ## ############################################################################### sub _croak { unless (defined $MCE::VERSION) { $\ = undef; require Carp; goto &Carp::croak; } else { goto &MCE::_croak; } } ## Helper method for getting the reference to the underlying array. ## Use with test scripts for comparing data only (not a public API). sub _get_aref { my ($_Q, $_p) = @_; return if (defined $MCE::VERSION && !defined $MCE::MCE->{_wid}); return if (defined $MCE::MCE && $MCE::MCE->{_wid}); if (defined $_p) { _croak('MCE::Queue::_get_aref: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); return undef unless (exists $_Q->{_datp}->{$_p}); return $_Q->{_datp}->{$_p}; } return $_Q->{_datq}; } ## A quick method for just wanting to know if the queue has pending data. sub _has_data { return ( scalar @{ $_[0]->{_datq} } || scalar @{ $_[0]->{_heap} } ) ? 1 : 0; } ## Insert priority into the heap. A lower priority level comes first. sub _heap_insert_low { my ($_Q, $_p) = @_; ## Insert priority at the head of the heap. if ($_p < $_Q->{_heap}->[0]) { unshift @{ $_Q->{_heap} }, $_p; } ## Insert priority at the end of the heap. elsif ($_p > $_Q->{_heap}->[-1]) { push @{ $_Q->{_heap} }, $_p; } ## Insert priority through binary search. else { my $_lower = 0; my $_upper = @{ $_Q->{_heap} }; while ($_lower < $_upper) { my $_midpoint = ($_upper + $_lower) >> 1; if ($_p > $_Q->{_heap}->[$_midpoint]) { $_lower = $_midpoint + 1; } else { $_upper = $_midpoint; } } ## Insert priority into heap. splice @{ $_Q->{_heap} }, $_lower, 0, $_p; } return; } ## Insert priority into the heap. A higher priority level comes first. sub _heap_insert_high { my ($_Q, $_p) = @_; ## Insert priority at the head of the heap. if ($_p > $_Q->{_heap}->[0]) { unshift @{ $_Q->{_heap} }, $_p; } ## Insert priority at the end of the heap. elsif ($_p < $_Q->{_heap}->[-1]) { push @{ $_Q->{_heap} }, $_p; } ## Insert priority through binary search. else { my $_lower = 0; my $_upper = @{ $_Q->{_heap} }; while ($_lower < $_upper) { my $_midpoint = ($_upper + $_lower) >> 1; if ($_p < $_Q->{_heap}->[$_midpoint]) { $_lower = $_midpoint + 1; } else { $_upper = $_midpoint; } } ## Insert priority into heap. splice @{ $_Q->{_heap} }, $_lower, 0, $_p; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Output routines for the manager process. ## ############################################################################### { my ($_MCE, $_DAU_R_SOCK_REF, $_DAU_R_SOCK, $_cnt, $_i, $_id); my ($_len, $_p, $_Q, $_pending); my %_output_function = ( OUTPUT_C_QUE.$LF => sub { ## Clear the queue my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); $_Q = $_all->{$_id}; sysread $_Q->{_qr_sock}, $_buf, 1 if ($_Q->_has_data()); $_Q->_clear(); print {$_DAU_R_SOCK} $LF; return; }, ## ---------------------------------------------------------------------- OUTPUT_A_QUE.$LF => sub { ## Enqueue into queue (A) my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_Q = $_all->{$_id}; if ($_Q->{gather}) { local $_ = $_MCE->{thaw}($_buf); $_Q->{gather}($_Q, @{ $_ }); } else { syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); push @{ $_Q->{_datq} }, @{ $_MCE->{thaw}($_buf) }; } return; }, OUTPUT_A_QUP.$LF => sub { ## Enqueue into queue (A,p) my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_p = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_Q = $_all->{$_id}; syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); $_Q->_enqueuep($_p, @{ $_MCE->{thaw}($_buf) }); return; }, ## ---------------------------------------------------------------------- OUTPUT_R_QUE.$LF => sub { ## Enqueue into queue (R) my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_Q = $_all->{$_id}; if ($_Q->{gather}) { local $_ = $_MCE->{thaw}($_buf); $_Q->{gather}($_Q, $_); } else { syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); push @{ $_Q->{_datq} }, $_MCE->{thaw}($_buf); } return; }, OUTPUT_R_QUP.$LF => sub { ## Enqueue into queue (R,p) my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_p = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_Q = $_all->{$_id}; syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); $_Q->_enqueuep($_p, $_MCE->{thaw}($_buf)); return; }, ## ---------------------------------------------------------------------- OUTPUT_S_QUE.$LF => sub { ## Enqueue into queue (S) $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; local $_; chomp($_id = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_, $_len; $_Q = $_all->{$_id}; if ($_Q->{gather}) { $_Q->{gather}($_Q, $_); } else { syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); push @{ $_Q->{_datq} }, $_; } return; }, OUTPUT_S_QUP.$LF => sub { ## Enqueue into queue (S,p) my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_p = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_Q = $_all->{$_id}; syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); $_Q->_enqueuep($_p, $_buf); return; }, ## ---------------------------------------------------------------------- OUTPUT_D_QUE.$LF => sub { ## Dequeue from queue (B) $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_cnt = <$_DAU_R_SOCK>); $_cnt = 0 if ($_cnt == 1); $_Q = $_all->{$_id}; my (@_items, $_buf); if ($_cnt) { push(@_items, $_Q->_dequeue()) for (1 .. $_cnt); } else { $_buf = $_Q->_dequeue(); } if ($_Q->{_fast}) { ## The 'fast' option may reduce wait time, thus run faster if ($_Q->{_dsem} <= 1) { $_pending = $_Q->_pending(); $_pending = int($_pending / $_cnt) if ($_cnt); if ($_pending) { $_pending = MAX_DQ_DEPTH if ($_pending > MAX_DQ_DEPTH); syswrite $_Q->{_qw_sock}, $LF for (1 .. $_pending); } $_Q->{_dsem} = $_pending; } else { $_Q->{_dsem} -= 1; } } else { ## Otherwise, never to exceed one byte in the channel syswrite $_Q->{_qw_sock}, $LF if ($_Q->_has_data()); } if ($_cnt) { unless (defined $_items[0]) { print {$_DAU_R_SOCK} -1 . $LF; } else { $_buf = $_MCE->{freeze}(\@_items); print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; } } else { unless (defined $_buf) { print {$_DAU_R_SOCK} -1 . $LF; } else { if (ref $_buf) { $_buf = $_MCE->{freeze}($_buf) . '1'; } else { $_buf .= '0'; } print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; } } $_Q->{_nb_flag} = 0; return; }, OUTPUT_D_QUN.$LF => sub { ## Dequeue from queue (NB) $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_cnt = <$_DAU_R_SOCK>); $_Q = $_all->{$_id}; if ($_cnt == 1) { my $_buf = $_Q->_dequeue(); unless (defined $_buf) { print {$_DAU_R_SOCK} -1 . $LF; } else { if (ref $_buf) { $_buf = $_MCE->{freeze}($_buf) . '1'; } else { $_buf .= '0'; } print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; } } else { my @_items; push(@_items, $_Q->_dequeue()) for (1 .. $_cnt); unless (defined $_items[0]) { print {$_DAU_R_SOCK} -1 . $LF; } else { my $_buf = $_MCE->{freeze}(\@_items); print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; } } $_Q->{_nb_flag} = 1; return; }, ## ---------------------------------------------------------------------- OUTPUT_N_QUE.$LF => sub { ## Return number of items $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); print {$_DAU_R_SOCK} $_all->{$_id}->_pending() . $LF; return; }, OUTPUT_I_QUE.$LF => sub { ## Insert into queue my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_i = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_Q = $_all->{$_id}; syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); if (chop $_buf) { $_Q->_insert($_i, @{ $_MCE->{thaw}($_buf) }); } else { $_Q->_insert($_i, $_buf); } return; }, OUTPUT_I_QUP.$LF => sub { ## Insert into queue (p) my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_p = <$_DAU_R_SOCK>); chomp($_i = <$_DAU_R_SOCK>); chomp($_len = <$_DAU_R_SOCK>); read $_DAU_R_SOCK, $_buf, $_len; $_Q = $_all->{$_id}; syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); if (chop $_buf) { $_Q->_insertp($_p, $_i, @{ $_MCE->{thaw}($_buf) }); } else { $_Q->_insertp($_p, $_i, $_buf); } return; }, ## ---------------------------------------------------------------------- OUTPUT_P_QUE.$LF => sub { ## Peek into queue my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_i = <$_DAU_R_SOCK>); $_Q = $_all->{$_id}; $_buf = $_Q->_peek($_i); unless (defined $_buf) { print {$_DAU_R_SOCK} -1 . $LF; } else { if (ref $_buf) { $_buf = $_MCE->{freeze}($_buf) . '1'; } else { $_buf .= '0'; } print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; } return; }, OUTPUT_P_QUP.$LF => sub { ## Peek into queue (p) my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_p = <$_DAU_R_SOCK>); chomp($_i = <$_DAU_R_SOCK>); $_Q = $_all->{$_id}; $_buf = $_Q->_peekp($_p, $_i); unless (defined $_buf) { print {$_DAU_R_SOCK} -1 . $LF; } else { if (ref $_buf) { $_buf = $_MCE->{freeze}($_buf) . '1'; } else { $_buf .= '0'; } print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; } return; }, OUTPUT_P_QUH.$LF => sub { ## Peek into heap my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); chomp($_i = <$_DAU_R_SOCK>); $_Q = $_all->{$_id}; $_buf = $_Q->_peekh($_i); unless (defined $_buf) { print {$_DAU_R_SOCK} -1 . $LF; } else { print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; } return; }, ## ---------------------------------------------------------------------- OUTPUT_H_QUE.$LF => sub { ## Return the heap my $_buf; $_DAU_R_SOCK = ${ $_DAU_R_SOCK_REF }; chomp($_id = <$_DAU_R_SOCK>); $_Q = $_all->{$_id}; $_buf = $_MCE->{freeze}([ $_Q->_heap() ]); print {$_DAU_R_SOCK} length($_buf) . $LF . $_buf; return; }, ); ## ------------------------------------------------------------------------- sub _mce_m_loop_begin { ($_MCE, $_DAU_R_SOCK_REF) = @_; return; } sub _mce_m_loop_end { $_MCE = $_DAU_R_SOCK_REF = $_DAU_R_SOCK = $_cnt = $_i = $_id = $_len = $_p = $_Q = undef; return; } sub _mce_m_init { MCE::_attach_plugin( \%_output_function, \&_mce_m_loop_begin, \&_mce_m_loop_end, \&_mce_w_init ); no strict 'refs'; no warnings 'redefine'; *{ 'MCE::Queue::clear' } = \&_mce_m_clear; *{ 'MCE::Queue::enqueue' } = \&_mce_m_enqueue; *{ 'MCE::Queue::enqueuep' } = \&_mce_m_enqueuep; *{ 'MCE::Queue::dequeue' } = \&_mce_m_dequeue; *{ 'MCE::Queue::dequeue_nb' } = \&_mce_m_dequeue_nb; *{ 'MCE::Queue::insert' } = \&_mce_m_insert; *{ 'MCE::Queue::insertp' } = \&_mce_m_insertp; *{ 'MCE::Queue::pending' } = \&_pending; *{ 'MCE::Queue::peek' } = \&_peek; *{ 'MCE::Queue::peekp' } = \&_peekp; *{ 'MCE::Queue::peekh' } = \&_peekh; *{ 'MCE::Queue::heap' } = \&_heap; return; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Wrapper methods for the manager process. ## ############################################################################### sub _mce_m_clear { my $_next; my ($_Q) = @_; if ($_Q->{_fast}) { warn "MCE::Queue: (clear) not allowed for fast => 1\n"; } else { sysread $_Q->{_qr_sock}, $_next, 1 if ($_Q->_has_data()); $_Q->_clear(); } return; } sub _mce_m_enqueue { my $_Q = shift; return unless (scalar @_); syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); push @{ $_Q->{_datq} }, @_; return; } sub _mce_m_enqueuep { my ($_Q, $_p) = (shift, shift); _croak('MCE::Queue::enqueuep: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); return unless (scalar @_); syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); $_Q->_enqueuep($_p, @_); return; } ## ---------------------------------------------------------------------------- sub _mce_m_dequeue { my ($_Q, $_cnt) = @_; my (@_items, $_buf, $_next, $_pending); sysread $_Q->{_qr_sock}, $_next, 1; ## Block here if (defined $_cnt && $_cnt ne '1') { @_items = $_Q->_dequeue($_cnt); } else { $_buf = $_Q->_dequeue(); } if ($_Q->{_fast}) { ## The 'fast' option may reduce wait time, thus run faster if ($_Q->{_dsem} <= 1) { $_pending = $_Q->_pending(); $_pending = int($_pending / $_cnt) if (defined $_cnt); if ($_pending) { $_pending = MAX_DQ_DEPTH if ($_pending > MAX_DQ_DEPTH); syswrite $_Q->{_qw_sock}, $LF for (1 .. $_pending); } $_Q->{_dsem} = $_pending; } else { $_Q->{_dsem} -= 1; } } else { ## Otherwise, never to exceed one byte in the channel syswrite $_Q->{_qw_sock}, $LF if ($_Q->_has_data()); } $_Q->{_nb_flag} = 0; return @_items if (defined $_cnt); return $_buf; } sub _mce_m_dequeue_nb { my ($_Q, $_cnt) = @_; if ($_Q->{_fast}) { warn "MCE::Queue: (dequeue_nb) not allowed for fast => 1\n"; return; } else { $_Q->{_nb_flag} = 1; return (defined $_cnt && $_cnt ne '1') ? $_Q->_dequeue($_cnt) : $_Q->_dequeue(); } } ## ---------------------------------------------------------------------------- sub _mce_m_insert { my ($_Q, $_i) = (shift, shift); _croak('MCE::Queue::insert: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return unless (scalar @_); syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); $_Q->_insert($_i, @_); return; } sub _mce_m_insertp { my ($_Q, $_p, $_i) = (shift, shift, shift); _croak('MCE::Queue::insertp: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); _croak('MCE::Queue::insertp: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return unless (scalar @_); syswrite $_Q->{_qw_sock}, $LF if (!$_Q->{_nb_flag} && !$_Q->_has_data()); $_Q->_insertp($_p, $_i, @_); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Wrapper methods for the worker process. ## ############################################################################### { my ($_chn, $_lock_chn, $_len, $_next, $_pending, $_tag); my ($_MCE, $_DAT_LOCK, $_DAT_W_SOCK, $_DAU_W_SOCK); sub _mce_w_init { ($_MCE) = @_; $_chn = $_MCE->{_chn}; $_DAT_LOCK = $_MCE->{_dat_lock}; $_DAT_W_SOCK = $_MCE->{_dat_w_sock}->[0]; $_DAU_W_SOCK = $_MCE->{_dat_w_sock}->[$_chn]; $_lock_chn = $_MCE->{_lock_chn}; for my $_p (keys %{ $_all }) { undef $_all->{$_p}->{_datp}; delete $_all->{$_p}->{_datp}; undef $_all->{$_p}->{_datq}; delete $_all->{$_p}->{_datq}; undef $_all->{$_p}->{_heap}; delete $_all->{$_p}->{_heap}; } no strict 'refs'; no warnings 'redefine'; *{ 'MCE::Queue::clear' } = \&_mce_w_clear; *{ 'MCE::Queue::enqueue' } = \&_mce_w_enqueue; *{ 'MCE::Queue::enqueuep' } = \&_mce_w_enqueuep; *{ 'MCE::Queue::dequeue' } = \&_mce_w_dequeue; *{ 'MCE::Queue::dequeue_nb' } = \&_mce_w_dequeue_nb; *{ 'MCE::Queue::pending' } = \&_mce_w_pending; *{ 'MCE::Queue::insert' } = \&_mce_w_insert; *{ 'MCE::Queue::insertp' } = \&_mce_w_insertp; *{ 'MCE::Queue::peek' } = \&_mce_w_peek; *{ 'MCE::Queue::peekp' } = \&_mce_w_peekp; *{ 'MCE::Queue::peekh' } = \&_mce_w_peekh; *{ 'MCE::Queue::heap' } = \&_mce_w_heap; return; } ## ------------------------------------------------------------------------- sub _mce_w_clear { my ($_Q) = @_; return $_Q->_clear() if (exists $_Q->{_standalone}); if ($_Q->{_fast}) { warn "MCE::Queue: (clear) not allowed for fast => 1\n"; } else { local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_C_QUE . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF; <$_DAU_W_SOCK>; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return; } ## ------------------------------------------------------------------------- sub _mce_w_enqueue { my ($_buf, $_tmp); my $_Q = shift; return $_Q->_enqueue(@_) if (exists $_Q->{_standalone}); return unless (scalar @_); if (scalar @_ > 1 || !defined $_[0]) { $_tag = OUTPUT_A_QUE; $_tmp = $_MCE->{freeze}(\@_); $_buf = $_Q->{_id} . $LF . length($_tmp) . $LF . $_tmp; } elsif (ref $_[0]) { $_tag = OUTPUT_R_QUE; $_tmp = $_MCE->{freeze}($_[0]); $_buf = $_Q->{_id} . $LF . length($_tmp) . $LF . $_tmp; } else { $_tag = OUTPUT_S_QUE; $_buf = $_Q->{_id} . $LF . length($_[0]) . $LF . $_[0]; } local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_tag . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_buf; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } sub _mce_w_enqueuep { my ($_buf, $_tmp); my ($_Q, $_p) = (shift, shift); _croak('MCE::Queue::enqueuep: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); return $_Q->_enqueuep($_p, @_) if (exists $_Q->{_standalone}); return unless (scalar @_); if (scalar @_ > 1 || !defined $_[0]) { $_tag = OUTPUT_A_QUP; $_tmp = $_MCE->{freeze}(\@_); $_buf = $_Q->{_id} . $LF . $_p . $LF . length($_tmp) . $LF . $_tmp; } elsif (ref $_[0]) { $_tag = OUTPUT_R_QUP; $_tmp = $_MCE->{freeze}($_[0]); $_buf = $_Q->{_id} . $LF . $_p . $LF . length($_tmp) . $LF . $_tmp; } else { $_tag = OUTPUT_S_QUP; $_buf = $_Q->{_id} . $LF . $_p . $LF . length($_[0]) . $LF . $_[0]; } local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} $_tag . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_buf; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } ## ------------------------------------------------------------------------- sub _mce_w_dequeue { my $_buf; my ($_Q, $_cnt) = @_; return $_Q->_dequeue(@_) if (exists $_Q->{_standalone}); if (defined $_cnt && $_cnt ne '1') { _croak('MCE::Queue::dequeue: (count argument) is not valid') if (!looks_like_number($_cnt) || int($_cnt) != $_cnt || $_cnt < 1); } else { $_cnt = 1; } { local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); sysread $_Q->{_qr_sock}, $_next, 1; ## Block here flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_D_QUE . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF . $_cnt . $LF; chomp($_len = <$_DAU_W_SOCK>); if ($_len < 0) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return undef; # Do not change this to return; } read $_DAU_W_SOCK, $_buf, $_len; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } if ($_cnt == 1) { return (chop $_buf) ? $_MCE->{thaw}($_buf) : $_buf; } else { return @{ $_MCE->{thaw}($_buf) }; } } sub _mce_w_dequeue_nb { my $_buf; my ($_Q, $_cnt) = @_; return $_Q->_dequeue(@_) if (exists $_Q->{_standalone}); if ($_Q->{_fast}) { warn "MCE::Queue: (dequeue_nb) not allowed for fast => 1\n"; return; } if (defined $_cnt && $_cnt ne '1') { _croak('MCE::Queue::dequeue: (count argument) is not valid') if (!looks_like_number($_cnt) || int($_cnt) != $_cnt || $_cnt < 1); } else { $_cnt = 1; } { local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_D_QUN . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF . $_cnt . $LF; chomp($_len = <$_DAU_W_SOCK>); if ($_len < 0) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return undef; # Do not change this to return; } read $_DAU_W_SOCK, $_buf, $_len; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } if ($_cnt == 1) { return (chop $_buf) ? $_MCE->{thaw}($_buf) : $_buf; } else { return @{ $_MCE->{thaw}($_buf) }; } } ## ------------------------------------------------------------------------- sub _mce_w_pending { my ($_Q) = @_; return $_Q->_pending(@_) if (exists $_Q->{_standalone}); local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_N_QUE . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF; chomp($_pending = <$_DAU_W_SOCK>); flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return $_pending; } sub _mce_w_insert { my ($_buf, $_tmp); my ($_Q, $_i) = (shift, shift); _croak('MCE::Queue::insert: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return $_Q->_insert($_i, @_) if (exists $_Q->{_standalone}); return unless (scalar @_); if (scalar @_ > 1 || ref $_[0] || !defined $_[0]) { $_tmp = $_MCE->{freeze}(\@_); $_buf = $_Q->{_id} . $LF . $_i . $LF . (length($_tmp) + 1) . $LF . $_tmp . '1'; } else { $_buf = $_Q->{_id} . $LF . $_i . $LF . (length($_[0]) + 1) . $LF . $_[0] . '0'; } local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_I_QUE . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_buf; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } sub _mce_w_insertp { my ($_buf, $_tmp); my ($_Q, $_p, $_i) = (shift, shift, shift); _croak('MCE::Queue::insertp: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); _croak('MCE::Queue::insertp: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return $_Q->_insertp($_p, $_i, @_) if (exists $_Q->{_standalone}); return unless (scalar @_); if (scalar @_ > 1 || ref $_[0] || !defined $_[0]) { $_tmp = $_MCE->{freeze}(\@_); $_buf = $_Q->{_id} . $LF . $_p . $LF . $_i . $LF . (length($_tmp) + 1) . $LF . $_tmp . '1'; } else { $_buf = $_Q->{_id} . $LF . $_p . $LF . $_i . $LF . (length($_[0]) + 1) . $LF . $_[0] . '0'; } local $\ = undef if (defined $\); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_I_QUP . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_buf; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return; } ## ------------------------------------------------------------------------- sub _mce_w_peek { my $_buf; my $_Q = shift; my $_i = shift || 0; _croak('MCE::Queue::peek: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return $_Q->_peek($_i, @_) if (exists $_Q->{_standalone}); { local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_P_QUE . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF . $_i . $LF; chomp($_len = <$_DAU_W_SOCK>); if ($_len < 0) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return undef; # Do not change this to return; } read $_DAU_W_SOCK, $_buf, $_len; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return (chop $_buf) ? $_MCE->{thaw}($_buf) : $_buf; } sub _mce_w_peekp { my $_buf; my ($_Q, $_p) = (shift, shift); my $_i = shift || 0; _croak('MCE::Queue::peekp: (priority) is not an integer') if (!looks_like_number($_p) || int($_p) != $_p); _croak('MCE::Queue::peekp: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return $_Q->_peekp($_p, $_i, @_) if (exists $_Q->{_standalone}); { local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_P_QUP . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF . $_p . $LF . $_i . $LF; chomp($_len = <$_DAU_W_SOCK>); if ($_len < 0) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return undef; # Do not change this to return; } read $_DAU_W_SOCK, $_buf, $_len; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return (chop $_buf) ? $_MCE->{thaw}($_buf) : $_buf; } sub _mce_w_peekh { my $_buf; my $_Q = shift; my $_i = shift || 0; _croak('MCE::Queue::peekh: (index) is not an integer') if (!looks_like_number($_i) || int($_i) != $_i); return $_Q->_peekh($_i, @_) if (exists $_Q->{_standalone}); { local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_P_QUH . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF . $_i . $LF; chomp($_len = <$_DAU_W_SOCK>); if ($_len < 0) { flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); return undef; # Do not change this to return; } read $_DAU_W_SOCK, $_buf, $_len; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return $_buf; } ## ------------------------------------------------------------------------- sub _mce_w_heap { my $_buf; my ($_Q) = @_; return $_Q->_heap(@_) if (exists $_Q->{_standalone}); { local $\ = undef if (defined $\); local $/ = $LF if (!$/ || $/ ne $LF); flock $_DAT_LOCK, LOCK_EX if ($_lock_chn); print {$_DAT_W_SOCK} OUTPUT_H_QUE . $LF . $_chn . $LF; print {$_DAU_W_SOCK} $_Q->{_id} . $LF; chomp($_len = <$_DAU_W_SOCK>); read $_DAU_W_SOCK, $_buf, $_len; flock $_DAT_LOCK, LOCK_UN if ($_lock_chn); } return @{ $_MCE->{thaw}($_buf) }; } } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Queue - Hybrid (normal and priority) queues for Many-Core Engine =head1 VERSION This document describes MCE::Queue version 1.608 =head1 SYNOPSIS use MCE; use MCE::Queue; my $F = MCE::Queue->new(fast => 1); my $consumers = 8; my $mce = MCE->new( task_end => sub { my ($mce, $task_id, $task_name) = @_; $F->enqueue((undef) x $consumers) if $task_name eq 'dir'; }, user_tasks => [{ max_workers => 1, task_name => 'dir', user_func => sub { ## Create a "standalone queue" only accessable to this worker. ## See included examples for running with multiple workers. my $D = MCE::Queue->new(queue => [ MCE->user_args->[0] ]); while (defined (my $dir = $D->dequeue_nb)) { my (@files, @dirs); foreach (glob("$dir/*")) { if (-d $_) { push @dirs, $_; next; } push @files, $_; } $D->enqueue(@dirs ) if scalar @dirs; $F->enqueue(@files) if scalar @files; } } },{ max_workers => $consumers, task_name => 'file', user_func => sub { while (defined (my $file = $F->dequeue)) { MCE->say($file); } } }] )->run({ user_args => [ $ARGV[0] || '.' ] }); __END__ Results from files_mce.pl and files_thr.pl; included with MCE. Usage: time ./files_mce.pl /usr 0 | wc -l time ./files_mce.pl /usr 1 | wc -l time ./files_thr.pl /usr | wc -l Darwin (OS) /usr: 216,271 files MCE::Queue, fast => 0 : 4.17s MCE::Queue, fast => 1 : 2.62s Thread::Queue : 4.14s Linux (VM) /usr: 186,154 files MCE::Queue, fast => 0 : 12.57s MCE::Queue, fast => 1 : 3.36s Thread::Queue : 5.91s Solaris (VM) /usr: 603,051 files MCE::Queue, fast => 0 : 39.04s MCE::Queue, fast => 1 : 18.08s Thread::Queue * Perl not built to support threads =head1 DESCRIPTION This module provides a queue interface supporting normal and priority queues and utilizing the IPC engine behind MCE. Data resides under the manager process. MCE::Queue also allows for a worker to create any number of queues locally not available to other workers including the manager process. Think of a CPU having L3 (shared) and L1 (local) cache. =head1 IMPORT Three options are available for overriding the default value for new queues. The porder option applies to priority queues only. use MCE::Queue porder => $MCE::Queue::HIGHEST, type => $MCE::Queue::FIFO, fast => 0; use MCE::Queue; # Same as above ## Possible values porder => $MCE::Queue::HIGHEST # Highest priority items dequeue first $MCE::Queue::LOWEST # Lowest priority items dequeue first type => $MCE::Queue::FIFO # First in, first out $MCE::Queue::LIFO # Last in, first out $MCE::Queue::LILO # (Synonym for FIFO) $MCE::Queue::FILO # (Synonym for LIFO) =head1 THREE RUN MODES MCE::Queue can be utilized under the following conditions: A) use MCE; B) use MCE::Queue; C) use MCE::Queue; use MCE::Queue; use MCE; =over 3 =item A) MCE is included prior to inclusion of MCE::Queue The dequeue method blocks for the manager process including workers. All data resides under the manager process. Workers send/request data via IPC. Creating a queue from within the worker process will cause the queue to run in local mode (C). The data resides under the worker process and not available to other workers including the manager process. =item B) MCE::Queue is included prior to inclusion of MCE Queues behave as if running in local mode for the manager and worker processes for the duration of the script. I cannot think of a use-case for this, but mentioning the behavior in the event MCE::Queue is included before MCE. =item C) MCE::Queue without inclusion of MCE The dequeue method is non-blocking. Queues behave similarly to local queuing. This mode is efficient due to minimum overhead and zero IPC behind the scene. Hence, MCE is not required to use MCE::Queue. =back =head1 API DOCUMENTATION =head2 MCE::Queue->new ( [ queue => \@array, fast => 1 ] ) This creates a new queue. Available options are queue, porder, type, fast, and gather. The gather option is mainly for running with MCE and wanting to pass item(s) to a callback function for appending to the queue. The 'fast' option speeds up ->dequeue ops and not enabled by default. It is beneficial for queues not calling ->clear or ->dequeue_nb and not altering the optional count value while running; e.g. ->dequeue($count). Basically, do not enable 'fast' if varying $count dynamically. use MCE; use MCE::Queue; my $q1 = MCE::Queue->new(); my $q2 = MCE::Queue->new( queue => [ 0, 1, 2 ] ); my $q3 = MCE::Queue->new( porder => $MCE::Queue::HIGHEST ); my $q4 = MCE::Queue->new( porder => $MCE::Queue::LOWEST ); my $q5 = MCE::Queue->new( type => $MCE::Queue::FIFO ); my $q6 = MCE::Queue->new( type => $MCE::Queue::LIFO ); my $q7 = MCE::Queue->new( fast => 1 ); Multiple queues may point to the same callback function. The first argument for the callback is the queue object. sub _append { my ($q, @items) = @_; $q->enqueue(@items); } my $q7 = MCE::Queue->new( gather => \&_append ); my $q8 = MCE::Queue->new( gather => \&_append ); ## Items are diverted to the callback function, not the queue. $q7->enqueue( 'apple', 'orange' ); The gather option allows one to store items temporarily while ensuring output order. Although a queue object is not required, this is simply a demonstration of the gather option in the context of a queue. use MCE; use MCE::Queue; sub preserve_order { my %tmp; my $order_id = 1; return sub { my ($q, $chunk_id, $data) = @_; $tmp{$chunk_id} = $data; while (1) { last unless exists $tmp{$order_id}; $q->enqueue( delete $tmp{$order_id++} ); } return; }; } my @squares; my $q = MCE::Queue->new( queue => \@squares, gather => preserve_order ); my $mce = MCE->new( chunk_size => 1, input_data => [ 1 .. 100 ], user_func => sub { $q->enqueue( MCE->chunk_id, $_ * $_ ); } ); $mce->run; print "@squares\n"; =head2 $q->clear ( void ) Clears the queue of any items. This has the effect of nulling the queue and the socket used for blocking. my @a; my $q = MCE::Queue->new( queue => \@a ); @a = (); ## bad, the blocking socket may become out of sync $q->clear; ## ok =head2 $q->enqueue ( $item [, $item, ... ] ) Appends a list of items onto the end of the normal queue. =head2 $q->enqueuep ( $p, $item [, $item, ... ] ) Appends a list of items onto the end of the priority queue with priority. =head2 $q->dequeue ( [ $count ] ) Returns the requested number of items (default 1) from the queue. Priority data will always dequeue first before any data from the normal queue. The method will block if the queue contains zero items. If the queue contains fewer than the requested number of items, the method will not block, but return the remaining items and undef for up to the count requested. The $count, used for requesting the number of items, is beneficial when workers are passing parameters through the queue. For this reason, always remember to dequeue using the same multiple for the count. This is unlike Thread::Queue which will block until the requested number of items are available. =head2 $q->dequeue_nb ( [ $count ] ) Returns the requested number of items (default 1) from the queue. Like with dequeue, priority data will always dequeue first. This method is non-blocking and will return undef in the absence of data from the queue. =head2 $q->insert ( $index, $item [, $item, ... ] ) Adds the list of items to the queue at the specified index position (0 is the head of the list). The head of the queue is that item which would be removed by a call to dequeue. $q = MCE::Queue->new( type => $MCE::Queue::FIFO ); $q->enqueue(1, 2, 3, 4); $q->insert(1, 'foo', 'bar'); # Queue now contains: 1, foo, bar, 2, 3, 4 $q = MCE::Queue->new( type => $MCE::Queue::LIFO ); $q->enqueue(1, 2, 3, 4); $q->insert(1, 'foo', 'bar'); # Queue now contains: 1, 2, 3, 'foo', 'bar', 4 =head2 $q->insertp ( $p, $index, $item [, $item, ... ] ) Adds the list of items to the queue at the specified index position with priority. The behavior is similarly to insert otherwise. =head2 $q->pending ( void ) Returns the number of items in the queue. The count includes both normal and priority data. $q = MCE::Queue->new(); $q->enqueuep(5, 'foo', 'bar'); $q->enqueue('sunny', 'day'); print $q->pending(), "\n"; # Output: 4 =head2 $q->peek ( [ $index ] ) Returns an item from the normal queue, at the specified index, without dequeuing anything. It defaults to the head of the queue if index is not specified. The head of the queue is that item which would be removed by a call to dequeue. Negative index values are supported, similarly to arrays. $q = MCE::Queue->new( type => $MCE::Queue::FIFO ); $q->enqueue(1, 2, 3, 4, 5); print $q->peek(1), ' ', $q->peek(-2), "\n"; # Output: 2 4 $q = MCE::Queue->new( type => $MCE::Queue::LIFO ); $q->enqueue(1, 2, 3, 4, 5); print $q->peek(1), ' ', $q->peek(-2), "\n"; # Output: 4 2 =head2 $q->peekp ( $p [, $index ] ) Returns an item from the queue with priority, at the specified index, without dequeuing anything. It defaults to the head of the queue if index is not specified. The behavior is similarly to peek otherwise. =head2 $q->peekh ( [ $index ] ) Returns an item from the heap, at the specified index. $q = MCE::Queue->new( porder => $MCE::Queue::HIGHEST ); $q->enqueuep(5, 'foo'); $q->enqueuep(6, 'bar'); $q->enqueuep(4, 'sun'); print $q->peekh(0), "\n"; # Output: 6 $q = MCE::Queue->new( porder => $MCE::Queue::LOWEST ); $q->enqueuep(5, 'foo'); $q->enqueuep(6, 'bar'); $q->enqueuep(4, 'sun'); print $q->peekh(0), "\n"; # Output: 4 =head2 $q->heap ( void ) Returns an array containing the heap data. Heap data consists of priority numbers, not the data. @h = $q->heap; # $MCE::Queue::HIGHEST # Heap contains: 6, 5, 4 @h = $q->heap; # $MCE::Queue::LOWEST # Heap contains: 4, 5, 6 =head1 ACKNOWLEDGEMENTS =over 3 =item L Two if statements were adopted for checking if the item belongs at the end or head of the queue. =item L The bsearch_num_pos method was helpful for accommodating the highest and lowest order in MCE::Queue. =item L MCE::Queue supports both normal and priority queues. =item L Thread::Queue is used as a template for identifying and documenting the methods. MCE::Queue is not fully compatible due to supporting normal and priority queues simultaneously; e.g. $q->enqueuep( $p, $item [, $item, ... ] ); ## Priority queue $q->enqueue( $item [, $item, ... ] ); ## Normal queue $q->dequeue( [ $count ] ); ## Priority data dequeues first $q->dequeue_nb( [ $count ] ); ## Behavior is not the same $q->pending(); ## Counts both normal/priority data ## in the queue =item L The recursion example, in the sysopsis above, was largely adopted from this module. =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Map.pm0000644000076400007640000004576212511673032013407 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Map - Parallel map model similar to the native map function. ## ############################################################################### package MCE::Map; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use Scalar::Util qw( looks_like_number ); use MCE; our $VERSION = '1.608'; our @CARP_NOT = qw( MCE ); ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $MAX_WORKERS = 'auto'; my $CHUNK_SIZE = 'auto'; my ($_MCE, $_loaded); my ($_params, $_prev_c); my $_tag = 'MCE::Map'; sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $MAX_WORKERS = shift and next if ( $_arg eq 'max_workers' ); $CHUNK_SIZE = shift and next if ( $_arg eq 'chunk_size' ); $MCE::FREEZE = $MCE::MCE->{freeze} = shift and next if ( $_arg eq 'freeze' ); $MCE::THAW = $MCE::MCE->{thaw} = shift and next if ( $_arg eq 'thaw' ); if ( $_arg eq 'sereal' ) { if (shift eq '1') { local $@; eval 'use Sereal qw(encode_sereal decode_sereal)'; unless ($@) { $MCE::FREEZE = $MCE::MCE->{freeze} = \&encode_sereal; $MCE::THAW = $MCE::MCE->{thaw} = \&decode_sereal; } } next; } if ( $_arg eq 'tmp_dir' ) { $MCE::TMP_DIR = $MCE::MCE->{tmp_dir} = shift; my $_e1 = 'is not a directory or does not exist'; my $_e2 = 'is not writeable'; _croak($_tag."::import: ($MCE::TMP_DIR) $_e1") unless -d $MCE::TMP_DIR; _croak($_tag."::import: ($MCE::TMP_DIR) $_e2") unless -w $MCE::TMP_DIR; next; } _croak($_tag."::import: ($_argument) is not a valid module argument"); } $MAX_WORKERS = MCE::Util::_parse_max_workers($MAX_WORKERS); _validate_number($MAX_WORKERS, 'MAX_WORKERS'); _validate_number($CHUNK_SIZE, 'CHUNK_SIZE') unless ($CHUNK_SIZE eq 'auto'); ## Import functions. no strict 'refs'; no warnings 'redefine'; my $_pkg = caller; *{ $_pkg.'::mce_map_f' } = \&run_file; *{ $_pkg.'::mce_map_s' } = \&run_seq; *{ $_pkg.'::mce_map' } = \&run; return; } END { return if (defined $_MCE && $_MCE->wid); finish(); } ############################################################################### ## ---------------------------------------------------------------------------- ## Gather callback for storing by chunk_id => chunk_ref into a hash. ## ############################################################################### my ($_total_chunks, %_tmp); sub _gather { my ($_chunk_id, $_data_ref) = @_; $_tmp{$_chunk_id} = $_data_ref; $_total_chunks++; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Init and finish routines. ## ############################################################################### sub init (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Map'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } finish(); $_params = (ref $_[0] eq 'HASH') ? shift : { @_ }; @_ = (); return; } sub finish () { if (defined $_MCE && $_MCE->{_spawned}) { MCE::_save_state; $_MCE->shutdown(); MCE::_restore_state; } $_prev_c = $_total_chunks = undef; undef %_tmp; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel map with MCE -- file. ## ############################################################################### sub run_file (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Map'); my $_code = shift; my $_file = shift; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{sequence} if (exists $_params->{sequence}); } else { $_params = {}; } if (defined $_file && ref $_file eq '' && $_file ne '') { _croak("$_tag: ($_file) does not exist") unless (-e $_file); _croak("$_tag: ($_file) is not readable") unless (-r $_file); _croak("$_tag: ($_file) is not a plain file") unless (-f $_file); $_params->{_file} = $_file; } elsif (ref $_file eq 'GLOB' || ref $_file eq 'SCALAR' || ref($_file) =~ /^IO::/) { $_params->{_file} = $_file; } else { _croak("$_tag: (file) is not specified or valid"); } @_ = (); return run($_code); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel map with MCE -- sequence. ## ############################################################################### sub run_seq (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Map'); my $_code = shift; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{_file} if (exists $_params->{_file}); } else { $_params = {}; } my ($_begin, $_end); if (ref $_[0] eq 'HASH') { $_begin = $_[0]->{begin}; $_end = $_[0]->{end}; $_params->{sequence} = $_[0]; } elsif (ref $_[0] eq 'ARRAY') { $_begin = $_[0]->[0]; $_end = $_[0]->[1]; $_params->{sequence} = $_[0]; } elsif (ref $_[0] eq '') { $_begin = $_[0]; $_end = $_[1]; $_params->{sequence} = [ @_ ]; } else { _croak("$_tag: (sequence) is not specified or valid"); } _croak("$_tag: (begin) is not specified for sequence") unless (defined $_begin); _croak("$_tag: (end) is not specified for sequence") unless (defined $_end); $_params->{sequence_run} = 1; @_ = (); return run($_code); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel map with MCE. ## ############################################################################### sub run (&@) { shift if (defined $_[0] && $_[0] eq 'MCE::Map'); my $_code = shift; $_total_chunks = 0; undef %_tmp; if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } my $_input_data; my $_max_workers = $MAX_WORKERS; my $_r = ref $_[0]; if ($_r eq 'ARRAY' || $_r eq 'CODE' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_input_data = shift; } if (defined $_params) { my $_p = $_params; $_max_workers = MCE::Util::_parse_max_workers($_p->{max_workers}) if (exists $_p->{max_workers}); delete $_p->{sequence} if (defined $_input_data || scalar @_); delete $_p->{user_func} if (exists $_p->{user_func}); delete $_p->{user_tasks} if (exists $_p->{user_tasks}); delete $_p->{use_slurpio} if (exists $_p->{use_slurpio}); delete $_p->{bounds_only} if (exists $_p->{bounds_only}); delete $_p->{gather} if (exists $_p->{gather}); } my $_chunk_size = MCE::Util::_parse_chunk_size( $CHUNK_SIZE, $_max_workers, $_params, $_input_data, scalar @_ ); if (defined $_params) { if (exists $_params->{_file}) { $_input_data = delete $_params->{_file}; } else { $_input_data = $_params->{input_data} if exists $_params->{input_data}; } } MCE::_save_state; ## ------------------------------------------------------------------------- if (!defined $_prev_c || $_prev_c != $_code) { $_MCE->shutdown() if (defined $_MCE); $_prev_c = $_code; my %_options = ( max_workers => $_max_workers, task_name => $_tag, user_func => sub { my ($_mce, $_chunk_ref, $_chunk_id) = @_; my $_wantarray = $_mce->{user_args}[0]; if ($_wantarray) { my @_a; if (ref $_chunk_ref eq 'SCALAR') { local $/ = $_mce->{RS} if defined $_mce->{RS}; open my $_MEM_FH, '<', $_chunk_ref; while ( <$_MEM_FH> ) { push @_a, &{ $_code }; } close $_MEM_FH; } else { if (ref $_chunk_ref) { push @_a, map { &{ $_code } } @{ $_chunk_ref }; } else { push @_a, map { &{ $_code } } $_chunk_ref; } } MCE->gather($_chunk_id, \@_a); } else { my $_cnt = 0; if (ref $_chunk_ref eq 'SCALAR') { local $/ = $_mce->{RS} if defined $_mce->{RS}; open my $_MEM_FH, '<', $_chunk_ref; while ( <$_MEM_FH> ) { $_cnt++; &{ $_code }; } close $_MEM_FH; } else { if (ref $_chunk_ref) { $_cnt += map { &{ $_code } } @{ $_chunk_ref }; } else { $_cnt += map { &{ $_code } } $_chunk_ref; } } MCE->gather($_cnt) if defined $_wantarray; } }, ); if (defined $_params) { for my $_p (keys %{ $_params }) { next if ($_p eq 'sequence_run'); next if ($_p eq 'input_data'); next if ($_p eq 'chunk_size'); _croak("MCE::Map: ($_p) is not a valid constructor argument") unless (exists $MCE::_valid_fields_new{$_p}); $_options{$_p} = $_params->{$_p}; } } $_MCE = MCE->new(%_options); } ## ------------------------------------------------------------------------- my $_cnt = 0; my $_wantarray = wantarray; $_MCE->{use_slurpio} = ($_chunk_size > MCE::MAX_RECS_SIZE) ? 1 : 0; $_MCE->{user_args} = [ $_wantarray ]; $_MCE->{gather} = $_wantarray ? \&_gather : sub { $_cnt += $_[0]; return; }; if (defined $_input_data) { @_ = (); $_MCE->process({ chunk_size => $_chunk_size }, $_input_data); delete $_MCE->{input_data}; } elsif (scalar @_) { $_MCE->process({ chunk_size => $_chunk_size }, \@_); delete $_MCE->{input_data}; } else { if (defined $_params && exists $_params->{sequence}) { $_MCE->run({ chunk_size => $_chunk_size, sequence => $_params->{sequence} }, 0); if (exists $_params->{sequence_run}) { delete $_params->{sequence_run}; delete $_params->{sequence}; } delete $_MCE->{sequence}; } } MCE::_restore_state; if (exists $_MCE->{_rla_return}) { $MCE::MCE->{_rla_return} = delete $_MCE->{_rla_return}; } finish() if ($^S); ## shutdown if in eval state if ($_wantarray) { return map { @{ $_ } } delete @_tmp{ 1 .. $_total_chunks }; } elsif (defined $_wantarray) { return $_cnt; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _croak { goto &MCE::_croak; } sub _validate_number { my ($_n, $_key) = @_; _croak("$_tag: ($_key) is not valid") if (!defined $_n); $_n =~ s/K\z//i; $_n =~ s/M\z//i; if (!looks_like_number($_n) || int($_n) != $_n || $_n < 1) { _croak("$_tag: ($_key) is not valid"); } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Map - Parallel map model similar to the native map function =head1 VERSION This document describes MCE::Map version 1.608 =head1 SYNOPSIS ## Exports mce_map, mce_map_f, and mce_map_s use MCE::Map; ## Array or array_ref my @a = mce_map { $_ * $_ } 1..10000; my @b = mce_map { $_ * $_ } [ 1..10000 ]; ## File_path, glob_ref, or scalar_ref my @c = mce_map_f { chomp; $_ } "/path/to/file"; my @d = mce_map_f { chomp; $_ } $file_handle; my @e = mce_map_f { chomp; $_ } \$scalar; ## Sequence of numbers (begin, end [, step, format]) my @f = mce_map_s { $_ * $_ } 1, 10000, 5; my @g = mce_map_s { $_ * $_ } [ 1, 10000, 5 ]; my @h = mce_map_s { $_ * $_ } { begin => 1, end => 10000, step => 5, format => undef }; =head1 DESCRIPTION This module provides a parallel map implementation via Many-Core Engine. MCE incurs a small overhead due to passing of data. A fast code block will run faster natively. However, the overhead will likely diminish as the complexity increases for the code. my @m1 = map { $_ * $_ } 1..1000000; ## 0.127 secs my @m2 = mce_map { $_ * $_ } 1..1000000; ## 0.304 secs Chunking, enabled by default, greatly reduces the overhead behind the scene. The time for mce_map below also includes the time for data exchanges between the manager and worker processes. More parallelization will be seen when the code incurs additional CPU time. sub calc { sqrt $_ * sqrt $_ / 1.3 * 1.5 / 3.2 * 1.07 } my @m1 = map { calc } 1..1000000; ## 0.367 secs my @m2 = mce_map { calc } 1..1000000; ## 0.365 secs Even faster is mce_map_s; useful when input data is a range of numbers. Workers generate sequences mathematically among themselves without any interaction from the manager process. Two arguments are required for mce_map_s (begin, end). Step defaults to 1 if begin is smaller than end, otherwise -1. my @m3 = mce_map_s { calc } 1, 1000000; ## 0.270 secs Although this document is about MCE::Map, the L module can write results immediately without waiting for all chunks to complete. This is made possible by passing the reference to an array (in this case @m4 and @m5). use MCE::Stream; sub calc { sqrt $_ * sqrt $_ / 1.3 * 1.5 / 3.2 * 1.07 } my @m4; mce_stream \@m4, sub { calc }, 1..1000000; ## Completes in 0.272 secs. This is amazing considering the ## overhead for passing data between the manager and workers. my @m5; mce_stream_s \@m5, sub { calc }, 1, 1000000; ## Completed in 0.176 secs. Like with mce_map_s, specifying a ## sequence specification turns out to be faster due to lesser ## overhead for the manager process. =head1 OVERRIDING DEFAULTS The following list 5 options which may be overridden when loading the module. use Sereal qw( encode_sereal decode_sereal ); use CBOR::XS qw( encode_cbor decode_cbor ); use JSON::XS qw( encode_json decode_json ); use MCE::Map max_workers => 4, ## Default 'auto' chunk_size => 100, ## Default 'auto' tmp_dir => "/path/to/app/tmp", ## $MCE::Signal::tmp_dir freeze => \&encode_sereal, ## \&Storable::freeze thaw => \&decode_sereal ## \&Storable::thaw ; There is a simpler way to enable Sereal with MCE 1.5. The following will attempt to use Sereal if available, otherwise defaults to Storable for serialization. use MCE::Map Sereal => 1; ## Serialization is by the Sereal module if available. my @m2 = mce_map { $_ * $_ } 1..10000; =head1 CUSTOMIZING MCE =over 3 =item MCE::Map->init ( options ) =item MCE::Map::init { options } The init function accepts a hash of MCE options. The gather option, if specified, is ignored due to being used internally by the module. use MCE::Map; MCE::Map::init { chunk_size => 1, max_workers => 4, user_begin => sub { print "## ", MCE->wid, " started\n"; }, user_end => sub { print "## ", MCE->wid, " completed\n"; } }; my @a = mce_map { $_ * $_ } 1..100; print "\n", "@a", "\n"; -- Output ## 2 started ## 1 started ## 3 started ## 4 started ## 1 completed ## 4 completed ## 2 completed ## 3 completed 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 1600 1681 1764 1849 1936 2025 2116 2209 2304 2401 2500 2601 2704 2809 2916 3025 3136 3249 3364 3481 3600 3721 3844 3969 4096 4225 4356 4489 4624 4761 4900 5041 5184 5329 5476 5625 5776 5929 6084 6241 6400 6561 6724 6889 7056 7225 7396 7569 7744 7921 8100 8281 8464 8649 8836 9025 9216 9409 9604 9801 10000 =back =head1 API DOCUMENTATION =over 3 =item MCE::Map->run ( sub { code }, iterator ) =item mce_map { code } iterator An iterator reference can by specified for input_data. Iterators are described under "SYNTAX for INPUT_DATA" at L. my @a = mce_map { $_ * 2 } make_iterator(10, 30, 2); =item MCE::Map->run ( sub { code }, list ) =item mce_map { code } list Input data can be defined using a list. my @a = mce_map { $_ * 2 } 1..1000; my @b = mce_map { $_ * 2 } [ 1..1000 ]; =item MCE::Map->run_file ( sub { code }, file ) =item mce_map_f { code } file The fastest of these is the /path/to/file. Workers communicate the next offset position among themselves without any interaction from the manager process. my @c = mce_map_f { chomp; $_ . "\r\n" } "/path/to/file"; my @d = mce_map_f { chomp; $_ . "\r\n" } $file_handle; my @e = mce_map_f { chomp; $_ . "\r\n" } \$scalar; =item MCE::Map->run_seq ( sub { code }, $beg, $end [, $step, $fmt ] ) =item mce_map_s { code } $beg, $end [, $step, $fmt ] Sequence can be defined as a list, an array reference, or a hash reference. The functions require both begin and end values to run. Step and format are optional. The format is passed to sprintf (% may be omitted below). my ($beg, $end, $step, $fmt) = (10, 20, 0.1, "%4.1f"); my @f = mce_map_s { $_ } $beg, $end, $step, $fmt; my @g = mce_map_s { $_ } [ $beg, $end, $step, $fmt ]; my @h = mce_map_s { $_ } { begin => $beg, end => $end, step => $step, format => $fmt }; =back =head1 MANUAL SHUTDOWN =over 3 =item MCE::Map->finish =item MCE::Map::finish Workers remain persistent as much as possible after running. Shutdown occurs automatically when the script terminates. Call finish when workers are no longer needed. use MCE::Map; MCE::Map::init { chunk_size => 20, max_workers => 'auto' }; my @a = mce_map { ... } 1..100; MCE::Map::finish; =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Stream.pm0000644000076400007640000007101712511673054014121 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Stream - Parallel stream model for chaining multiple maps and greps. ## ############################################################################### package MCE::Stream; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use Scalar::Util qw( looks_like_number ); use MCE; use MCE::Queue; our $VERSION = '1.608'; our @CARP_NOT = qw( MCE ); ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $DEFAULT_MODE = 'map'; my $MAX_WORKERS = 'auto'; my $CHUNK_SIZE = 'auto'; my $FAST = 0; my ($_params, @_prev_c, @_prev_m, @_prev_n, @_prev_w, @_user_tasks, @_queue); my ($_MCE, $_loaded); my $_tag = 'MCE::Stream'; sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $DEFAULT_MODE = shift and next if ( $_arg eq 'default_mode' ); $MAX_WORKERS = shift and next if ( $_arg eq 'max_workers' ); $CHUNK_SIZE = shift and next if ( $_arg eq 'chunk_size' ); $MCE::FREEZE = $MCE::MCE->{freeze} = shift and next if ( $_arg eq 'freeze' ); $MCE::THAW = $MCE::MCE->{thaw} = shift and next if ( $_arg eq 'thaw' ); if ( $_arg eq 'sereal' ) { if (shift eq '1') { local $@; eval 'use Sereal qw(encode_sereal decode_sereal)'; unless ($@) { $MCE::FREEZE = $MCE::MCE->{freeze} = \&encode_sereal; $MCE::THAW = $MCE::MCE->{thaw} = \&decode_sereal; } } next; } if ( $_arg eq 'tmp_dir' ) { $MCE::TMP_DIR = $MCE::MCE->{tmp_dir} = shift; my $_e1 = 'is not a directory or does not exist'; my $_e2 = 'is not writeable'; _croak($_tag."::import: ($MCE::TMP_DIR) $_e1") unless -d $MCE::TMP_DIR; _croak($_tag."::import: ($MCE::TMP_DIR) $_e2") unless -w $MCE::TMP_DIR; next; } if ( $_arg eq 'fast' ) { $FAST = 1 if (shift eq '1'); next; } _croak($_tag."::import: ($_argument) is not a valid module argument"); } _croak("$_tag: (DEFAULT_MODE) is not valid") if ($DEFAULT_MODE ne 'grep' && $DEFAULT_MODE ne 'map'); $MAX_WORKERS = MCE::Util::_parse_max_workers($MAX_WORKERS); _validate_number($MAX_WORKERS, 'MAX_WORKERS'); _validate_number($CHUNK_SIZE, 'CHUNK_SIZE') unless ($CHUNK_SIZE eq 'auto'); ## Import functions. no strict 'refs'; no warnings 'redefine'; my $_pkg = caller; *{ $_pkg.'::mce_stream_f' } = \&run_file; *{ $_pkg.'::mce_stream_s' } = \&run_seq; *{ $_pkg.'::mce_stream' } = \&run; return; } END { return if (defined $_MCE && $_MCE->wid); finish(); } ############################################################################### ## ---------------------------------------------------------------------------- ## Gather callback to ensure chunk order is preserved during gathering. ## Also, the task end callback for when a task completes. ## ############################################################################### my ($_gather_ref, $_order_id, %_tmp); sub _preserve_order { $_tmp{$_[1]} = $_[0]; if (defined $_gather_ref) { while (1) { last unless exists $_tmp{$_order_id}; push @{ $_gather_ref }, @{ delete $_tmp{$_order_id++} }; } } else { $_order_id++; } return; } sub _task_end { my ($_mce, $_task_id, $_task_name) = @_; if (defined $_mce->{user_tasks}->[$_task_id + 1]) { my $n_workers = $_mce->{user_tasks}->[$_task_id + 1]->{max_workers}; my $_queue_id = @_queue - $_task_id - 1; $_queue[$_queue_id]->enqueue((undef) x $n_workers); } $_params->{task_end}->($_mce, $_task_id, $_task_name) if (exists $_params->{task_end} && ref $_params->{task_end} eq 'CODE'); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Init and finish routines. ## ############################################################################### sub init (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Stream'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } finish(); $_params = (ref $_[0] eq 'HASH') ? shift : { @_ }; @_ = (); return; } sub finish () { if (defined $_MCE && $_MCE->{_spawned}) { MCE::_save_state; $_MCE->shutdown(); MCE::_restore_state; } $_gather_ref = $_order_id = undef; undef %_tmp; @_user_tasks = (); @_prev_w = (); @_prev_n = (); @_prev_m = (); @_prev_c = (); $_->DESTROY() for (@_queue); @_queue = (); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel stream with MCE -- file. ## ############################################################################### sub run_file (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Stream'); my ($_file, $_pos); my $_start_pos = (ref $_[0] eq 'HASH') ? 2 : 1; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{sequence} if (exists $_params->{sequence}); } else { $_params = {}; } for my $_i ($_start_pos .. @_ - 1) { my $_r = ref $_[$_i]; if ($_r eq '' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_file = $_[$_i]; $_pos = $_i; last; } } if (defined $_file && ref $_file eq '' && $_file ne '') { _croak("$_tag: ($_file) does not exist") unless (-e $_file); _croak("$_tag: ($_file) is not readable") unless (-r $_file); _croak("$_tag: ($_file) is not a plain file") unless (-f $_file); $_params->{_file} = $_file; } elsif (ref $_file eq 'GLOB' || ref $_file eq 'SCALAR' || ref($_file) =~ /^IO::/) { $_params->{_file} = $_file; } else { _croak("$_tag: (file) is not specified or valid"); } if (defined $_pos) { pop @_ for ($_pos .. @_ - 1); } return run(@_); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel stream with MCE -- sequence. ## ############################################################################### sub run_seq (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Stream'); my ($_begin, $_end, $_pos); my $_start_pos = (ref $_[0] eq 'HASH') ? 2 : 1; if (defined $_params) { delete $_params->{sequence} if (exists $_params->{sequence}); delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{_file} if (exists $_params->{_file}); } else { $_params = {}; } for my $_i ($_start_pos .. @_ - 1) { my $_ref = ref $_[$_i]; if ($_ref eq '' || $_ref eq 'HASH' || $_ref eq 'ARRAY') { $_pos = $_i; if ($_ref eq '') { $_begin = $_[$_pos]; $_end = $_[$_pos + 1]; $_params->{sequence} = [ $_[$_pos], $_[$_pos + 1], $_[$_pos + 2], $_[$_pos + 3] ]; } elsif ($_ref eq 'HASH') { $_begin = $_[$_pos]->{begin}; $_end = $_[$_pos]->{end}; $_params->{sequence} = $_[$_pos]; } elsif ($_ref eq 'ARRAY') { $_begin = $_[$_pos]->[0]; $_end = $_[$_pos]->[1]; $_params->{sequence} = $_[$_pos]; } last; } } _croak("$_tag: (sequence) is not specified or valid") unless (exists $_params->{sequence}); _croak("$_tag: (begin) is not specified for sequence") unless (defined $_begin); _croak("$_tag: (end) is not specified for sequence") unless (defined $_end); $_params->{sequence_run} = 1; if (defined $_pos) { pop @_ for ($_pos .. @_ - 1); } return run(@_); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel stream with MCE. ## ############################################################################### sub run (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Stream'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } if (ref $_[0] eq 'HASH' && !exists $_[0]->{code}) { $_params = {} unless defined $_params; for my $_p (keys %{ $_[0] }) { $_params->{$_p} = $_[0]->{$_p}; } shift; } my $_aref; $_aref = shift if (ref $_[0] eq 'ARRAY'); $_order_id = 1; undef %_tmp; if (defined $_aref) { $_gather_ref = $_aref; @{ $_aref } = (); } ## ------------------------------------------------------------------------- my (@_code, @_mode, @_name, @_wrks); my $_init_mce = 0; my $_pos = 0; while (ref $_[0] eq 'CODE' || ref $_[0] eq 'HASH') { if (ref $_[0] eq 'CODE') { push @_code, $_[0]; push @_mode, $DEFAULT_MODE; } else { push @_code, exists $_[0]->{code} ? $_[0]->{code} : undef; push @_mode, exists $_[0]->{mode} ? $_[0]->{mode} : $DEFAULT_MODE; unless (ref $_code[-1] eq 'CODE') { @_ = (); _croak("$_tag: (code) is not valid"); } if ($_mode[-1] ne 'grep' && $_mode[-1] ne 'map') { @_ = (); _croak("$_tag: (mode) is not valid"); } } push @_name, (defined $_params && ref $_params->{task_name} eq 'ARRAY') ? $_params->{task_name}->[$_pos] : undef; push @_wrks, (defined $_params && ref $_params->{max_workers} eq 'ARRAY') ? $_params->{max_workers}->[$_pos] : undef; $_init_mce = 1 if (!defined $_prev_c[$_pos] || $_prev_c[$_pos] != $_code[$_pos]); $_init_mce = 1 if (!defined $_prev_m[$_pos] || $_prev_m[$_pos] ne $_mode[$_pos]); { no warnings; $_init_mce = 1 if ($_prev_n[$_pos] ne $_name[$_pos]); $_init_mce = 1 if ($_prev_w[$_pos] ne $_wrks[$_pos]); } $_prev_c[$_pos] = $_code[$_pos]; $_prev_m[$_pos] = $_mode[$_pos]; $_prev_n[$_pos] = $_name[$_pos]; $_prev_w[$_pos] = $_wrks[$_pos]; shift; $_pos++; } if (defined $_prev_c[$_pos]) { pop @_prev_c for ($_pos .. @_prev_c - 1); pop @_prev_m for ($_pos .. @_prev_m - 1); pop @_prev_n for ($_pos .. @_prev_n - 1); pop @_prev_w for ($_pos .. @_prev_w - 1); $_init_mce = 1; } return unless (scalar @_code); ## ------------------------------------------------------------------------- my $_input_data; my $_max_workers = $MAX_WORKERS; my $_r = ref $_[0]; if ($_r eq 'ARRAY' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_input_data = shift; } if (defined $_params) { my $_p = $_params; $_max_workers = MCE::Util::_parse_max_workers($_p->{max_workers}) if (exists $_p->{max_workers} && ref $_p->{max_workers} ne 'ARRAY'); delete $_p->{sequence} if (defined $_input_data || scalar @_); delete $_p->{user_func} if (exists $_p->{user_func}); delete $_p->{user_tasks} if (exists $_p->{user_tasks}); delete $_p->{use_slurpio} if (exists $_p->{use_slurpio}); delete $_p->{bounds_only} if (exists $_p->{bounds_only}); delete $_p->{gather} if (exists $_p->{gather}); } if (@_code > 1 && $_max_workers > 1) { $_max_workers = int($_max_workers / @_code + 0.5) + 1; } my $_chunk_size = MCE::Util::_parse_chunk_size( $CHUNK_SIZE, $_max_workers, $_params, $_input_data, scalar @_ ); if (defined $_params) { if (exists $_params->{_file}) { $_input_data = delete $_params->{_file}; } else { $_input_data = $_params->{input_data} if exists $_params->{input_data}; } } MCE::_save_state; ## ------------------------------------------------------------------------- if ($_init_mce) { $_MCE->shutdown() if (defined $_MCE); pop( @_queue )->DESTROY for (@_code .. @_queue); push @_queue, MCE::Queue->new(fast => $FAST) for (@_queue .. @_code - 2); _gen_user_tasks(\@_queue, \@_code, \@_mode, \@_name, \@_wrks); my %_options = ( max_workers => $_max_workers, task_name => $_tag, user_tasks => \@_user_tasks, task_end => \&_task_end, use_slurpio => 0, ); if (defined $_params) { local $_; my $_p = $_params; for (keys %{ $_p }) { next if ($_ eq 'sequence_run'); next if ($_ eq 'max_workers' && ref $_p->{max_workers} eq 'ARRAY'); next if ($_ eq 'task_name' && ref $_p->{task_name} eq 'ARRAY'); next if ($_ eq 'input_data'); next if ($_ eq 'chunk_size'); next if ($_ eq 'task_end'); _croak("MCE::Stream: ($_) is not a valid constructor argument") unless (exists $MCE::_valid_fields_new{$_}); $_options{$_} = $_p->{$_}; } } $_MCE = MCE->new(%_options); } else { ## Workers may persist after running. Thus, updating the MCE instance. ## These options do not require respawning. if (defined $_params) { for my $_p (qw( RS interval stderr_file stdout_file user_error user_output job_delay submit_delay on_post_exit on_post_run user_args flush_file flush_stderr flush_stdout )) { $_MCE->{$_p} = $_params->{$_p} if (exists $_params->{$_p}); } } } ## ------------------------------------------------------------------------- if (defined $_input_data) { @_ = (); $_MCE->process({ chunk_size => $_chunk_size }, $_input_data); delete $_MCE->{input_data}; } elsif (scalar @_) { $_MCE->process({ chunk_size => $_chunk_size }, \@_); delete $_MCE->{input_data}; } else { if (defined $_params && exists $_params->{sequence}) { $_MCE->run({ chunk_size => $_chunk_size, sequence => $_params->{sequence} }, 0); if (exists $_params->{sequence_run}) { delete $_params->{sequence_run}; delete $_params->{sequence}; } delete $_MCE->{sequence}; } } MCE::_restore_state; if (exists $_MCE->{_rla_return}) { $MCE::MCE->{_rla_return} = delete $_MCE->{_rla_return}; } finish() if ($^S); ## shutdown if in eval state return map { @{ $_ } } delete @_tmp{ 1 .. $_order_id - 1 } unless (defined $_aref); $_gather_ref = undef; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _croak { goto &MCE::_croak; } sub _gen_user_tasks { my ($_queue_ref, $_code_ref, $_mode_ref, $_name_ref, $_wrks_ref) = @_; @_user_tasks = (); ## For the code block farthest to the right. push @_user_tasks, { task_name => $_name_ref->[-1], max_workers => $_wrks_ref->[-1], gather => (@{ $_code_ref } > 1) ? $_queue_ref->[-1] : \&_preserve_order, user_func => sub { my ($_mce, $_chunk_ref, $_chunk_id) = @_; my @_a; my $_code = $_code_ref->[-1]; if (ref $_chunk_ref) { push @_a, ($_mode_ref->[-1] eq 'map') ? map { &{ $_code } } @{ $_chunk_ref } : grep { &{ $_code } } @{ $_chunk_ref }; } else { push @_a, ($_mode_ref->[-1] eq 'map') ? map { &{ $_code } } $_chunk_ref : grep { &{ $_code } } $_chunk_ref; } MCE->gather( (@{ $_code_ref } > 1) ? MCE->freeze([ \@_a, $_chunk_id ]) : (\@_a, $_chunk_id) ); } }; ## For in-between code blocks (processed from right to left). for (my $_i = @{ $_code_ref } - 2; $_i > 0; $_i--) { my $_pos = $_i; push @_user_tasks, { task_name => $_name_ref->[$_pos], max_workers => $_wrks_ref->[$_pos], gather => $_queue_ref->[$_pos - 1], user_func => sub { my $_q = $_queue_ref->[$_pos]; while (1) { my $_chunk = $_q->dequeue; last unless (defined $_chunk); my @_a; my $_code = $_code_ref->[$_pos]; $_chunk = MCE->thaw($_chunk); push @_a, ($_mode_ref->[$_pos] eq 'map') ? map { &{ $_code } } @{ $_chunk->[0] } : grep { &{ $_code } } @{ $_chunk->[0] }; MCE->gather(MCE->freeze([ \@_a, $_chunk->[1] ])); } return; } }; } ## For the left-most code block. if (@{ $_code_ref } > 1) { push @_user_tasks, { task_name => $_name_ref->[0], max_workers => $_wrks_ref->[0], gather => \&_preserve_order, user_func => sub { my $_q = $_queue_ref->[0]; while (1) { my $_chunk = $_q->dequeue; last unless (defined $_chunk); my @_a; my $_code = $_code_ref->[0]; $_chunk = MCE->thaw($_chunk); push @_a, ($_mode_ref->[0] eq 'map') ? map { &{ $_code } } @{ $_chunk->[0] } : grep { &{ $_code } } @{ $_chunk->[0] }; MCE->gather(\@_a, $_chunk->[1]); } return; } }; } return; } sub _validate_number { my ($_n, $_key) = @_; _croak("$_tag: ($_key) is not valid") if (!defined $_n); $_n =~ s/K\z//i; $_n =~ s/M\z//i; if (!looks_like_number($_n) || int($_n) != $_n || $_n < 1) { _croak("$_tag: ($_key) is not valid"); } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Stream - Parallel stream model for chaining multiple maps and greps =head1 VERSION This document describes MCE::Stream version 1.608 =head1 SYNOPSIS ## Exports mce_stream, mce_stream_f, mce_stream_s use MCE::Stream; my (@m1, @m2, @m3); ## Default mode is map and processed from right-to-left @m1 = mce_stream sub { $_ * 3 }, sub { $_ * 2 }, 1..10000; mce_stream \@m2, sub { $_ * 3 }, sub { $_ * 2 }, 1..10000; ## Native Perl @m3 = map { $_ * $_ } grep { $_ % 5 == 0 } 1..10000; ## Streaming grep and map in parallel mce_stream \@m3, { mode => 'map', code => sub { $_ * $_ } }, { mode => 'grep', code => sub { $_ % 5 == 0 } }, 1..10000; ## Array or array_ref my @a = mce_stream sub { $_ * $_ }, 1..10000; my @b = mce_stream sub { $_ * $_ }, [ 1..10000 ]; ## File_path, glob_ref, or scalar_ref my @c = mce_stream_f sub { chomp; $_ }, "/path/to/file"; my @d = mce_stream_f sub { chomp; $_ }, $file_handle; my @e = mce_stream_f sub { chomp; $_ }, \$scalar; ## Sequence of numbers (begin, end [, step, format]) my @f = mce_stream_s sub { $_ * $_ }, 1, 10000, 5; my @g = mce_stream_s sub { $_ * $_ }, [ 1, 10000, 5 ]; my @h = mce_stream_s sub { $_ * $_ }, { begin => 1, end => 10000, step => 5, format => undef }; =head1 DESCRIPTION This module allows one to stream multiple map and/or grep operations in parallel. Code blocks run simultaneously from right-to-left. The results are appended immediately when providing a reference to an array. ## Appends are serialized, even out-of-order ok, but immediately. ## Out-of-order chunks are held temporarily until ordered chunks ## arrive. mce_stream \@a, sub { $_ }, sub { $_ }, sub { $_ }, 1..10000; ## input ## chunk1 input ## chunk3 chunk2 input ## chunk2 chunk2 chunk3 input ## append1 chunk3 chunk1 chunk4 input ## append2 chunk1 chunk5 chunk5 input ## append3 chunk5 chunk4 chunk6 ... ## append4 chunk4 chunk6 ... ## append5 chunk6 ... ## append6 ... ## ... ## MCE incurs a small overhead due to passing of data. A fast code block will run faster natively when chaining multiple map functions. However, the overhead will likely diminish as the complexity increases for the code. ## 0.334 secs -- baseline using the native map function my @m1 = map { $_ * 4 } map { $_ * 3 } map { $_ * 2 } 1..1000000; ## 0.427 secs -- this is quite amazing considering data passing my @m2 = mce_stream sub { $_ * 4 }, sub { $_ * 3 }, sub { $_ * 2 }, 1..1000000; ## 0.355 secs -- appends to @m3 immediately, not after running my @m3; mce_stream \@m3, sub { $_ * 4 }, sub { $_ * 3 }, sub { $_ * 2 }, 1..1000000; Even faster is mce_stream_s; useful when input data is a range of numbers. Workers generate sequences mathematically among themselves without any interaction from the manager process. Two arguments are required for mce_stream_s (begin, end). Step defaults to 1 if begin is smaller than end, otherwise -1. ## 0.278 secs -- numbers are generated mathematically via sequence my @m4; mce_stream_s \@m4, sub { $_ * 4 }, sub { $_ * 3 }, sub { $_ * 2 }, 1, 1000000; =head1 OVERRIDING DEFAULTS The following list 7 options which may be overridden when loading the module. use Sereal qw( encode_sereal decode_sereal ); use CBOR::XS qw( encode_cbor decode_cbor ); use JSON::XS qw( encode_json decode_json ); use MCE::Stream default_mode => 'grep', ## Default 'map' max_workers => 8, ## Default 'auto' chunk_size => 500, ## Default 'auto' fast => 1, ## Default 0 (fast queue?) tmp_dir => "/path/to/app/tmp", ## $MCE::Signal::tmp_dir freeze => \&encode_sereal, ## \&Storable::freeze thaw => \&decode_sereal ## \&Storable::thaw ; There is a simpler way to enable Sereal with MCE 1.5. The following will attempt to use Sereal if available, otherwise defaults to Storable for serialization. use MCE::Stream Sereal => 1; ## Serialization is by the Sereal module if available. my @m2 = mce_stream sub { $_ * $_ }, 1..10000; =head1 CUSTOMIZING MCE =over 3 =item MCE::Stream->init ( options ) =item MCE::Stream::init { options } The init function accepts a hash of MCE options. The gather and bounds_only options, if specified, are ignored due to being used internally by the module (not shown below). use MCE::Stream; MCE::Stream::init { chunk_size => 1, max_workers => 4, user_begin => sub { print "## ", MCE->wid, " started\n"; }, user_end => sub { print "## ", MCE->wid, " completed\n"; } }; my @a = mce_stream sub { $_ * $_ }, 1..100; print "\n", "@a", "\n"; -- Output ## 1 started ## 2 started ## 3 started ## 4 started ## 3 completed ## 1 completed ## 2 completed ## 4 completed 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 1600 1681 1764 1849 1936 2025 2116 2209 2304 2401 2500 2601 2704 2809 2916 3025 3136 3249 3364 3481 3600 3721 3844 3969 4096 4225 4356 4489 4624 4761 4900 5041 5184 5329 5476 5625 5776 5929 6084 6241 6400 6561 6724 6889 7056 7225 7396 7569 7744 7921 8100 8281 8464 8649 8836 9025 9216 9409 9604 9801 10000 =back Like with MCE::Stream::init above, MCE options may be specified using an anonymous hash for the first argument. Notice how both max_workers and task_name can take an anonymous array for setting values uniquely for each code block. Remember that MCE::Stream processes from right-to-left when setting the individual values. use MCE::Stream; my @a = mce_stream { task_name => [ 'c', 'b', 'a' ], max_workers => [ 2, 4, 3, ], user_end => sub { my ($mce, $task_id, $task_name) = @_; print "$task_id - $task_name completed\n"; }, task_end => sub { my ($mce, $task_id, $task_name) = @_; MCE->print("$task_id - $task_name ended\n"); } }, sub { $_ * 4 }, ## 2 workers, named c sub { $_ * 3 }, ## 4 workers, named b sub { $_ * 2 }, 1..10000; ## 3 workers, named a -- Output 0 - a completed 0 - a completed 0 - a completed 0 - a ended 1 - b completed 1 - b completed 1 - b completed 1 - b completed 1 - b ended 2 - c completed 2 - c completed 2 - c ended Note that the anonymous hash, for specifying options, also comes first when passing an array reference. my @a; mce_stream { ... }, \@a, sub { ... }, sub { ... }, 1..10000; =head1 API DOCUMENTATION Scripts using MCE::Stream can be written using the long or short form. The long form becomes relevant when mixing modes. Again, processing occurs from right-to-left. my @m3 = mce_stream { mode => 'map', code => sub { $_ * $_ } }, { mode => 'grep', code => sub { $_ % 5 == 0 } }, 1..10000; my @m4; mce_stream \@m4, { mode => 'map', code => sub { $_ * $_ } }, { mode => 'grep', code => sub { $_ % 5 == 0 } }, 1..10000; For multiple grep blocks, the short form can be used. Simply specify the default mode for the module. The two valid values for default_mode is 'grep' and 'map'. use MCE::Stream default_mode => 'grep'; my @f = mce_stream_f sub { /ending$/ }, sub { /^starting/ }, $file; The following assumes 'map' for default_mode in order to demonstrate all the possibilities of passing input data into the code block. =over 3 =item MCE::Stream->run ( { input_data => iterator }, sub { code } ) =item mce_stream { input_data => iterator }, sub { code } An iterator reference can by specified for input_data. The only other way is to specify input_data via MCE::Stream::init. This prevents MCE::Stream from configuring the iterator reference as another user task which will not work. Iterators are described under "SYNTAX for INPUT_DATA" at L. MCE::Stream::init { input_data => iterator }; my @a = mce_stream sub { $_ * 3 }, sub { $_ * 2 }; =item MCE::Stream->run ( sub { code }, list ) =item mce_stream sub { code }, list Input data can be defined using a list. my @a = mce_stream sub { $_ * 2 }, 1..1000; my @b = mce_stream sub { $_ * 2 }, [ 1..1000 ]; =item MCE::Stream->run_file ( sub { code }, file ) =item mce_stream_f sub { code }, file The fastest of these is the /path/to/file. Workers communicate the next offset position among themselves without any interaction from the manager process. my @c = mce_stream_f sub { chomp; $_ . "\r\n" }, "/path/to/file"; my @d = mce_stream_f sub { chomp; $_ . "\r\n" }, $file_handle; my @e = mce_stream_f sub { chomp; $_ . "\r\n" }, \$scalar; =item MCE::Stream->run_seq ( sub { code }, $beg, $end [, $step, $fmt ] ) =item mce_stream_s sub { code }, $beg, $end [, $step, $fmt ] Sequence can be defined as a list, an array reference, or a hash reference. The functions require both begin and end values to run. Step and format are optional. The format is passed to sprintf (% may be omitted below). my ($beg, $end, $step, $fmt) = (10, 20, 0.1, "%4.1f"); my @f = mce_stream_s sub { $_ }, $beg, $end, $step, $fmt; my @g = mce_stream_s sub { $_ }, [ $beg, $end, $step, $fmt ]; my @h = mce_stream_s sub { $_ }, { begin => $beg, end => $end, step => $step, format => $fmt }; =back =head1 MANUAL SHUTDOWN =over 3 =item MCE::Stream->finish =item MCE::Stream::finish Workers remain persistent as much as possible after running. Shutdown occurs automatically when the script terminates. Call finish when workers are no longer needed. use MCE::Stream; MCE::Stream::init { chunk_size => 20, max_workers => 'auto' }; my @a = mce_stream { ... } 1..100; MCE::Stream::finish; =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Mutex.pm0000644000076400007640000001004512511673035013761 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Mutex - Simple semaphore for Many-Core Engine. ## ############################################################################### package MCE::Mutex; use strict; use warnings; no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use MCE::Util qw( $LF ); use bytes; our $VERSION = '1.608'; sub DESTROY { my ($_mutex, $_arg) = @_; my $_id = $INC{'threads.pm'} ? $$ .'.'. threads->tid() : $$; $_mutex->unlock() if ($_mutex->{ $_id }); if (!defined $_arg || $_arg ne 'shutdown') { return if (defined $MCE::VERSION && !defined $MCE::MCE->{_wid}); return if (defined $MCE::MCE && $MCE::MCE->{_wid}); } MCE::Util::_destroy_sockets($_mutex, qw(_w_sock _r_sock)); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Public methods. ## ############################################################################### sub new { my ($_class, %_argv) = @_; @_ = (); my $_mutex = {}; bless($_mutex, ref($_class) || $_class); MCE::Util::_make_socket_pair($_mutex, qw(_w_sock _r_sock)); syswrite($_mutex->{_w_sock}, '0'); return $_mutex; } sub lock { my $_mutex = shift; my $_id = $INC{'threads.pm'} ? $$ .'.'. threads->tid() : $$; unless ($_mutex->{ $_id }) { sysread($_mutex->{_r_sock}, my $_b, 1); $_mutex->{ $_id } = 1; } return; } sub unlock { my $_mutex = shift; my $_id = $INC{'threads.pm'} ? $$ .'.'. threads->tid() : $$; if ($_mutex->{ $_id }) { syswrite($_mutex->{_w_sock}, '0'); $_mutex->{ $_id } = 0; } return; } sub synchronize { my ($_mutex, $_code) = (shift, shift); if (ref $_code eq 'CODE') { if (defined wantarray) { $_mutex->lock(); my @_a = $_code->(@_); $_mutex->unlock(); return wantarray ? @_a : $_a[0]; } else { $_mutex->lock(); $_code->(@_); $_mutex->unlock(); } } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Mutex - Simple semaphore for Many-Core Engine =head1 VERSION This document describes MCE::Mutex version 1.608 =head1 SYNOPSIS use MCE::Flow max_workers => 4; use MCE::Mutex; print "## running a\n"; my $a = MCE::Mutex->new; mce_flow sub { $a->lock; ## access shared resource my $wid = MCE->wid; MCE->say($wid); sleep 1; $a->unlock; }; print "## running b\n"; my $b = MCE::Mutex->new; mce_flow sub { $b->synchronize( sub { ## access shared resource my ($wid) = @_; MCE->say($wid); sleep 1; }, MCE->wid ); }; =head1 DESCRIPTION This module implements locking methods that can be used to coordinate access to shared data from multiple workers spawned as processes or threads. The inspiration for this module came from reading Mutex for Ruby. =head1 API DOCUMENTATION =head2 MCE::Mutex->new ( void ) Creates a new mutex. =head2 $m->lock ( void ) Attempts to grab the lock and waits if not available. Multiple calls to mutex->lock by the same process or thread is safe. The mutex will remain locked until mutex->unlock is called. =head2 $m->unlock ( void ) Releases the lock. A held lock by an exiting process or thread is released automatically. =head2 $m->synchronize ( sub { ... }, @_ ) Obtains a lock, runs the code block, and releases the lock after the block completes. Optionally, the method is wantarray aware. my $value = $m->synchronize( sub { ## access shared resource 'value'; }); =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Candy.pm0000644000076400007640000003014212511673016013714 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Candy - Sugar methods and output iterators for Many-Core Engine. ## ############################################################################### package MCE::Candy; use strict; use warnings; our $VERSION = '1.608'; our @CARP_NOT = qw( MCE ); no warnings 'threads'; no warnings 'recursion'; no warnings 'uninitialized'; use bytes; ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $_loaded; sub import { my $_class = shift; return if ($_loaded++); unless (defined $MCE::VERSION) { $\ = undef; require Carp; Carp::croak( "MCE::Candy requires MCE. Please see the MCE::Candy documentation\n". "for more information.\n\n" ); } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Forchunk, foreach, and forseq sugar methods. ## ############################################################################### sub forchunk { my $x = shift; my $self = ref($x) ? $x : $MCE::MCE; my $_input_data = $_[0]; MCE::_validate_runstate($self, 'MCE::forchunk'); my ($_user_func, $_params_ref); if (ref $_[1] eq 'HASH') { $_user_func = $_[2]; $_params_ref = $_[1]; } else { $_user_func = $_[1]; $_params_ref = {}; } @_ = (); MCE::_croak('MCE::forchunk: (input_data) is not specified') unless (defined $_input_data); MCE::_croak('MCE::forchunk: (code_block) is not specified') unless (defined $_user_func); $_params_ref->{input_data} = $_input_data; $_params_ref->{user_func} = $_user_func; $self->run(1, $_params_ref); return $self; } sub foreach { my $x = shift; my $self = ref($x) ? $x : $MCE::MCE; my $_input_data = $_[0]; MCE::_validate_runstate($self, 'MCE::foreach'); my ($_user_func, $_params_ref); if (ref $_[1] eq 'HASH') { $_user_func = $_[2]; $_params_ref = $_[1]; } else { $_user_func = $_[1]; $_params_ref = {}; } @_ = (); MCE::_croak('MCE::foreach: (input_data) is not specified') unless (defined $_input_data); MCE::_croak('MCE::foreach: (code_block) is not specified') unless (defined $_user_func); $_params_ref->{chunk_size} = 1; $_params_ref->{input_data} = $_input_data; $_params_ref->{user_func} = $_user_func; $self->run(1, $_params_ref); return $self; } sub forseq { my $x = shift; my $self = ref($x) ? $x : $MCE::MCE; my $_sequence = $_[0]; MCE::_validate_runstate($self, 'MCE::forseq'); my ($_user_func, $_params_ref); if (ref $_[1] eq 'HASH') { $_user_func = $_[2]; $_params_ref = $_[1]; } else { $_user_func = $_[1]; $_params_ref = {}; } @_ = (); MCE::_croak('MCE::forseq: (sequence) is not specified') unless (defined $_sequence); MCE::_croak('MCE::forseq: (code_block) is not specified') unless (defined $_user_func); $_params_ref->{sequence} = $_sequence; $_params_ref->{user_func} = $_user_func; $self->run(1, $_params_ref); return $self; } ############################################################################### ## ---------------------------------------------------------------------------- ## Output iterators for preserving output order. ## ############################################################################### sub out_iter_array { my $_aref = shift; my %_tmp; my $_order_id = 1; MCE::_croak('The argument to (out_iter_array) is not an array ref.') unless (ref($_aref) eq 'ARRAY'); return sub { my $_chunk_id = shift; if ($_chunk_id == $_order_id && keys %_tmp == 0) { ## already orderly $_order_id++; push @{ $_aref }, @_; } else { ## hold temporarily otherwise until orderly @{ $_tmp{ $_chunk_id } } = @_; while (1) { last unless exists $_tmp{ $_order_id }; push @{ $_aref }, @{ delete $_tmp{ $_order_id++ } }; } } }; } sub out_iter_fh { my $_fh = shift; my %_tmp; my $_order_id = 1; MCE::_croak('The argument to (out_iter_fh) is not a supported file handle.') unless (ref($_fh) eq 'GLOB' || ref($_fh) =~ /^IO::/); return sub { my $_chunk_id = shift; if ($_chunk_id == $_order_id && keys %_tmp == 0) { ## already orderly $_order_id++; print {$_fh} @_; } else { ## hold temporarily otherwise until orderly @{ $_tmp{ $_chunk_id } } = @_; while (1) { last unless exists $_tmp{ $_order_id }; print {$_fh} @{ delete $_tmp{ $_order_id++ } }; } } }; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Candy - Sugar methods and output iterators for Many-Core Engine =head1 VERSION This document describes MCE::Candy version 1.608 =head1 DESCRIPTION This module provides a collection of sugar methods and helpful output iterators for preserving output order. =head1 "FOR" SUGAR METHODS The sugar methods described below were created prior to the 1.5 release which added MCE Models. This module is loaded automatically upon calling a "for" method. =head2 $mce->forchunk ( $input_data [, { options } ], sub { ... } ) Forchunk, foreach, and forseq are sugar methods in MCE. Workers are spawned automatically, the code block is executed in parallel, and shutdown is called. Do not call these methods if workers must persist afterwards. Specifying options is optional. Valid options are the same as for the process method. ## Declare a MCE instance. my $mce = MCE->new( chunk_size => 20, max_workers => $max_workers ); ## Arguments inside the code block are the same as passed to user_func. $mce->forchunk(\@input_data, sub { my ($mce, $chunk_ref, $chunk_id) = @_; foreach ( @{ $chunk_ref } ) { MCE->print("$chunk_id: $_\n"); } }); ## Passing chunk_size as an option. $mce->forchunk(\@input_data, { chunk_size => 30 }, sub { ... }); =head2 $mce->foreach ( $input_data [, { options } ], sub { ... } ) Foreach implies chunk_size => 1 and cannot be overwritten. Thus, looping is not necessary inside the block. my $mce = MCE->new( max_workers => $max_workers ); $mce->foreach(\@input_data, sub { my ($mce, $chunk_ref, $chunk_id) = @_; my $row = $chunk_ref->[0]; MCE->print("$chunk_id: $row\n"); }); =head2 $mce->forseq ( $sequence_spec [, { options } ], sub { ... } ) Sequence can be defined using an array or hash reference. my $mce = MCE->new( max_workers => 3 ); $mce->forseq([ 20, 40 ], sub { my ($mce, $n, $chunk_id) = @_; my $result = `ping 192.168.1.${n}`; ... }); $mce->forseq({ begin => 15, end => 10, step => -1 }, sub { my ($mce, $n, $chunk_id) = @_; print $n, " from ", MCE->wid, "\n"; }); The $n_seq variable points to an array_ref of sequences. Chunk size defaults to 1 when not specified. $mce->forseq([ 20, 80 ], { chunk_size => 10 }, sub { my ($mce, $n_seq, $chunk_id) = @_; for my $n ( @{ $n_seq } ) { my $result = `ping 192.168.1.${n}`; ... } }); =head1 OUTPUT ITERATORS WITH INPUT This module includes 2 output iterators which are useful for preserving output order while gathering data. These cover the 2 general use cases. The chunk_id value must be the first argument to gather. Gather must also not be called more than once inside the block. =head2 gather => MCE::Candy::out_iter_array( \@array ) The example utilizes the Core API with chunking disabled. Basically, setting chunk_size to 1. use MCE; use MCE::Candy; my @results; my $mce = MCE->new( chunk_size => 1, max_workers => 4, gather => MCE::Candy::out_iter_array(\@results), user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; $mce->gather($chunk_id, $chunk_ref->[0] * 2); } ); $mce->process([ 100 .. 109 ]); print "@results", "\n"; -- Output 200 202 204 206 208 210 212 214 216 218 Chunking may be desired for thousands or more items. In other words, wanting to reduce the overhead placed on IPC. use MCE; use MCE::Candy; my @results; my $mce = MCE->new( chunk_size => 100, max_workers => 4, gather => MCE::Candy::out_iter_array(\@results), user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; my @output; foreach my $item (@{ $chunk_ref }) { push @output, $item * 2; } $mce->gather($chunk_id, @output); } ); $mce->process([ 100_000 .. 200_000 - 1 ]); print scalar @results, "\n"; -- Output 100000 =head2 gather => MCE::Candy::out_iter_fh( $fh ) Let's change things a bit and use MCE::Flow for the next 2 examples. Chunking is not desired for the first example. use MCE::Flow; use MCE::Candy; open my $fh, '>', '/tmp/foo.txt'; mce_flow { chunk_size => 1, max_workers => 4, gather => MCE::Candy::out_iter_fh($fh) }, sub { my ($mce, $chunk_ref, $chunk_id) = @_; $mce->gather($chunk_id, $chunk_ref->[0] * 2, "\n"); }, (100 .. 109); close $fh; -- Output sent to '/tmp/foo.txt' 200 202 204 206 208 210 212 214 216 218 Chunking is desired for the next example due to processing many thousands. use MCE::Flow; use MCE::Candy; open my $fh, '>', '/tmp/foo.txt'; mce_flow { chunk_size => 100, max_workers => 4, gather => MCE::Candy::out_iter_fh( $fh ) }, sub { my ($mce, $chunk_ref, $chunk_id) = @_; my @output; foreach my $item (@{ $chunk_ref }) { push @output, ($item * 2) . "\n"; } $mce->gather($chunk_id, @output); }, (100_000 .. 200_000 - 1); close $fh; print -s '/tmp/foo.txt', "\n"; -- Output 700000 =head1 OUTPUT ITERATORS WITHOUT INPUT Input data is not a requirement for using the output iterators included in this module. The 'chunk_id' value is set uniquely and the same as 'wid' when not processing input data. =head2 gather => MCE::Candy::out_iter_array( \@array ) use MCE::Flow; use MCE::Candy; my @results; mce_flow { max_workers => 'auto', ## Note that 'auto' is never higher than 8 gather => MCE::Candy::out_iter_array(\@results) }, sub { my ($mce) = @_; ## This line is not necessary ## Calling via module okay; e.g: MCE->method ## Do work ## Sending a complex data structure is allowed ## Output will become orderly by iterator $mce->gather( $mce->chunk_id, { wid => $mce->wid, result => $mce->wid * 2 }); }; foreach my $href (@results) { print $href->{wid} .": ". $href->{result} ."\n"; } -- Output 1: 2 2: 4 3: 6 4: 8 5: 10 6: 12 7: 14 8: 16 =head2 gather => MCE::Candy::out_iter_fh( $fh ) use MCE::Flow; use MCE::Candy; open my $fh, '>', '/tmp/out.txt'; mce_flow { max_workers => 'auto', ## See get_ncpu in gather => MCE::Candy::out_iter_fh($fh) }, sub { my $output = "# Worker ID: " . MCE->wid . "\n"; ## Append results to $output string $output .= (MCE->wid * 2) . "\n\n"; ## Output will become orderly by iterator MCE->gather( MCE->wid, $output ); }; close $fh; -- Output # Worker ID: 1 2 # Worker ID: 2 4 # Worker ID: 3 6 # Worker ID: 4 8 # Worker ID: 5 10 # Worker ID: 6 12 # Worker ID: 7 14 # Worker ID: 8 16 =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Step.pm0000644000076400007640000010600712511673051013574 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Step - Parallel step model for building creative steps. ## ############################################################################### package MCE::Step; use strict; use warnings; ## no critic (BuiltinFunctions::ProhibitStringyEval) ## no critic (Subroutines::ProhibitSubroutinePrototypes) ## no critic (TestingAndDebugging::ProhibitNoStrict) use Scalar::Util qw( looks_like_number ); use MCE; use MCE::Queue; our $VERSION = '1.608'; our @CARP_NOT = qw( MCE ); ############################################################################### ## ---------------------------------------------------------------------------- ## Import routine. ## ############################################################################### my $MAX_WORKERS = 'auto'; my $CHUNK_SIZE = 'auto'; my $FAST = 0; my ($_params, @_prev_c, @_prev_n, @_prev_w, @_user_tasks, @_queue); my ($_MCE, $_loaded, $_last_task_id); my $_tag = 'MCE::Step'; sub import { my $_class = shift; return if ($_loaded++); ## Process module arguments. while (my $_argument = shift) { my $_arg = lc $_argument; $MAX_WORKERS = shift and next if ( $_arg eq 'max_workers' ); $CHUNK_SIZE = shift and next if ( $_arg eq 'chunk_size' ); $MCE::FREEZE = $MCE::MCE->{freeze} = shift and next if ( $_arg eq 'freeze' ); $MCE::THAW = $MCE::MCE->{thaw} = shift and next if ( $_arg eq 'thaw' ); if ( $_arg eq 'sereal' ) { if (shift eq '1') { local $@; eval 'use Sereal qw(encode_sereal decode_sereal)'; unless ($@) { $MCE::FREEZE = $MCE::MCE->{freeze} = \&encode_sereal; $MCE::THAW = $MCE::MCE->{thaw} = \&decode_sereal; } } next; } if ( $_arg eq 'tmp_dir' ) { $MCE::TMP_DIR = $MCE::MCE->{tmp_dir} = shift; my $_e1 = 'is not a directory or does not exist'; my $_e2 = 'is not writeable'; _croak($_tag."::import: ($MCE::TMP_DIR) $_e1") unless -d $MCE::TMP_DIR; _croak($_tag."::import: ($MCE::TMP_DIR) $_e2") unless -w $MCE::TMP_DIR; next; } if ( $_arg eq 'fast' ) { $FAST = 1 if (shift eq '1'); next; } _croak($_tag."::import: ($_argument) is not a valid module argument"); } $MAX_WORKERS = MCE::Util::_parse_max_workers($MAX_WORKERS); _validate_number($MAX_WORKERS, 'MAX_WORKERS'); _validate_number($CHUNK_SIZE, 'CHUNK_SIZE') unless ($CHUNK_SIZE eq 'auto'); ## Import functions. no strict 'refs'; no warnings 'redefine'; my $_pkg = caller; *{ $_pkg.'::mce_step_f' } = \&run_file; *{ $_pkg.'::mce_step_s' } = \&run_seq; *{ $_pkg.'::mce_step' } = \&run; return; } END { return if (defined $_MCE && $_MCE->wid); finish(); } ############################################################################### ## ---------------------------------------------------------------------------- ## The task end callback for when a task completes. ## Also, the step method for MCE is defined here. ## ############################################################################### sub _task_end { my ($_mce, $_task_id, $_task_name) = @_; if (defined $_mce->{user_tasks}->[$_task_id + 1]) { my $n_workers = $_mce->{user_tasks}->[$_task_id + 1]->{max_workers}; $_queue[$_task_id]->enqueue((undef) x $n_workers); } $_params->{task_end}->($_mce, $_task_id, $_task_name) if (exists $_params->{task_end} && ref $_params->{task_end} eq 'CODE'); return; } { no warnings 'redefine'; sub MCE::step { my $x = shift; my $self = ref($x) ? $x : $_MCE; _croak('MCE::step: method cannot be called by the manager process') unless ($self->{_wid}); my $_task_id = $self->{_task_id}; if ($_task_id < $_last_task_id) { if (scalar @_ > 1 || ref $_[0] || !defined $_[0]) { $_queue[$_task_id]->enqueue($self->freeze([ @_ ]).'1'); } else { $_queue[$_task_id]->enqueue($_[0].'0'); } } else { _croak('MCE::step: method cannot be called by the last task'); } return; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Init and finish routines. ## ############################################################################### sub init (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Step'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } finish(); $_params = (ref $_[0] eq 'HASH') ? shift : { @_ }; @_ = (); return; } sub finish () { if (defined $_MCE && $_MCE->{_spawned}) { MCE::_save_state; $_MCE->shutdown(); MCE::_restore_state; } @_user_tasks = (); @_prev_w = (); @_prev_n = (); @_prev_c = (); $_->DESTROY() for (@_queue); @_queue = (); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel step with MCE -- file. ## ############################################################################### sub run_file (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Step'); my ($_file, $_pos); my $_start_pos = (ref $_[0] eq 'HASH') ? 2 : 1; if (defined $_params) { delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{sequence} if (exists $_params->{sequence}); } else { $_params = {}; } for my $_i ($_start_pos .. @_ - 1) { my $_r = ref $_[$_i]; if ($_r eq '' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_file = $_[$_i]; $_pos = $_i; last; } } if (defined $_file && ref $_file eq '' && $_file ne '') { _croak("$_tag: ($_file) does not exist") unless (-e $_file); _croak("$_tag: ($_file) is not readable") unless (-r $_file); _croak("$_tag: ($_file) is not a plain file") unless (-f $_file); $_params->{_file} = $_file; } elsif (ref $_file eq 'GLOB' || ref $_file eq 'SCALAR' || ref($_file) =~ /^IO::/) { $_params->{_file} = $_file; } else { _croak("$_tag: (file) is not specified or valid"); } if (defined $_pos) { pop @_ for ($_pos .. @_ - 1); } return run(@_); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel step with MCE -- sequence. ## ############################################################################### sub run_seq (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Step'); my ($_begin, $_end, $_pos); my $_start_pos = (ref $_[0] eq 'HASH') ? 2 : 1; if (defined $_params) { delete $_params->{sequence} if (exists $_params->{sequence}); delete $_params->{input_data} if (exists $_params->{input_data}); delete $_params->{_file} if (exists $_params->{_file}); } else { $_params = {}; } for my $_i ($_start_pos .. @_ - 1) { my $_ref = ref $_[$_i]; if ($_ref eq '' || $_ref eq 'HASH' || $_ref eq 'ARRAY') { $_pos = $_i; if ($_ref eq '') { $_begin = $_[$_pos]; $_end = $_[$_pos + 1]; $_params->{sequence} = [ $_[$_pos], $_[$_pos + 1], $_[$_pos + 2], $_[$_pos + 3] ]; } elsif ($_ref eq 'HASH') { $_begin = $_[$_pos]->{begin}; $_end = $_[$_pos]->{end}; $_params->{sequence} = $_[$_pos]; } elsif ($_ref eq 'ARRAY') { $_begin = $_[$_pos]->[0]; $_end = $_[$_pos]->[1]; $_params->{sequence} = $_[$_pos]; } last; } } _croak("$_tag: (sequence) is not specified or valid") unless (exists $_params->{sequence}); _croak("$_tag: (begin) is not specified for sequence") unless (defined $_begin); _croak("$_tag: (end) is not specified for sequence") unless (defined $_end); $_params->{sequence_run} = 1; if (defined $_pos) { pop @_ for ($_pos .. @_ - 1); } return run(@_); } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallel step with MCE. ## ############################################################################### sub run (@) { shift if (defined $_[0] && $_[0] eq 'MCE::Step'); if (MCE->wid) { @_ = (); _croak( "$_tag: function cannot be called by the worker process" ); } if (ref $_[0] eq 'HASH') { $_params = {} unless defined $_params; for my $_p (keys %{ $_[0] }) { $_params->{$_p} = $_[0]->{$_p}; } shift; } ## ------------------------------------------------------------------------- my (@_code, @_name, @_wrks); my $_init_mce = 0; my $_pos = 0; while (ref $_[0] eq 'CODE') { push @_code, $_[0]; push @_name, (defined $_params && ref $_params->{task_name} eq 'ARRAY') ? $_params->{task_name}->[$_pos] : undef; push @_wrks, (defined $_params && ref $_params->{max_workers} eq 'ARRAY') ? $_params->{max_workers}->[$_pos] : undef; $_init_mce = 1 if (!defined $_prev_c[$_pos] || $_prev_c[$_pos] != $_code[$_pos]); { no warnings; $_init_mce = 1 if ($_prev_n[$_pos] ne $_name[$_pos]); $_init_mce = 1 if ($_prev_w[$_pos] ne $_wrks[$_pos]); } $_prev_c[$_pos] = $_code[$_pos]; $_prev_n[$_pos] = $_name[$_pos]; $_prev_w[$_pos] = $_wrks[$_pos]; shift; $_pos++; } if (defined $_prev_c[$_pos]) { pop @_prev_c for ($_pos .. @_prev_c - 1); pop @_prev_n for ($_pos .. @_prev_n - 1); pop @_prev_w for ($_pos .. @_prev_w - 1); $_init_mce = 1; } return unless (scalar @_code); ## ------------------------------------------------------------------------- my $_input_data; my $_max_workers = $MAX_WORKERS; my $_r = ref $_[0]; if ($_r eq 'ARRAY' || $_r eq 'GLOB' || $_r eq 'SCALAR' || $_r =~ /^IO::/) { $_input_data = shift; } if (defined $_params) { my $_p = $_params; $_max_workers = MCE::Util::_parse_max_workers($_p->{max_workers}) if (exists $_p->{max_workers} && ref $_p->{max_workers} ne 'ARRAY'); delete $_p->{sequence} if (defined $_input_data || scalar @_); delete $_p->{user_func} if (exists $_p->{user_func}); delete $_p->{user_tasks} if (exists $_p->{user_tasks}); } if (@_code > 1 && $_max_workers > 1) { $_max_workers = int($_max_workers / @_code + 0.5) + 1; } my $_chunk_size = MCE::Util::_parse_chunk_size( $CHUNK_SIZE, $_max_workers, $_params, $_input_data, scalar @_ ); if (defined $_params) { if (exists $_params->{_file}) { $_input_data = delete $_params->{_file}; } else { $_input_data = $_params->{input_data} if exists $_params->{input_data}; } } MCE::_save_state; ## ------------------------------------------------------------------------- if ($_init_mce) { $_MCE->shutdown() if (defined $_MCE); pop( @_queue )->DESTROY for (@_code .. @_queue); push @_queue, MCE::Queue->new(fast => $FAST) for (@_queue .. @_code - 2); _gen_user_tasks(\@_queue, \@_code, \@_name, \@_wrks, $_chunk_size); $_last_task_id = @_code - 1; my %_options = ( max_workers => $_max_workers, task_name => $_tag, user_tasks => \@_user_tasks, task_end => \&_task_end, ); if (defined $_params) { local $_; my $_p = $_params; for (keys %{ $_p }) { next if ($_ eq 'sequence_run'); next if ($_ eq 'max_workers' && ref $_p->{max_workers} eq 'ARRAY'); next if ($_ eq 'task_name' && ref $_p->{task_name} eq 'ARRAY'); next if ($_ eq 'input_data'); next if ($_ eq 'chunk_size'); next if ($_ eq 'task_end'); _croak("MCE::Step: ($_) is not a valid constructor argument") unless (exists $MCE::_valid_fields_new{$_}); $_options{$_} = $_p->{$_}; } } $_MCE = MCE->new(%_options); } else { ## Workers may persist after running. Thus, updating the MCE instance. ## These options do not require respawning. if (defined $_params) { for my $_p (qw( RS interval stderr_file stdout_file user_error user_output job_delay submit_delay on_post_exit on_post_run user_args flush_file flush_stderr flush_stdout gather )) { $_MCE->{$_p} = $_params->{$_p} if (exists $_params->{$_p}); } } } ## ------------------------------------------------------------------------- my @_a; my $_wa = wantarray; $_MCE->{gather} = \@_a if (defined $_wa); if (defined $_input_data) { @_ = (); $_MCE->process({ chunk_size => $_chunk_size }, $_input_data); delete $_MCE->{input_data}; } elsif (scalar @_) { $_MCE->process({ chunk_size => $_chunk_size }, \@_); delete $_MCE->{input_data}; } else { if (defined $_params && exists $_params->{sequence}) { $_MCE->run({ chunk_size => $_chunk_size, sequence => $_params->{sequence} }, 0); if (exists $_params->{sequence_run}) { delete $_params->{sequence_run}; delete $_params->{sequence}; } delete $_MCE->{sequence}; } else { $_MCE->run({ chunk_size => $_chunk_size }, 0); } } delete $_MCE->{gather} if (defined $_wa); MCE::_restore_state; if (exists $_MCE->{_rla_return}) { $MCE::MCE->{_rla_return} = delete $_MCE->{_rla_return}; } finish() if ($^S); ## shutdown if in eval state return ((defined $_wa) ? @_a : ()); } ############################################################################### ## ---------------------------------------------------------------------------- ## Private methods. ## ############################################################################### sub _croak { goto &MCE::_croak; } sub _gen_user_func { my ($_queue_ref, $_code_ref, $_chunk_size, $_pos) = @_; my $_q_in = $_queue_ref->[$_pos - 1]; my $_c_ref = $_code_ref->[$_pos]; return sub { my ($_mce) = @_; $_mce->{_next_jmp} = sub { goto _MCE_STEP__NEXT; }; $_mce->{_last_jmp} = sub { goto _MCE_STEP__LAST; }; _MCE_STEP__NEXT: while (defined (local $_ = $_q_in->dequeue())) { if (chop $_) { my $_args = $_mce->thaw($_); $_ = $_args->[0]; $_c_ref->($_mce, @{ $_args }); } else { $_c_ref->($_mce, $_); } } _MCE_STEP__LAST: return; }; } sub _gen_user_tasks { my ($_queue_ref, $_code_ref, $_name_ref, $_wrks_ref, $_chunk_size) = @_; @_user_tasks = (); push @_user_tasks, { task_name => $_name_ref->[0], max_workers => $_wrks_ref->[0], user_func => sub { $_code_ref->[0]->(@_); return; } }; for my $_pos (1 .. @{ $_code_ref } - 1) { push @_user_tasks, { task_name => $_name_ref->[$_pos], max_workers => $_wrks_ref->[$_pos], user_func => _gen_user_func( $_queue_ref, $_code_ref, $_chunk_size, $_pos ) }; } return; } sub _validate_number { my ($_n, $_key) = @_; _croak("$_tag: ($_key) is not valid") if (!defined $_n); $_n =~ s/K\z//i; $_n =~ s/M\z//i; if (!looks_like_number($_n) || int($_n) != $_n || $_n < 1) { _croak("$_tag: ($_key) is not valid"); } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Step - Parallel step model for building creative steps =head1 VERSION This document describes MCE::Step version 1.608 =head1 DESCRIPTION MCE::Step is similar to L for writing custom apps. The main difference comes from the transparent use of queues between sub-tasks. It is trivial to parallelize with mce_stream shown below. ## Native map function my @a = map { $_ * 4 } map { $_ * 3 } map { $_ * 2 } 1..10000; ## Same as with MCE::Stream (processing from right to left) @a = mce_stream sub { $_ * 4 }, sub { $_ * 3 }, sub { $_ * 2 }, 1..10000; ## Pass an array reference to have writes occur simultaneously mce_stream \@a, sub { $_ * 4 }, sub { $_ * 3 }, sub { $_ * 2 }, 1..10000; However, let's have MCE::Step compute the same in parallel. Unlike the example in L, the use of MCE::Queue is totally transparent. use MCE::Step; This calls for preserving output order. sub preserve_order { my %tmp; my $order_id = 1; my $gather_ref = $_[0]; @{ $gather_ref } = (); ## clear the array (optional) return sub { my ($data_ref, $chunk_id) = @_; $tmp{$chunk_id} = $data_ref; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ delete $tmp{$order_id++} }; } return; }; } Next are the 3 sub-tasks. Compare these 3 sub-tasks with the same as described in L. The call to MCE->step simplifies the passing of data into the next sub-task. sub task_a { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 2 } @{ $chunk_ref }; MCE->step(\@ans, $chunk_id); } sub task_b { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 3 } @{ $chunk_ref }; MCE->step(\@ans, $chunk_id); } sub task_c { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 4 } @{ $chunk_ref }; MCE->gather(\@ans, $chunk_id); } In summary, MCE::Step builds out a MCE instance behind the scene and starts running. Both task_name and max_workers (not shown) can take an anonymous array for specifying the values uniquely for each sub-task. my @a; mce_step { task_name => [ 'a', 'b', 'c' ], gather => preserve_order(\@a) }, \&task_a, \&task_b, \&task_c, 1..10000; print "@a\n"; =head1 STEP DEMO In the demonstration below, one may call ->gather or ->step any number of times although ->step is not allowed in the last sub-block. Data is gathered to @arr which may likely be out-of-order. Gathering data is optional. All sub-blocks receive $mce as the first argument. First, defining 3 sub-tasks. use MCE::Step; sub task_a { my ($mce, $chunk_ref, $chunk_id) = @_; if ($_ % 2 == 0) { MCE->gather($_); # MCE->gather($_ * 4); ## Ok to gather multiple times } else { MCE->print("a step: $_, $_ * $_\n"); MCE->step($_, $_ * $_); # MCE->step($_, $_ * 4 ); ## Ok to step multiple times } } sub task_b { my ($mce, $arg1, $arg2) = @_; MCE->print("b args: $arg1, $arg2\n"); if ($_ % 3 == 0) { ## $_ is the same as $arg1 MCE->gather($_); } else { MCE->print("b step: $_ * $_\n"); MCE->step($_ * $_); } } sub task_c { my ($mce, $arg1) = @_; MCE->print("c: $_\n"); MCE->gather($_); } Next, pass MCE options, using chunk_size 1, and run all 3 tasks in parallel. Notice how max_workers can take an anonymous array, similarly to task_name. my @arr = mce_step { task_name => [ 'a', 'b', 'c' ], max_workers => [ 2, 2, 2 ], chunk_size => 1 }, \&task_a, \&task_b, \&task_c, 1..10; Finally, sort the array and display its contents. @arr = sort { $a <=> $b } @arr; print "\n@arr\n\n"; -- Output a step: 1, 1 * 1 a step: 3, 3 * 3 a step: 5, 5 * 5 a step: 7, 7 * 7 a step: 9, 9 * 9 b args: 1, 1 b step: 1 * 1 b args: 3, 9 b args: 7, 49 b step: 7 * 7 b args: 5, 25 b step: 5 * 5 b args: 9, 81 c: 1 c: 49 c: 25 1 2 3 4 6 8 9 10 25 49 =head1 SYNOPSIS when CHUNK_SIZE EQUALS 1 Although L may be preferred for running using a single code block, the text below also applies to this module, particularly for the first block. All models in MCE default to 'auto' for chunk_size. The arguments for the block are the same as writing a user_func block using the Core API. Beginning with MCE 1.5, the next input item is placed into the input scalar variable $_ when chunk_size equals 1. Otherwise, $_ points to $chunk_ref containing many items. Basically, line 2 below may be omitted from your code when using $_. One can call MCE->chunk_id to obtain the current chunk id. line 1: user_func => sub { line 2: my ($mce, $chunk_ref, $chunk_id) = @_; line 3: line 4: $_ points to $chunk_ref->[0] line 5: in MCE 1.5 when chunk_size == 1 line 6: line 7: $_ points to $chunk_ref line 8: in MCE 1.5 when chunk_size > 1 line 9: } Follow this synopsis when chunk_size equals one. Looping is not required from inside the first block. Hence, the block is called once per each item. ## Exports mce_step, mce_step_f, and mce_step_s use MCE::Step; MCE::Step::init { chunk_size => 1 }; ## Array or array_ref mce_step sub { do_work($_) }, 1..10000; mce_step sub { do_work($_) }, [ 1..10000 ]; ## File_path, glob_ref, or scalar_ref mce_step_f sub { chomp; do_work($_) }, "/path/to/file"; mce_step_f sub { chomp; do_work($_) }, $file_handle; mce_step_f sub { chomp; do_work($_) }, \$scalar; ## Sequence of numbers (begin, end [, step, format]) mce_step_s sub { do_work($_) }, 1, 10000, 5; mce_step_s sub { do_work($_) }, [ 1, 10000, 5 ]; mce_step_s sub { do_work($_) }, { begin => 1, end => 10000, step => 5, format => undef }; =head1 SYNOPSIS when CHUNK_SIZE is GREATER THAN 1 Follow this synopsis when chunk_size equals 'auto' or greater than 1. This means having to loop through the chunk from inside the first block. use MCE::Step; MCE::Step::init { ## Chunk_size defaults to 'auto' when chunk_size => 'auto' ## not specified. Therefore, the init }; ## function may be omitted. ## Syntax is shown for mce_step for demonstration purposes. ## Looping inside the block is the same for mce_step_f and ## mce_step_s. mce_step sub { do_work($_) for (@{ $_ }) }, 1..10000; ## Same as above, resembles code using the Core API. mce_step sub { my ($mce, $chunk_ref, $chunk_id) = @_; for (@{ $chunk_ref }) { do_work($_); } }, 1..10000; Chunking reduces the number of IPC calls behind the scene. Think in terms of chunks whenever processing a large amount of data. For relatively small data, choosing 1 for chunk_size is fine. =head1 OVERRIDING DEFAULTS The following list 6 options which may be overridden when loading the module. use Sereal qw( encode_sereal decode_sereal ); use CBOR::XS qw( encode_cbor decode_cbor ); use JSON::XS qw( encode_json decode_json ); use MCE::Step max_workers => 8, ## Default 'auto' chunk_size => 500, ## Default 'auto' fast => 1, ## Default 0 (fast queue?) tmp_dir => "/path/to/app/tmp", ## $MCE::Signal::tmp_dir freeze => \&encode_sereal, ## \&Storable::freeze thaw => \&decode_sereal ## \&Storable::thaw ; There is a simpler way to enable Sereal with MCE 1.5. The following will attempt to use Sereal if available, otherwise defaults to Storable for serialization. use MCE::Step Sereal => 1; MCE::Step::init { chunk_size => 1 }; ## Serialization is by the Sereal module if available. my %answer = mce_step sub { MCE->gather( $_, sqrt $_ ) }, 1..10000; =head1 CUSTOMIZING MCE =over 3 =item MCE::Step->init ( options ) =item MCE::Step::init { options } The init function accepts a hash of MCE options. Unlike with MCE::Stream, both gather and bounds_only options may be specified when calling init (not shown below). use MCE::Step; MCE::Step::init { chunk_size => 1, max_workers => 4, user_begin => sub { print "## ", MCE->wid, " started\n"; }, user_end => sub { print "## ", MCE->wid, " completed\n"; } }; my %a = mce_step sub { MCE->gather($_, $_ * $_) }, 1..100; print "\n", "@a{1..100}", "\n"; -- Output ## 3 started ## 1 started ## 4 started ## 2 started ## 3 completed ## 4 completed ## 1 completed ## 2 completed 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 1600 1681 1764 1849 1936 2025 2116 2209 2304 2401 2500 2601 2704 2809 2916 3025 3136 3249 3364 3481 3600 3721 3844 3969 4096 4225 4356 4489 4624 4761 4900 5041 5184 5329 5476 5625 5776 5929 6084 6241 6400 6561 6724 6889 7056 7225 7396 7569 7744 7921 8100 8281 8464 8649 8836 9025 9216 9409 9604 9801 10000 =back Like with MCE::Step::init above, MCE options may be specified using an anonymous hash for the first argument. Notice how both max_workers and task_name can take an anonymous array for setting values uniquely for each code block. Unlike MCE::Stream which processes from right-to-left, MCE::Step begins with the first code block, thus processing from left-to-right. The following takes 9 seconds to complete. The 9 seconds is from having only 2 workers assigned for the last sub-task and waiting 1 or 2 seconds initially before calling MCE->step. Removing both calls to MCE->step will cause the script to complete in just 1 second. The reason is due to the 2nd and subsequent sub-tasks awaiting data from an internal queue. Workers terminate upon receiving an undef. use MCE::Step; my @a = mce_step { task_name => [ 'a', 'b', 'c' ], max_workers => [ 3, 4, 2, ], user_end => sub { my ($mce, $task_id, $task_name) = @_; MCE->print("$task_id - $task_name completed\n"); }, task_end => sub { my ($mce, $task_id, $task_name) = @_; MCE->print("$task_id - $task_name ended\n"); } }, sub { sleep 1; MCE->step(""); }, ## 3 workers, named a sub { sleep 2; MCE->step(""); }, ## 4 workers, named b sub { sleep 3; }; ## 2 workers, named c -- Output 0 - a completed 0 - a completed 0 - a completed 0 - a ended 1 - b completed 1 - b completed 1 - b completed 1 - b completed 1 - b ended 2 - c completed 2 - c completed 2 - c ended =head1 API DOCUMENTATION Although input data is optional for MCE::Step, the following assumes chunk_size equals 1 in order to demonstrate all the possibilities of passing input data into the code block. =over 3 =item MCE::Step->run ( { input_data => iterator }, sub { code } ) =item mce_step { input_data => iterator }, sub { code } An iterator reference can by specified for input_data. The only other way is to specify input_data via MCE::Step::init. This prevents MCE::Step from configuring the iterator reference as another user task which will not work. Iterators are described under "SYNTAX for INPUT_DATA" at L. MCE::Step::init { input_data => iterator }; mce_step sub { $_ }; =item MCE::Step->run ( sub { code }, list ) =item mce_step sub { code }, list Input data can be defined using a list. mce_step sub { $_ }, 1..1000; mce_step sub { $_ }, [ 1..1000 ]; =item MCE::Step->run_file ( sub { code }, file ) =item mce_step_f sub { code }, file The fastest of these is the /path/to/file. Workers communicate the next offset position among themselves without any interaction from the manager process. mce_step_f sub { $_ }, "/path/to/file"; mce_step_f sub { $_ }, $file_handle; mce_step_f sub { $_ }, \$scalar; =item MCE::Step->run_seq ( sub { code }, $beg, $end [, $step, $fmt ] ) =item mce_step_s sub { code }, $beg, $end [, $step, $fmt ] Sequence can be defined as a list, an array reference, or a hash reference. The functions require both begin and end values to run. Step and format are optional. The format is passed to sprintf (% may be omitted below). my ($beg, $end, $step, $fmt) = (10, 20, 0.1, "%4.1f"); mce_step_s sub { $_ }, $beg, $end, $step, $fmt; mce_step_s sub { $_ }, [ $beg, $end, $step, $fmt ]; mce_step_s sub { $_ }, { begin => $beg, end => $end, step => $step, format => $fmt }; =back The sequence engine can compute 'begin' and 'end' items only, for the chunk, and not the items in between (hence boundaries only). This option applies to sequence only and has no effect when chunk_size equals 1. The time to run is 0.006s below. This becomes 0.827s without the bounds_only option due to computing all items in between, thus creating a very large array. Basically, specify bounds_only => 1 when boundaries is all you need for looping inside the block; e.g. Monte Carlo simulations. Time was measured using 1 worker to emphasize the difference. use MCE::Step; MCE::Step::init { max_workers => 1, chunk_size => 1_250_000, bounds_only => 1 }; ## For sequence, the input scalar $_ points to $chunk_ref ## when chunk_size > 1, otherwise $chunk_ref->[0]. ## ## mce_step_s sub { ## my $begin = $_->[0]; my $end = $_->[-1]; ## ## for ($begin .. $end) { ## ... ## } ## ## }, 1, 10_000_000; mce_step_s sub { my ($mce, $chunk_ref, $chunk_id) = @_; ## $chunk_ref contains 2 items, not 1_250_000 my $begin = $chunk_ref->[ 0]; my $end = $chunk_ref->[-1]; ## or $chunk_ref->[1] MCE->printf("%7d .. %8d\n", $begin, $end); }, 1, 10_000_000; -- Output 1 .. 1250000 1250001 .. 2500000 2500001 .. 3750000 3750001 .. 5000000 5000001 .. 6250000 6250001 .. 7500000 7500001 .. 8750000 8750001 .. 10000000 =head1 GATHERING DATA Unlike MCE::Map where gather and output order are done for you automatically, the gather method is used to have results sent back to the manager process. use MCE::Step chunk_size => 1; ## Output order is not guaranteed. my @a = mce_step sub { MCE->gather($_ * 2) }, 1..100; print "@a\n\n"; ## Outputs to a hash instead (key, value). my %h1 = mce_step sub { MCE->gather($_, $_ * 2) }, 1..100; print "@h1{1..100}\n\n"; ## This does the same thing due to chunk_id starting at one. my %h2 = mce_step sub { MCE->gather(MCE->chunk_id, $_ * 2) }, 1..100; print "@h2{1..100}\n\n"; The gather method can be called multiple times within the block unlike return which would leave the block. Therefore, think of gather as yielding results immediately to the manager process without actually leaving the block. use MCE::Step chunk_size => 1, max_workers => 3; my @hosts = qw( hosta hostb hostc hostd hoste ); my %h3 = mce_step sub { my ($output, $error, $status); my $host = $_; ## Do something with $host; $output = "Worker ". MCE->wid .": Hello from $host"; if (MCE->chunk_id % 3 == 0) { ## Simulating an error condition local $? = 1; $status = $?; $error = "Error from $host" } else { $status = 0; } ## Ensure unique keys (key, value) when gathering to ## a hash. MCE->gather("$host.out", $output); MCE->gather("$host.err", $error) if (defined $error); MCE->gather("$host.sta", $status); }, @hosts; foreach my $host (@hosts) { print $h3{"$host.out"}, "\n"; print $h3{"$host.err"}, "\n" if (exists $h3{"$host.err"}); print "Exit status: ", $h3{"$host.sta"}, "\n\n"; } -- Output Worker 3: Hello from hosta Exit status: 0 Worker 2: Hello from hostb Exit status: 0 Worker 1: Hello from hostc Error from hostc Exit status: 1 Worker 3: Hello from hostd Exit status: 0 Worker 2: Hello from hoste Exit status: 0 The following uses an anonymous array containing 3 elements when gathering data. Serialization is automatic behind the scene. my %h3 = mce_step sub { ... MCE->gather($host, [$output, $error, $status]); }, @hosts; foreach my $host (@hosts) { print $h3{$host}->[0], "\n"; print $h3{$host}->[1], "\n" if (defined $h3{$host}->[1]); print "Exit status: ", $h3{$host}->[2], "\n\n"; } Although MCE::Map comes to mind, one may want additional control when gathering data such as retaining output order. use MCE::Step; sub preserve_order { my %tmp; my $order_id = 1; my $gather_ref = $_[0]; return sub { $tmp{ (shift) } = \@_; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ delete $tmp{$order_id++} }; } return; }; } ## Workers persist for the most part after running. Though, not always ## the case and depends on Perl. Pass a reference to a subroutine if ## workers must persist; e.g. mce_step { ... }, \&foo, 1..100000. MCE::Step::init { chunk_size => 'auto', max_workers => 'auto' }; for (1..2) { my @m2; mce_step { gather => preserve_order(\@m2) }, sub { my @a; my ($mce, $chunk_ref, $chunk_id) = @_; ## Compute the entire chunk data at once. push @a, map { $_ * 2 } @{ $chunk_ref }; ## Afterwards, invoke the gather feature, which ## will direct the data to the callback function. MCE->gather(MCE->chunk_id, @a); }, 1..100000; print scalar @m2, "\n"; } MCE::Step::finish; All 6 models support 'auto' for chunk_size unlike the Core API. Think of the models as the basis for providing JIT for MCE. They create the instance, tune max_workers, and tune chunk_size automatically regardless of the hardware. The following does the same thing using the Core API. Workers persist after running. use MCE; sub preserve_order { ... } my $mce = MCE->new( max_workers => 'auto', chunk_size => 8000, user_func => sub { my @a; my ($mce, $chunk_ref, $chunk_id) = @_; ## Compute the entire chunk data at once. push @a, map { $_ * 2 } @{ $chunk_ref }; ## Afterwards, invoke the gather feature, which ## will direct the data to the callback function. MCE->gather(MCE->chunk_id, @a); } ); for (1..2) { my @m2; $mce->process({ gather => preserve_order(\@m2) }, [1..100000]); print scalar @m2, "\n"; } $mce->shutdown; =head1 MANUAL SHUTDOWN =over 3 =item MCE::Step->finish =item MCE::Step::finish Workers remain persistent as much as possible after running. Shutdown occurs automatically when the script terminates. Call finish when workers are no longer needed. use MCE::Step; MCE::Step::init { chunk_size => 20, max_workers => 'auto' }; mce_step sub { ... }, 1..100; MCE::Step::finish; =back =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE/Signal.pm0000644000076400007640000004510212511673046014100 0ustar mariomario############################################################################### ## ---------------------------------------------------------------------------- ## MCE::Signal - Temporary directory creation/cleanup and signal handling. ## ############################################################################### package MCE::Signal; use strict; use warnings; use Carp (); use File::Path (); use Time::HiRes qw( sleep time ); use Fcntl qw( :flock O_RDONLY ); use base qw( Exporter ); our $VERSION = '1.608'; our ($display_die_with_localtime, $display_warn_with_localtime); our ($main_proc_id, $prog_name, $tmp_dir); BEGIN { $main_proc_id = $$; $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_name = 'perl' if ($prog_name eq '-e'); } our @EXPORT_OK = qw( $tmp_dir sys_cmd stop_and_exit ); our %EXPORT_TAGS = ( all => \@EXPORT_OK, tmp_dir => [ qw( $tmp_dir ) ] ); ############################################################################### ## ---------------------------------------------------------------------------- ## Process import, export, & module arguments. ## ############################################################################### sub _croak { $\ = undef; goto &Carp::croak; } sub _usage { return _croak "MCE::Signal error: ($_[0]) is not a valid option"; } sub _flag { return 1; } my $_is_MSWin32 = ($^O eq 'MSWin32') ? 1 : 0; my $_keep_tmp_dir = 0; my $_no_sigmsg = 0; my $_no_kill9 = 0; my $_loaded; sub import { my $_class = shift; return if ($_loaded++); my @_export_args = (); my $_no_setpgrp = 0; my $_setpgrp = 0; my $_use_dev_shm = 0; while (my $_arg = shift) { $_keep_tmp_dir = _flag() and next if ($_arg eq '-keep_tmp_dir'); $_no_sigmsg = _flag() and next if ($_arg eq '-no_sigmsg'); $_no_kill9 = _flag() and next if ($_arg eq '-no_kill9'); $_no_setpgrp = _flag() and next if ($_arg eq '-no_setpgrp'); $_setpgrp = _flag() and next if ($_arg eq '-setpgrp'); $_use_dev_shm = _flag() and next if ($_arg eq '-use_dev_shm'); _usage($_arg) if ($_arg =~ /^-/); push @_export_args, $_arg; } local $Exporter::ExportLevel = 1; Exporter::import($_class, @_export_args); # ## MCE no longer calls setpgrp by default as of MCE 1.405. # ## Sets the current process group for the current process. # setpgrp(0,0) if ($_no_setpgrp == 0 && $^O ne 'MSWin32'); ## Sets the current process group for the current process. setpgrp(0,0) if ($_setpgrp == 1 && $^O ne 'MSWin32'); my ($_tmp_dir_base, $_count); $_count = 0; if (exists $ENV{TEMP} && -d $ENV{TEMP} && -w $ENV{TEMP}) { if ($_is_MSWin32) { $_tmp_dir_base = $ENV{TEMP} . '/mce'; mkdir $_tmp_dir_base unless (-d $_tmp_dir_base); } else { $_tmp_dir_base = $ENV{TEMP}; } } else { $_tmp_dir_base = ($_use_dev_shm && -d '/dev/shm' && -w '/dev/shm') ? '/dev/shm' : '/tmp'; } _croak("MCE::Signal::import: ($_tmp_dir_base) is not writeable") unless (-w $_tmp_dir_base); ## Remove tainted'ness from $tmp_dir. ($tmp_dir) = "$_tmp_dir_base/$prog_name.$$.$_count" =~ /(.*)/; while ( !(mkdir $tmp_dir, 0770) ) { ($tmp_dir) = ("$_tmp_dir_base/$prog_name.$$.".(++$_count)) =~ /(.*)/; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Configure signal handling. ## ############################################################################### my ($_mce_sess_dir_ref, $_mce_spawned_ref); sub _set_session_vars { ($_mce_sess_dir_ref, $_mce_spawned_ref) = @_; return; } ## Set traps to catch signals. $SIG{XCPU} = \&stop_and_exit if (exists $SIG{XCPU}); ## UNIX SIG 24 $SIG{XFSZ} = \&stop_and_exit if (exists $SIG{XFSZ}); ## UNIX SIG 25 $SIG{HUP} = \&stop_and_exit; ## UNIX SIG 1 $SIG{INT} = \&stop_and_exit; ## UNIX SIG 2 $SIG{PIPE} = \&stop_and_exit; ## UNIX SIG 13 $SIG{QUIT} = \&stop_and_exit; ## UNIX SIG 3 $SIG{TERM} = \&stop_and_exit; ## UNIX SIG 15 ## For a more reliable MCE, $SIG{CHLD} is set to 'DEFAULT'. MCE handles ## the reaping of its children, especially when running multiple MCEs ## simultaneously. ## $SIG{CHLD} = 'DEFAULT' unless ($_is_MSWin32); ############################################################################### ## ---------------------------------------------------------------------------- ## Call stop_and_exit when exiting the script. ## ############################################################################### END { my $_exit_status = $?; MCE::Signal->_shutdown_mce($_exit_status) if (defined $_mce_spawned_ref); MCE::Signal->stop_and_exit($_exit_status) if ($$ == $main_proc_id); } ############################################################################### ## ---------------------------------------------------------------------------- ## Run command via the system(...) function. ## ## The system function in Perl ignores SIGINT and SIGQUIT. These 2 signals ## are sent to the command being executed via system() but not back to ## the underlying Perl script. The code below will ensure the Perl script ## receives the same signal in order to raise an exception immediately ## after the system call. ## ## Returns the actual exit status. ## ############################################################################### sub sys_cmd { shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal'); _croak('MCE::Signal::sys_cmd: no arguments were specified') if (@_ == 0); my $_status = system(@_); my $_sig_no = $_status & 127; my $_exit_status = $_status >> 8; ## Kill the process group if command caught SIGINT or SIGQUIT. kill('INT', $main_proc_id, ($_is_MSWin32 ? -$$ : -getpgrp)) if $_sig_no == 2; kill('QUIT', $main_proc_id, ($_is_MSWin32 ? -$$ : -getpgrp)) if $_sig_no == 3; return $_exit_status; } ############################################################################### ## ---------------------------------------------------------------------------- ## Stops execution, removes temp directory and exits cleanly. ## ## Provides safe reentrant logic for both parent and child processes. ## The $main_proc_id variable is defined above. ## ############################################################################### { my $_handler_cnt = 0; my %_sig_name_lkup = map { $_ => 1 } qw( __DIE__ __WARN__ HUP INT PIPE QUIT TERM CHLD XCPU XFSZ ); sub _NOOP { } sub stop_and_exit { shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal'); my $_sig_name = $_[0] || 0; my $_exit_status = $?; my $_is_sig = 0; my $_is_main_thread; if (exists $_sig_name_lkup{$_sig_name}) { $_mce_spawned_ref = undef; $SIG{$_sig_name} = \&_NOOP; $_exit_status = $_is_sig = 1; } else { $_exit_status = $_sig_name if ($_sig_name ne '0'); } if ($INC{'threads.pm'}) { $_is_main_thread = ($$ == $main_proc_id && threads->tid() == 0); } else { $_is_main_thread = ($$ == $main_proc_id); } $SIG{INT} = \&_NOOP if ($_sig_name ne 'INT'); $SIG{__DIE__} = $SIG{__WARN__} = \&_NOOP; ## ---------------------------------------------------------------------- ## For the main thread / manager process. if ($_is_main_thread) { if (++$_handler_cnt == 1 && ! -e "$tmp_dir/stopped") { open my $_FH, '>', "$tmp_dir/stopped"; close $_FH; local $\ = undef; ## Display message and kill process group if signaled. if ($_is_sig == 1) { my $_err_msg = undef; if ($_sig_name eq 'XCPU') { $_err_msg = 'exceeded CPU time limit, exiting'; } elsif ($_sig_name eq 'XFSZ') { $_err_msg = 'exceeded file size limit, exiting'; } elsif ($_sig_name eq 'INT' && -f "$tmp_dir/died") { $_err_msg = 'caught signal (__DIE__), exiting'; } elsif ($_sig_name eq '__DIE__') { $_err_msg = 'caught signal (__DIE__), exiting'; } elsif ($_sig_name ne 'PIPE') { $_err_msg = "caught signal ($_sig_name), exiting"; } ## Display error message. if ($_err_msg && $_no_sigmsg == 0) { print {*STDERR} "\n## $prog_name: $_err_msg\n"; } open my $_FH, '>', "$tmp_dir/killed"; close $_FH; ## Signal process group to terminate. kill('INT', $_is_MSWin32 ? -$$ : -getpgrp); ## Pause a bit. if ($_sig_name ne 'PIPE') { sleep 0.065 for (1..3); } else { sleep 0.015 for (1..2); } } ## Remove temp directory. if (defined $tmp_dir && $tmp_dir ne '' && -d $tmp_dir) { if (defined $_mce_sess_dir_ref) { for my $_sess_dir (keys %{ $_mce_sess_dir_ref }) { File::Path::rmtree($_sess_dir); delete $_mce_sess_dir_ref->{$_sess_dir}; } } if ($_keep_tmp_dir == 1) { print {*STDERR} "$prog_name: saved tmp_dir = $tmp_dir\n"; } else { if ($tmp_dir ne '/tmp' && $tmp_dir ne '/var/tmp') { File::Path::rmtree($tmp_dir); } } $tmp_dir = undef; } ## Signal process group to die. if ($_is_sig == 1) { if ($_sig_name ne 'PIPE' && $_no_sigmsg == 0) { print {*STDERR} "\n"; } if ($_no_kill9 == 1 || $_sig_name eq 'PIPE') { kill('INT', $_is_MSWin32 ? -$$ : -getpgrp); } else { kill('KILL', -$$, $main_proc_id); } } } } ## ---------------------------------------------------------------------- ## For child processes / threads. if (!$_is_main_thread && $_is_sig == 1 && -d $tmp_dir) { ## Signal process group to terminate. if (++$_handler_cnt == 1) { ## Obtain lock. open my $CHILD_LOCK, '+>>', "$tmp_dir/child.lock"; flock $CHILD_LOCK, LOCK_EX; ## Notify the main process that I've died. if ($_sig_name eq '__DIE__' && ! -f "$tmp_dir/died") { local $@; eval { open my $_FH, '>', "$tmp_dir/died"; close $_FH; }; } ## Signal process group to terminate. if (! -f "$tmp_dir/killed" && ! -f "$tmp_dir/stopped") { local $@; eval { open my $_FH, '>', "$tmp_dir/killed"; close $_FH; }; if ($_sig_name eq 'PIPE') { kill('PIPE', $main_proc_id, -$$); } else { kill('INT', $main_proc_id, -$$); } } ## Release lock. flock $CHILD_LOCK, LOCK_UN; close $CHILD_LOCK; } } ## ---------------------------------------------------------------------- ## Exit thread/process with status. if ($_is_sig == 1 && $_no_kill9 == 0) { sleep 0.065 for (1..6); } if ($INC{'threads.pm'} && threads->can('exit')) { threads->exit($_exit_status); } CORE::exit($_exit_status); } } ############################################################################### ## ---------------------------------------------------------------------------- ## Shutdown MCEs that were previously initiated by this process ID and are ## still running. ## ############################################################################### sub _shutdown_mce { shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal'); my $_exit_status = $_[0] || $?; if (defined $_mce_spawned_ref) { my $_tid = ($INC{'threads.pm'}) ? threads->tid() : ''; $_tid = '' unless defined $_tid; for my $_mce_sid (keys %{ $_mce_spawned_ref }) { if ($_mce_spawned_ref->{$_mce_sid}->wid()) { $_mce_spawned_ref->{$_mce_sid}->exit($_exit_status) if ($_mce_spawned_ref->{$_mce_sid}->pid() == $$); } else { $_mce_spawned_ref->{$_mce_sid}->shutdown() if ($_mce_sid =~ /\A$$\.$_tid\./); } delete $_mce_spawned_ref->{$_mce_sid}; } } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Signal handlers for __DIE__ & __WARN__ utilized by MCE. ## ############################################################################### sub _die_handler { shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal'); if (!defined $^S || $^S) { if ( ($INC{'threads.pm'} && threads->tid() != 0) || $ENV{'PERL_IPERL_RUNNING'} ) { # thread env or running inside IPerl, check stack trace my $_t = Carp::longmess(); $_t =~ s/\teval [^\n]+\n$//; if ( $_t =~ /^(?:[^\n]+\n){1,7}\teval / || $_t =~ /\n\teval [^\n]+\n\t(?:eval|Try)/ ) { CORE::die(@_); } } else { # normal env, trust $^S CORE::die(@_); } } local $\ = undef; ## Set $MCE::Signal::display_die_with_localtime = 1; ## when wanting the output to contain the localtime. if (defined $_[0]) { if ($MCE::Signal::display_die_with_localtime) { my $_time_stamp = localtime; print {*STDERR} "## $_time_stamp: $prog_name: ERROR:\n", $_[0]; } else { print {*STDERR} $_[0]; } } MCE::Signal->stop_and_exit('__DIE__'); if ($INC{'threads.pm'} && threads->can('exit')) { threads->exit(255); } CORE::exit(255); } sub _warn_handler { shift @_ if (defined $_[0] && $_[0] eq 'MCE::Signal'); ## Ignore thread warnings during exiting. return if ( $_[0] =~ /^A thread exited while \d+ threads were running/ || $_[0] =~ /^Attempt to free unreferenced scalar/ || $_[0] =~ /^Perl exited with active threads/ || $_[0] =~ /^Thread \d+ terminated abnormally/ ); local $\ = undef; ## Set $MCE::Signal::display_warn_with_localtime = 1; ## when wanting the output to contain the localtime. if (defined $_[0]) { if ($MCE::Signal::display_warn_with_localtime) { my $_time_stamp = localtime; print {*STDERR} "## $_time_stamp: $prog_name: WARNING:\n", $_[0]; } else { print {*STDERR} $_[0]; } } return; } 1; __END__ ############################################################################### ## ---------------------------------------------------------------------------- ## Module usage. ## ############################################################################### =head1 NAME MCE::Signal - Temporary directory creation/cleanup and signal handling =head1 VERSION This document describes MCE::Signal version 1.608 =head1 SYNOPSIS use MCE::Signal qw( [-keep_tmp_dir] [-use_dev_shm] ); use MCE; ## MCE loads MCE::Signal when not present. ## Include MCE::Signal first for options to take effect. =head1 DESCRIPTION This package configures $SIG{HUP,INT,PIPE,QUIT,TERM,XCPU,XFSZ} to point to stop_and_exit and creates a temporary directory. The main process and workers receiving said signals call stop_and_exit, which signals all workers to terminate, removes the temporary directory unless -keep_tmp_dir is specified, and terminates itself. The location of the temp directory resides under $ENV{TEMP} if defined, otherwise /dev/shm if writeable and -use_dev_shm is specified, or /tmp. The temp dir resides under $ENV{TEMP}/mce/ for native Perl on Microsoft Windows. As of MCE 1.405, MCE::Signal no longer calls setpgrp by default. Pass the -setpgrp option to MCE::Signal to call setpgrp. ## Running MCE through Daemon::Control requires setpgrp to be called ## for MCE releases 1.511 and below. use MCE::Signal qw(-setpgrp); ## Not necessary for MCE 1.512 and above use MCE; The following are available arguments and their meanings. -keep_tmp_dir - The temporary directory is not removed during exiting A message is displayed with the location afterwards -use_dev_shm - Create the temporary directory under /dev/shm -no_sigmsg - Do not display a message when receiving a signal -no_kill9 - Do not kill -9 after receiving a signal to terminate -setpgrp - Calls setpgrp to set the process group for the process This option ensures all workers terminate when reading STDIN for MCE releases 1.511 and below. cat big_input_file | ./mce_script.pl | head -10 This works fine without the -setpgrp option: ./mce_script.pl < big_input_file | head -10 Nothing is exported by default. Exportable are 1 variable and 2 subroutines. $tmp_dir - Path to the temporary directory. stop_and_exit - Described below sys_cmd - Described below =head2 stop_and_exit ( [ $exit_status | $signal ] ) Stops execution, removes temp directory, and exits the entire application. Pass 'TERM' to terminate a spawned or running MCE state. MCE::Signal::stop_and_exit(1); MCE::Signal::stop_and_exit('TERM'); =head2 sys_cmd ( $command ) The system function in Perl ignores SIGNINT and SIGQUIT. These 2 signals are sent to the command being executed via system() but not back to the underlying Perl script. For this reason, sys_cmd was added to MCE::Signal. ## Execute command and return the actual exit status. The perl script ## is also signaled if command caught SIGINT or SIGQUIT. use MCE::Signal qw(sys_cmd); ## Include before MCE use MCE; my $exit_status = sys_cmd($command); =head1 EXAMPLES ## Creates tmp_dir under $ENV{TEMP} if defined, otherwise /tmp use MCE::Signal; use MCE::Signal qw( :all ); ## Attempt to create tmp_dir under /dev/shm if writable use MCE::Signal qw( -use_dev_shm ); ## Keep tmp_dir after script terminates use MCE::Signal qw( -keep_tmp_dir ); use MCE::Signal qw( -use_dev_shm -keep_tmp_dir ); =head1 INDEX L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =cut MCE-1.608/lib/MCE.pod0000644000076400007640000000764112511672701013034 0ustar mariomario =head1 NAME MCE - Many-Core Engine for Perl providing parallel processing capabilities =head1 VERSION This document describes MCE version 1.608 =head1 DESCRIPTION Many-Core Engine (MCE) for Perl helps enable a new level of performance by maximizing all available cores. MCE spawns a pool of workers and therefore does not fork a new process per each element of data. Instead, MCE follows a bank queuing model. Imagine the line being the data and bank-tellers the parallel workers. MCE enhances that model by adding the ability to chunk the next n elements from the input stream to the next available worker. Input data is optional in MCE. Thus, input data is not required to run MCE. =head1 CORE MODULES Three modules make up the core engine for MCE. =over 3 =item L Provides the Core API for Many-Core Engine. =item L Temporary directory creation/cleanup and signal handling. =item L Utility functions for Many-Core Engine. =back =head1 MCE EXTRAS There are 3 add-on modules. =over 3 =item L Provides a simple semaphore implementation supporting threads and processes. =item L Provides a hybrid queuing implementation for MCE supporting normal queues and priority queues from a single module. MCE::Queue exchanges data via the core engine to enable queueing to work for both children (spawned from fork) and threads. =item L Enables workers to receive and pass on information orderly with zero involvement by the manager process while running. =back =head1 MCE MODELS The models take Many-Core Engine to a new level for ease of use. Two options (chunk_size and max_workers) are configured automatically as well as spawning and shutdown. =over 3 =item L Provides a parallel loop utilizing MCE for building creative loops. =item L A parallel flow model for building creative applications. This makes use of user_tasks in MCE. The author has full control when utilizing this model. MCE::Flow is similar to MCE::Loop, but allows for multiple code blocks to run in parallel with a slight change to syntax. =item L Provides a parallel grep implementation similar to the native grep function. =item L Provides a parallel map model similar to the native map function. =item L Provides a parallel step implementation utilizing MCE::Queue between user tasks. MCE::Step is a spin off from MCE::Flow with a touch of MCE::Stream. This model, introduced in 1.506, allows one to pass data from one sub-task into the next transparently. =item L Provides an efficient parallel implementation for chaining multiple maps and greps together through user_tasks and MCE::Queue. Like with MCE::Flow, MCE::Stream can run multiple code blocks in parallel with a slight change to syntax from MCE::Map and MCE::Grep. =back =head1 MISCELLANEOUS Miscellaneous additions included with the distribution. =over 3 =item L Provides a collection of sugar methods and output iterators for preserving output order. =item L A placeholder for the examples included with the distribution. Describes various demonstrations for MCE including a Monte Carlo simulation. =item L Exports functions mapped directly to MCE methods; e.g. mce_wid. The module allows 3 options; :manager, :worker, and :getter. =back =head1 REQUIREMENTS Perl 5.8.0 or later. PDL::IO::Storable is required in scripts running PDL. =head1 SOURCE The source is hosted at L =head1 AUTHOR Mario E. Roy, Smarioeroy AT gmail DOT comE> =head1 COPYRIGHT AND LICENSE Copyright (C) 2012-2015 by Mario E. Roy MCE is released under the same license as Perl. See L for more information. =cut MCE-1.608/CREDITS0000644000076400007640000002075312511671246012200 0ustar mariomario All the people reporting problems and fixes or solutions. More specifically in alphabetical order by last name. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### Berger, Joel The inspiration for the sixth model (MCE::Step) came from reading a blog by Joel, "Writing Non-Blocking Applications with Mojolicious: Part 3": http://blogs.perl.org/users/joel_berger/2014/01/ writing-non-blocking-applications-with-mojolicious-part-3.html Bouras, George For reporting sockets failing under the Windows environment due to a period of inactivity. An example is when workers execute an external command taking beyond 4 minutes to complete. On returning, the sockets have gone stale with a CPU core going 100% at that point. The MCE 1.601 release is attributed to the many use cases I received from George. Cantrell, David (DrHyde on perlmonks.org) For reporting on MCE hanging with cpan -t MCE under his environment. Also, thank you for providing me a guest account via ssh. I was able to reproduce the issue. Thus, added writeable check and croak if tmp dir is not writeable. One cannot assume that /dev/shm is writeable by everyone. :) Collet, Eric For the inspiration to the tre-agrep binary. I first learned of tre-agrep from Eric. He emailed me running times for a couple scenarios utilizing a single core. Thus increasing the need to have a MCE-driven wrapper to run tre-agrep in parallel. The bin/mce_grep script was created to show folks how one may go about chunking data between Perl and an external C binary efficiently without invoking the shell. The script supports grep, egrep, fgrep, agrep and tre-agrep. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### Eldai, Hisham While working on the biofasta examples, Hisham introduced me to hg19.fa. A couple records inside hg19 (driven by $/ = "\n>") are 250 MiB in size. Memory optimizations in v1.600 came about by running against hg19.fa hundreds of times. Farrell, David David wrote a nice article about MCE (love the picture). What stood out was his example. I never thought about using mce_grep { ... } <$fileHandle> without the _f (mce_grep_f). At the time, updated MCE::Grep and MCE::Map to be fully aware of wantarray. The blog "Make your code run faster with Perl's secret turbo module" is located at: http://perltricks.com/article/61/2014/1/21/ Make-your-code-run-faster-with-Perl-s-secret-turbo-module Gorwits, Oliver For reporting on a couple issues with MCE along with a solution for each. Pointed me in the right direction for improving the logic for the die handler. Basically, eval { die 'this should not cause MCE to die' }; This has reached 100% (all cases, threads and processes) in MCE 1.600. Halpenny, Shawn For reporting an issue (bug RT#94706) with signal handling in MCE::Signal. Also, thank you for tip on getpgrp. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### Jacobsen, Dana For reporting an issue with get_ncpu on AIX. In addition, suggesting various solutions. Karasik, Dmitry For bug (RT#102802) and elegant solution for the die handler inside MCE::Core::Worker and MCE::Signal. Kharchenko, Oleksandr Someone once asked Oleksandr why not use MCE. Oleksandr created 2 modules on CPAN, Parallel::DataPipe and Parallel::parallel_map. I used his example heavily in comparing IPC between pipe-driven and socket-driven. Not pleased with MCE's performance with chunk_size => 1, although 2 was as fast if not faster, I tried various things to include IO::Select. Finally, realized increasing the number of data channels was all that was needed (v1.500). Kulow, Stephan For making the OpenSUSE package for MCE. https://build.opensuse.org/package/show/devel:languages:perl:CPAN-M/perl-MCE Lu, Henry For listening during our walks on numerous occasions while I talked about how ridiculously hard to provide support for the Windows environment. For encouraging me to hang in there when I wanted to give up. Mabon, Philip For reporting on a couple issues with MCE in particular bug RT#92627. Mertens, David I am thankful for the tip on making PDL + MCE a possibility. Workers were crashing during exiting. Adding "sub PDL::CLONE_SKIP { 1 }" inside MCE.pm solved this issue entirely. Also, for tips on effectively using PDL in his example at https://gist.github.com/run4flat/4942132. Barrier synchronization in MCE came from reading parallel_sync in David's Perl module; PDL::Parallel::threads::SIMD. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### Mughal, Zakariyya First, Demian Riccardi introduced me to Devel::IPerl by Zakariyya. The die handlers in MCE brake the user experience. It seems that looking at $^S (for Perl state) is not enough. MCE v1.600 fixes this once and for all. The answer is in perl-5.20.1/cpan/CGI/lib/CGI/Carp.pm (Carp::longmess). Ogulin, Joseph For providing the initial perl-MCE.spec file. For the cool remarks at work about MCE being really big :) Riccardi, Demian I received a message from Demian asking for mce_map. His message requested ( my @a = mce_map { ... } 1..100 ). Five models were introduced in v1.500; MCE::Flow, MCE::Grep, MCE::Loop, MCE::Map, and MCE::Stream. Rowe, Jillian For reporting on IO::File handles not working with MCE. Fixed in 1.515. For believing in possibilities beyond text-book thinking. Roy, Sylvia For driving on long trips while I worked in the passenger seat further developing MCE. Not to forget, locally around town as well. Gosh, a lot of MCE was developed on the road not to forget Panera Bread as well. Šabata, Petr For initial package submission at Red Hat. https://bugzilla.redhat.com/show_bug.cgi?id=1162531 Sasser, Tom For reporting on bin/mce_grep failing with Perl 5.8.x. Schlichting, Florian For making a Debian package for MCE: http://pkgs.org/search/libmce-perl. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### Shen, Wei For the BioUtil CPAN module. BioUtil::Seq::FastaReader inspired me to create parallel examples (biofasta folder) in MCE 1.600. We tried to make FastaReader in BioUtil::Seq even faster. Sjøgren, Adam For reporting on running /usr/bin/time mce_script.pl and pressing Ctrl-C failing. The default behavior in MCE::Signal is to call setpgrp. MCE 1.403 adds a new option to MCE::Signal to not call setpgrp. This also gives the developer finer control as to where setpgrp should be called, if ever needed, such as in the child process and not the manager process. use MCE::Signal qw(-no_setpgrp); use MCE; Smith, Marcus For reporting a problem with MCE including a sample script demonstrating MCE not 100% UTF-8 safe. All this time, totally overlooked the fact that the length function deals in logical characters, not physical bytes. :) Thalhammer, Jeffrey Ryan For submitting a feature request for lazy arrays support. Although a lazy array cannot be used directly for input_data, support for iterator objects was added to MCE 1.505; e.g. input_data => $iterator_ref; This enabled support for lazy arrays using closures. I am thankful for the small code snippet highlighting a use-case of MCE. I would have failed initially without it. Your example was placed under the "SYNTAX for INPUT_DATA" section in MCE::Core for others to see on how to process output from Path::Iterator::Rule in parallel. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### Thank you. Warm regards, Mario MCE-1.608/t/0000755000076400007640000000000012511673554011420 5ustar mariomarioMCE-1.608/t/05_mce_grep.t0000755000076400007640000000167212511671246013677 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 4; use MCE::Grep; ## preparation my $in_file = MCE->tmp_dir . '/input.txt'; my $fh_data = \*DATA; open my $fh, '>', $in_file; binmode $fh; print {$fh} "1\n2\n3\n4\n5\n6\n7\n8\n9\n"; close $fh; my @a; MCE::Grep::init { max_workers => 2 }; sub _task { $_ % 3 == 0 } ## mce_grep can take a code block, e.g: mce_grep { code } ( 1..9 ) ## below, workers will persist between runs @a = mce_grep \&_task, ( 1..9 ); is( join('', @a), '369', 'block_ref: check results for array' ); @a = mce_grep_f \&_task, $in_file; is( join('', @a), "3\n6\n9\n", 'block_ref: check results for path' ); @a = mce_grep_f \&_task, $fh_data; is( join('', @a), "3\n6\n9\n", 'block_ref: check results for glob' ); @a = mce_grep_s \&_task, 1, 9, 1; is( join('', @a), '369', 'block_ref: check results for sequence' ); MCE::Grep::finish; ## cleanup unlink $in_file; __DATA__ 1 2 3 4 5 6 7 8 9 MCE-1.608/t/02_do_callback_result.t0000755000076400007640000000346312511671246015727 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 5; use MCE; my (@ans, @rpl, $mce); ############################################################################### sub callback { my ($wid) = @_; push @ans, $wid; return; } $mce = MCE->new( max_workers => 4, user_func => sub { MCE->do('callback', MCE->wid()); return; } ); @ans = (); $mce->run; is(join('', sort @ans), '1234', 'test1: check that wid is correct'); ############################################################################### sub callback2 { my ($wid) = @_; push @ans, $wid; return $wid * 2; } sub callback3 { my ($ans) = @_; push @rpl, $ans; return; } $mce = MCE->new( max_workers => 4, user_func => sub { my $reply = MCE->do('callback2', MCE->wid()); MCE->do('callback3', $reply); return; } ); @ans = (); @rpl = (); $mce->run; is(join('', sort @ans), '1234', 'test2: check that wid is correct'); is(join('', sort @rpl), '2468', 'test3: check that scalar is correct'); ############################################################################### sub callback4 { return @rpl; } sub callback5 { my ($a_ref) = @_; my %h = (); @ans = (); foreach (@{ $a_ref }) { push @ans, $_ / 2; $h{$_ / 2} = $_; } return %h; } sub callback6 { my ($h_ref) = @_; @rpl = (); foreach (sort keys %{ $h_ref }) { $rpl[$_ - 1] = $h_ref->{$_}; } return; } $mce = MCE->new( max_workers => 1, user_func => sub { my @reply = MCE->do('callback4'); my %reply = MCE->do('callback5', \@reply); MCE->do('callback6', \%reply); return; } ); $mce->run; is(join('', sort @ans), '1234', 'test4: check that list is correct'); is(join('', sort @rpl), '2468', 'test5: check that hash is correct'); MCE-1.608/t/00_required_signals.t0000755000076400007640000000124012511671246015440 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More; if ($^O eq 'MSWin32') { plan 'tests' => 5; } else { plan 'tests' => 6; } ## Optional signals detected by MCE::Signal and not tested here are ## $SIG{XCPU} & $SIG{XFSZ}. MCE::Signal assigns signal handlers for ## the following by default. ## ok(exists $SIG{HUP }, 'Check that $SIG{HUP} exists'); ok(exists $SIG{INT }, 'Check that $SIG{INT} exists'); ok(exists $SIG{PIPE}, 'Check that $SIG{PIPE} exists'); ok(exists $SIG{QUIT}, 'Check that $SIG{QUIT} exists'); ok(exists $SIG{TERM}, 'Check that $SIG{TERM} exists'); if ($^O ne 'MSWin32') { ok(exists $SIG{CHLD}, 'Check that $SIG{CHLD} exists'); } MCE-1.608/t/05_mce_stream.t0000755000076400007640000000415612511671246014235 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 9; use MCE::Stream; ## preparation my $in_file = MCE->tmp_dir . '/input.txt'; my $fh_data = \*DATA; my $fh_pos = tell $fh_data; open my $fh, '>', $in_file; binmode $fh; print {$fh} "1\n2\n3\n4\n5\n6\n7\n8\n9\n"; close $fh; ## reminder ; MCE::Stream processes sub-tasks from right-to-left my $answers = '6 12 18 24 30 36 42 48 54'; my $ans_mix = '18 36 54'; my @a; MCE::Stream::init { max_workers => [ 2 , 2 ], # run with 2 workers for both sub-tasks task_name => [ 'b' , 'a' ] }; sub _task_a { chomp; $_ * 2 } sub _task_b { $_ * 3 } ## @a = mce_stream ... # @a is populated after running # not recommended for big input data @a = mce_stream \&_task_b, \&_task_a, ( 1..9 ); is( join(' ', @a), $answers, 'array: check results for array' ); @a = mce_stream_f \&_task_b, \&_task_a, $in_file; is( join(' ', @a), $answers, 'array: check results for path' ); @a = mce_stream_f \&_task_b, \&_task_a, $fh_data; is( join(' ', @a), $answers, 'array: check results for glob' ); @a = mce_stream_s \&_task_b, \&_task_a, 1, 9, 1; is( join(' ', @a), $answers, 'array: check results for sequence' ); seek($fh_data, $fh_pos, 0); ## mce_stream \@a, ... # @a is populated while running # faster and consumes less memory mce_stream \@a, \&_task_b, \&_task_a, ( 1..9 ); is( join(' ', @a), $answers, 'array_ref: check results for array' ); mce_stream_f \@a, \&_task_b, \&_task_a, $in_file; is( join(' ', @a), $answers, 'array_ref: check results for path' ); mce_stream_f \@a, \&_task_b, \&_task_a, $fh_data; is( join(' ', @a), $answers, 'array_ref: check results for glob' ); mce_stream_s \@a, \&_task_b, \&_task_a, 1, 9, 1; is( join(' ', @a), $answers, 'array_ref: check results for sequence' ); MCE::Stream::finish; @a = mce_stream { mode => 'map', code => sub { $_ * 2 * 3 } }, { mode => 'grep', code => sub { $_ % 3 == 0 } }, ( 1..9 ); is( join(' ', @a), $ans_mix, 'array: check results for mix_mode' ); MCE::Stream::finish; ## cleanup unlink $in_file; __DATA__ 1 2 3 4 5 6 7 8 9 MCE-1.608/t/04_norm_que_manager.t0000755000076400007640000000723512511671246015435 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 32; use MCE::Flow max_workers => 1; use MCE::Queue; ############################################################################### ## MCE::Queue supports 3 operating modes (local, manager, worker). ## This will test MCE::Queue (normal queue) by the MCE manager process. ## ## *{ 'MCE::Queue::clear' } = \&MCE::Queue::_mce_m_clear; ## *{ 'MCE::Queue::enqueue' } = \&MCE::Queue::_mce_m_enqueue; ## *{ 'MCE::Queue::dequeue' } = \&MCE::Queue::_mce_m_dequeue; ## *{ 'MCE::Queue::insert' } = \&MCE::Queue::_mce_m_insert; ## *{ 'MCE::Queue::pending' } = \&MCE::Queue::_pending; ## *{ 'MCE::Queue::peek' } = \&MCE::Queue::_peek; my (@a, $q, @r); ############################################################################### ## FIFO tests @a = (); $q = MCE::Queue->new( queue => \@a, type => $MCE::Queue::FIFO ); $q->enqueue('1', '2'); $q->enqueue('3'); $q->enqueue('4'); is( join('', @a), '1234', 'fifo, check enqueue' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '123', 'fifo, check dequeue' ); is( join('', @a), '4', 'fifo, check array' ); $q->clear; is( scalar(@a), 0, 'fifo, check clear' ); $q->enqueue('a', 'b', 'c', 'd'); $q->insert( 1, 'e', 'f'); $q->insert( 3, 'g'); $q->insert( -2, 'h'); $q->insert( 7, 'i'); $q->insert( 9, 'j'); $q->insert( 20, 'k'); $q->insert(-10, 'l'); $q->insert(-12, 'm'); $q->insert(-20, 'n'); is( join('', @a) , 'nmalefgbhcidjk', 'fifo, check insert' ); is( $q->pending(), 14, 'fifo, check pending' ); is( $q->peek( ), 'n', 'fifo, check peek at head' ); is( $q->peek( 0), 'n', 'fifo, check peek at index 0' ); is( $q->peek( 2), 'a', 'fifo, check peek at index 2' ); is( $q->peek( 13), 'k', 'fifo, check peek at index 13' ); is( $q->peek( 20), undef, 'fifo, check peek at index 20' ); is( $q->peek( -2), 'j', 'fifo, check peek at index -2' ); is( $q->peek(-13), 'm', 'fifo, check peek at index -13' ); is( $q->peek(-14), 'n', 'fifo, check peek at index -14' ); is( $q->peek(-15), undef, 'fifo, check peek at index -15' ); is( $q->peek(-20), undef, 'fifo, check peek at index -20' ); ############################################################################### ## LIFO tests @a = (); $q = MCE::Queue->new( queue => \@a, type => $MCE::Queue::LIFO ); $q->enqueue('1', '2'); $q->enqueue('3'); $q->enqueue('4'); ## Note (lifo) ## ## Enqueue appends to an array similarly to fifo ## Thus, the enqueue check is identical to fifo is( join('', @a), '1234', 'lifo, check enqueue' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '432', 'lifo, check dequeue' ); is( join('', @a), '1', 'lifo, check array' ); $q->clear; is( scalar(@a), 0, 'lifo, check clear' ); $q->enqueue('a', 'b', 'c', 'd'); $q->insert( 1, 'e', 'f'); $q->insert( 3, 'g'); $q->insert( -2, 'h'); $q->insert( 7, 'i'); $q->insert( 9, 'j'); $q->insert( 20, 'k'); $q->insert(-10, 'l'); $q->insert(-12, 'm'); $q->insert(-20, 'n'); is( join('', @a) , 'kjaibhcgefldmn', 'lifo, check insert' ); is( $q->pending(), 14, 'lifo, check pending' ); is( $q->peek( ), 'n', 'lifo, check peek at head' ); is( $q->peek( 0), 'n', 'lifo, check peek at index 0' ); is( $q->peek( 2), 'd', 'lifo, check peek at index 2' ); is( $q->peek( 13), 'k', 'lifo, check peek at index 13' ); is( $q->peek( 20), undef, 'lifo, check peek at index 20' ); is( $q->peek( -2), 'j', 'lifo, check peek at index -2' ); is( $q->peek(-13), 'm', 'lifo, check peek at index -13' ); is( $q->peek(-14), 'n', 'lifo, check peek at index -14' ); is( $q->peek(-15), undef, 'lifo, check peek at index -15' ); is( $q->peek(-20), undef, 'lifo, check peek at index -20' ); MCE-1.608/t/03_user_args.t0000755000076400007640000000155012511671246014101 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 4; use MCE::Flow max_workers => 1; sub check_hello { my ($arg1, $arg2) = @_; is( $arg1, 'hello', 'check user_args, hello' ); is( $arg2, 'there', 'check user_args, there' ); return; } sub check_other { my ($arg1, $arg2) = @_; is( $arg1, 'sunny', 'check user_args, sunny' ); is( $arg2, 'today', 'check user_args, today' ); return; } ## Workers persist between runs when passed a reference to a subroutine. sub task { my ($arg1, $arg2) = @{ MCE->user_args }; if ($arg1 eq 'hello') { MCE->do('check_hello', $arg1, $arg2); } else { MCE->do('check_other', $arg1, $arg2); } return; } mce_flow { user_args => [ 'hello', 'there' ] }, \&task; mce_flow { user_args => [ 'sunny', 'today' ] }, \&task; ## Shutdown workers. MCE::Flow::finish; MCE-1.608/t/06_nodata_flow.t0000755000076400007640000000047412511671246014413 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 1; use MCE::Flow; MCE::Flow::init { max_workers => 4 }; ## input_data is not required to run mce_flow my @a = mce_flow sub { MCE->gather(MCE->wid * 2); }; is( join('', sort @a), '2468', 'check gathered data' ); MCE::Flow::finish; MCE-1.608/t/01_load_mce.t0000755000076400007640000000037612511671246013655 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 2; ## MCE::Signal is loaded by MCE automatically and is not neccessary in ## scripts unless wanting to export or pass options. BEGIN { use_ok('MCE'); use_ok('MCE::Util'); } MCE-1.608/t/04_prio_que_local.t0000755000076400007640000001301112511671246015100 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 40; use MCE::Queue; ############################################################################### ## MCE::Queue supports 3 operating modes (local, manager, worker). ## This will test MCE::Queue (priority queue) in local mode. ## ## Local mode is selected when MCE is not present, during importing of ## MCE::Queue, or when initiating queues from inside the worker process. ## ## *{ 'MCE::Queue::clear' } = \&MCE::Queue::_clear; ## *{ 'MCE::Queue::enqueuep' } = \&MCE::Queue::_enqueuep; ## *{ 'MCE::Queue::dequeue' } = \&MCE::Queue::_dequeue; ## *{ 'MCE::Queue::insertp' } = \&MCE::Queue::_insertp; ## *{ 'MCE::Queue::pending' } = \&MCE::Queue::_pending; ## *{ 'MCE::Queue::peekp' } = \&MCE::Queue::_peekp; ## *{ 'MCE::Queue::peekh' } = \&MCE::Queue::_peekh; ## *{ 'MCE::Queue::heap' } = \&MCE::Queue::_heap; my ($q, @r, @h); ############################################################################### ## FIFO tests $q = MCE::Queue->new( type => $MCE::Queue::FIFO ); $q->enqueuep(5, '1', '2'); $q->enqueuep(5, '3'); $q->enqueuep(5, '4'); is( join('', @{ $q->_get_aref(5) }), '1234', 'fifo, check enqueuep' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '123', 'fifo, check dequeue' ); is( join('', @{ $q->_get_aref(5) }), '4', 'fifo, check array' ); $q->clear; is( $q->_get_aref(5), undef, 'fifo, check clear' ); $q->enqueuep(5, 'a', 'b', 'c', 'd'); $q->insertp(5, 1, 'e', 'f'); $q->insertp(5, 3, 'g'); $q->insertp(5, -2, 'h'); $q->insertp(5, 7, 'i'); $q->insertp(5, 9, 'j'); $q->insertp(5, 20, 'k'); $q->insertp(5, -10, 'l'); $q->insertp(5, -12, 'm'); $q->insertp(5, -20, 'n'); is( join('', @{ $q->_get_aref(5) }), 'nmalefgbhcidjk', 'fifo, check insertp' ); is( $q->pending(), 14, 'fifo, check pending' ); is( $q->peekp(5 ), 'n', 'fifo, check peekp at head' ); is( $q->peekp(5, 0), 'n', 'fifo, check peekp at index 0' ); is( $q->peekp(5, 2), 'a', 'fifo, check peekp at index 2' ); is( $q->peekp(5, 13), 'k', 'fifo, check peekp at index 13' ); is( $q->peekp(5, 20), undef, 'fifo, check peekp at index 20' ); is( $q->peekp(5, -2), 'j', 'fifo, check peekp at index -2' ); is( $q->peekp(5, -13), 'm', 'fifo, check peekp at index -13' ); is( $q->peekp(5, -14), 'n', 'fifo, check peekp at index -14' ); is( $q->peekp(5, -15), undef, 'fifo, check peekp at index -15' ); is( $q->peekp(5, -20), undef, 'fifo, check peekp at index -20' ); ############################################################################### ## LIFO tests $q = MCE::Queue->new( type => $MCE::Queue::LIFO ); $q->enqueuep(5, '1', '2'); $q->enqueuep(5, '3'); $q->enqueuep(5, '4'); ## Note (lifo) ## ## Enqueue appends to an array similarly to fifo ## Thus, the enqueuep check is identical to fifo is( join('', @{ $q->_get_aref(5) }), '1234', 'lifo, check enqueuep' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '432', 'lifo, check dequeue' ); is( join('', @{ $q->_get_aref(5) }), '1', 'lifo, check array' ); $q->clear; is( $q->_get_aref(5), undef, 'lifo, check clear' ); $q->enqueuep(5, 'a', 'b', 'c', 'd'); $q->insertp(5, 1, 'e', 'f'); $q->insertp(5, 3, 'g'); $q->insertp(5, -2, 'h'); $q->insertp(5, 7, 'i'); $q->insertp(5, 9, 'j'); $q->insertp(5, 20, 'k'); $q->insertp(5, -10, 'l'); $q->insertp(5, -12, 'm'); $q->insertp(5, -20, 'n'); is( join('', @{ $q->_get_aref(5) }), 'kjaibhcgefldmn', 'lifo, check insertp' ); is( $q->pending(), 14, 'lifo, check pending' ); is( $q->peekp(5 ), 'n', 'lifo, check peekp at head' ); is( $q->peekp(5, 0), 'n', 'lifo, check peekp at index 0' ); is( $q->peekp(5, 2), 'd', 'lifo, check peekp at index 2' ); is( $q->peekp(5, 13), 'k', 'lifo, check peekp at index 13' ); is( $q->peekp(5, 20), undef, 'lifo, check peekp at index 20' ); is( $q->peekp(5, -2), 'j', 'lifo, check peekp at index -2' ); is( $q->peekp(5, -13), 'm', 'lifo, check peekp at index -13' ); is( $q->peekp(5, -14), 'n', 'lifo, check peekp at index -14' ); is( $q->peekp(5, -15), undef, 'lifo, check peekp at index -15' ); is( $q->peekp(5, -20), undef, 'lifo, check peekp at index -20' ); ############################################################################### ## HIGHEST priority tests $q = MCE::Queue->new( porder => $MCE::Queue::HIGHEST, type => $MCE::Queue::FIFO ); $q->enqueuep(5, 'a', 'b'); # priority queue $q->enqueuep(7, 'e', 'f'); # priority queue $q->enqueue ( 'i', 'j'); # normal queue $q->enqueuep(8, 'g', 'h'); # priority queue $q->enqueuep(6, 'c', 'd'); # priority queue @h = $q->heap; is( join('', @h), '8765', 'highest, check heap' ); is( $q->peekh( 0), '8', 'lowest, check peekh at index 0' ); is( $q->peekh(-2), '6', 'lowest, check peekh at index -2' ); @r = $q->dequeue(10); is( join('', @r), 'ghefcdabij', 'highest, check dequeue' ); ############################################################################### ## LOWEST priority tests $q = MCE::Queue->new( porder => $MCE::Queue::LOWEST, type => $MCE::Queue::FIFO ); $q->enqueuep(5, 'a', 'b'); # priority queue $q->enqueuep(7, 'e', 'f'); # priority queue $q->enqueue ( 'i', 'j'); # normal queue $q->enqueuep(8, 'g', 'h'); # priority queue $q->enqueuep(6, 'c', 'd'); # priority queue @h = $q->heap; is( join('', @h), '5678', 'lowest, check heap' ); is( $q->peekh( 0), '5', 'lowest, check peekh at index 0' ); is( $q->peekh(-2), '7', 'lowest, check peekh at index -2' ); @r = $q->dequeue(10); is( join('', @r), 'abcdefghij', 'highest, check dequeue' ); MCE-1.608/t/01_load_signal_tag.t0000755000076400007640000000034712511671246015217 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 2; ## Always load MCE::Signal before MCE when wanting to export or pass options. BEGIN { use_ok('MCE::Signal', qw( :all :tmp_dir )); use_ok('MCE'); } MCE-1.608/t/05_mce_flow.t0000755000076400007640000000424412511671246013707 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 4; use MCE::Flow; use MCE::Queue; ## preparation my $in_file = MCE->tmp_dir . '/input.txt'; my $fh_data = \*DATA; open my $fh, '>', $in_file; binmode $fh; print {$fh} "1\n2\n3\n4\n5\n6\n7\n8\n9\n"; close $fh; ## output iterator to ensure output order sub output_iterator { my ($gather_ref) = @_; my %tmp; my $order_id = 1; @{ $gather_ref } = (); ## reset array return sub { my ($data_ref, $chunk_id) = @_; $tmp{ $chunk_id } = $data_ref; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ $tmp{$order_id} }; delete $tmp{$order_id++}; } return; }; } ## sub-tasks my $q = MCE::Queue->new; sub task_a { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 2 } @{ $chunk_ref }; $q->enqueue( [ \@ans, $chunk_id ] ); # forward to task_b } sub task_b { while (defined (my $next_ref = $q->dequeue)) { my @ans; my ($chunk_ref, $chunk_id) = @{ $next_ref }; push @ans, map { $_ * 3 } @{ $chunk_ref }; MCE->gather(\@ans, $chunk_id); # send to output_iterator } } ## Reminder; MCE::Flow processes sub-tasks from left-to-right my $answers = '6 12 18 24 30 36 42 48 54'; my @a; MCE::Flow::init { max_workers => [ 2 , 2 ], # run with 2 workers for both sub-tasks task_name => [ 'a' , 'b' ], task_end => sub { my ($mce, $task_id, $task_name) = @_; if ($task_name eq 'a') { $q->enqueue((undef) x 2); } } }; mce_flow { gather => output_iterator(\@a) }, \&task_a, \&task_b, ( 1..9 ); is( join(' ', @a), $answers, 'check results for array' ); mce_flow_f { gather => output_iterator(\@a) }, \&task_a, \&task_b, $in_file; is( join(' ', @a), $answers, 'check results for path' ); mce_flow_f { gather => output_iterator(\@a) }, \&task_a, \&task_b, $fh_data; is( join(' ', @a), $answers, 'check results for glob' ); mce_flow_s { gather => output_iterator(\@a) }, \&task_a, \&task_b, 1, 9, 1; is( join(' ', @a), $answers, 'check results for sequence' ); MCE::Flow::finish; ## cleanup unlink $in_file; __DATA__ 1 2 3 4 5 6 7 8 9 MCE-1.608/t/00_required_modules.t0000755000076400007640000000064412511671246015457 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 5; ## The following are minimum Perl modules required by MCE BEGIN { use_ok('Fcntl', qw( :flock O_CREAT O_TRUNC O_RDWR O_RDONLY )); } BEGIN { use_ok('File::Path', qw( rmtree )); } BEGIN { use_ok('Socket', qw( :DEFAULT :crlf )); } BEGIN { use_ok('Storable', 2.04, qw( store retrieve freeze thaw )); } BEGIN { use_ok('Time::HiRes', qw( time )); } MCE-1.608/t/05_mce_step.t0000755000076400007640000000364412511671246013716 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 4; use MCE::Step; ## preparation my $in_file = MCE->tmp_dir . '/input.txt'; my $fh_data = \*DATA; open my $fh, '>', $in_file; binmode $fh; print {$fh} "1\n2\n3\n4\n5\n6\n7\n8\n9\n"; close $fh; ## output iterator to ensure output order sub output_iterator { my ($gather_ref) = @_; my %tmp; my $order_id = 1; @{ $gather_ref } = (); ## reset array return sub { my ($data_ref, $chunk_id) = @_; $tmp{ $chunk_id } = $data_ref; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ $tmp{$order_id} }; delete $tmp{$order_id++}; } return; }; } ## sub-tasks sub task_a { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 2 } @{ $chunk_ref }; MCE->step(\@ans, $chunk_id); # forward to task_b } sub task_b { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 3 } @{ $chunk_ref }; MCE->gather(\@ans, $chunk_id); # send to output_iterator } ## Reminder; MCE::Step processes sub-tasks from left-to-right my $answers = '6 12 18 24 30 36 42 48 54'; my @a; MCE::Step::init { max_workers => [ 2 , 2 ], # run with 2 workers for both sub-tasks task_name => [ 'a' , 'b' ] }; mce_step { gather => output_iterator(\@a) }, \&task_a, \&task_b, ( 1..9 ); is( join(' ', @a), $answers, 'check results for array' ); mce_step_f { gather => output_iterator(\@a) }, \&task_a, \&task_b, $in_file; is( join(' ', @a), $answers, 'check results for path' ); mce_step_f { gather => output_iterator(\@a) }, \&task_a, \&task_b, $fh_data; is( join(' ', @a), $answers, 'check results for glob' ); mce_step_s { gather => output_iterator(\@a) }, \&task_a, \&task_b, 1, 9, 1; is( join(' ', @a), $answers, 'check results for sequence' ); MCE::Step::finish; ## cleanup unlink $in_file; __DATA__ 1 2 3 4 5 6 7 8 9 MCE-1.608/t/04_prio_que_manager.t0000755000076400007640000001267112511671246015433 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 40; use MCE::Flow max_workers => 1; use MCE::Queue; ############################################################################### ## MCE::Queue supports 3 operating modes (local, manager, worker). ## This will test MCE::Queue (priority queue) by the MCE manager process. ## ## *{ 'MCE::Queue::clear' } = \&MCE::Queue::_mce_m_clear; ## *{ 'MCE::Queue::enqueuep' } = \&MCE::Queue::_mce_m_enqueuep; ## *{ 'MCE::Queue::dequeue' } = \&MCE::Queue::_mce_m_dequeue; ## *{ 'MCE::Queue::insertp' } = \&MCE::Queue::_mce_m_insertp; ## *{ 'MCE::Queue::pending' } = \&MCE::Queue::_pending; ## *{ 'MCE::Queue::peekp' } = \&MCE::Queue::_peekp; ## *{ 'MCE::Queue::peekh' } = \&MCE::Queue::_peekh; ## *{ 'MCE::Queue::heap' } = \&MCE::Queue::_heap; my ($q, @r, @h); ############################################################################### ## FIFO tests $q = MCE::Queue->new( type => $MCE::Queue::FIFO ); $q->enqueuep(5, '1', '2'); $q->enqueuep(5, '3'); $q->enqueuep(5, '4'); is( join('', @{ $q->_get_aref(5) }), '1234', 'fifo, check enqueuep' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '123', 'fifo, check dequeue' ); is( join('', @{ $q->_get_aref(5) }), '4', 'fifo, check array' ); $q->clear; is( $q->_get_aref(5), undef, 'fifo, check clear' ); $q->enqueuep(5, 'a', 'b', 'c', 'd'); $q->insertp(5, 1, 'e', 'f'); $q->insertp(5, 3, 'g'); $q->insertp(5, -2, 'h'); $q->insertp(5, 7, 'i'); $q->insertp(5, 9, 'j'); $q->insertp(5, 20, 'k'); $q->insertp(5, -10, 'l'); $q->insertp(5, -12, 'm'); $q->insertp(5, -20, 'n'); is( join('', @{ $q->_get_aref(5) }), 'nmalefgbhcidjk', 'fifo, check insertp' ); is( $q->pending(), 14, 'fifo, check pending' ); is( $q->peekp(5 ), 'n', 'fifo, check peekp at head' ); is( $q->peekp(5, 0), 'n', 'fifo, check peekp at index 0' ); is( $q->peekp(5, 2), 'a', 'fifo, check peekp at index 2' ); is( $q->peekp(5, 13), 'k', 'fifo, check peekp at index 13' ); is( $q->peekp(5, 20), undef, 'fifo, check peekp at index 20' ); is( $q->peekp(5, -2), 'j', 'fifo, check peekp at index -2' ); is( $q->peekp(5, -13), 'm', 'fifo, check peekp at index -13' ); is( $q->peekp(5, -14), 'n', 'fifo, check peekp at index -14' ); is( $q->peekp(5, -15), undef, 'fifo, check peekp at index -15' ); is( $q->peekp(5, -20), undef, 'fifo, check peekp at index -20' ); ############################################################################### ## LIFO tests $q = MCE::Queue->new( type => $MCE::Queue::LIFO ); $q->enqueuep(5, '1', '2'); $q->enqueuep(5, '3'); $q->enqueuep(5, '4'); ## Note (lifo) ## ## Enqueue appends to an array similarly to fifo ## Thus, the enqueuep check is identical to fifo is( join('', @{ $q->_get_aref(5) }), '1234', 'lifo, check enqueuep' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '432', 'lifo, check dequeue' ); is( join('', @{ $q->_get_aref(5) }), '1', 'lifo, check array' ); $q->clear; is( $q->_get_aref(5), undef, 'lifo, check clear' ); $q->enqueuep(5, 'a', 'b', 'c', 'd'); $q->insertp(5, 1, 'e', 'f'); $q->insertp(5, 3, 'g'); $q->insertp(5, -2, 'h'); $q->insertp(5, 7, 'i'); $q->insertp(5, 9, 'j'); $q->insertp(5, 20, 'k'); $q->insertp(5, -10, 'l'); $q->insertp(5, -12, 'm'); $q->insertp(5, -20, 'n'); is( join('', @{ $q->_get_aref(5) }), 'kjaibhcgefldmn', 'lifo, check insertp' ); is( $q->pending(), 14, 'lifo, check pending' ); is( $q->peekp(5 ), 'n', 'lifo, check peekp at head' ); is( $q->peekp(5, 0), 'n', 'lifo, check peekp at index 0' ); is( $q->peekp(5, 2), 'd', 'lifo, check peekp at index 2' ); is( $q->peekp(5, 13), 'k', 'lifo, check peekp at index 13' ); is( $q->peekp(5, 20), undef, 'lifo, check peekp at index 20' ); is( $q->peekp(5, -2), 'j', 'lifo, check peekp at index -2' ); is( $q->peekp(5, -13), 'm', 'lifo, check peekp at index -13' ); is( $q->peekp(5, -14), 'n', 'lifo, check peekp at index -14' ); is( $q->peekp(5, -15), undef, 'lifo, check peekp at index -15' ); is( $q->peekp(5, -20), undef, 'lifo, check peekp at index -20' ); ############################################################################### ## HIGHEST priority tests $q = MCE::Queue->new( porder => $MCE::Queue::HIGHEST, type => $MCE::Queue::FIFO ); $q->enqueuep(5, 'a', 'b'); # priority queue $q->enqueuep(7, 'e', 'f'); # priority queue $q->enqueue ( 'i', 'j'); # normal queue $q->enqueuep(8, 'g', 'h'); # priority queue $q->enqueuep(6, 'c', 'd'); # priority queue @h = $q->heap; is( join('', @h), '8765', 'highest, check heap' ); is( $q->peekh( 0), '8', 'lowest, check peekh at index 0' ); is( $q->peekh(-2), '6', 'lowest, check peekh at index -2' ); @r = $q->dequeue(10); is( join('', @r), 'ghefcdabij', 'highest, check dequeue' ); ############################################################################### ## LOWEST priority tests $q = MCE::Queue->new( porder => $MCE::Queue::LOWEST, type => $MCE::Queue::FIFO ); $q->enqueuep(5, 'a', 'b'); # priority queue $q->enqueuep(7, 'e', 'f'); # priority queue $q->enqueue ( 'i', 'j'); # normal queue $q->enqueuep(8, 'g', 'h'); # priority queue $q->enqueuep(6, 'c', 'd'); # priority queue @h = $q->heap; is( join('', @h), '5678', 'lowest, check heap' ); is( $q->peekh( 0), '5', 'lowest, check peekh at index 0' ); is( $q->peekh(-2), '7', 'lowest, check peekh at index -2' ); @r = $q->dequeue(10); is( join('', @r), 'abcdefghij', 'highest, check dequeue' ); MCE-1.608/t/02_do_callback_args.t0000755000076400007640000000133312511671246015337 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 4; use MCE; sub callback1 { my ($a_ref, $h_ref, $s_ref) = @_; is($a_ref->[1], 'two', 'check array reference'); is($h_ref->{'two'}, 'TWO', 'check hash reference'); is(${ $s_ref }, 'fall colors', 'check scalar reference'); return; } sub callback2 { my ($wid) = @_; is($wid, 1, 'check scalar value'); return; } my $mce = MCE->new( max_workers => 1, user_func => sub { my ($self) = @_; my @a = ('one', 'two'); my %h = ('one' => 'ONE', 'two' => 'TWO'); my $s = 'fall colors'; $self->do('callback1', \@a, \%h, \$s); $self->do('callback2', $self->wid()); return; } ); $mce->run; MCE-1.608/t/04_norm_que_worker.t0000755000076400007640000001145212511671246015330 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 32; use MCE::Flow max_workers => 1; use MCE::Queue; ############################################################################### ## MCE::Queue supports 3 operating modes (local, manager, worker). ## This will test MCE::Queue (normal queue) by the MCE worker process. ## ## *{ 'MCE::Queue::clear' } = \&MCE::Queue::_mce_w_clear; ## *{ 'MCE::Queue::enqueue' } = \&MCE::Queue::_mce_w_enqueue; ## *{ 'MCE::Queue::dequeue' } = \&MCE::Queue::_mce_w_dequeue; ## *{ 'MCE::Queue::insert' } = \&MCE::Queue::_mce_w_insert; ## *{ 'MCE::Queue::pending' } = \&MCE::Queue::_mce_w_pending; ## *{ 'MCE::Queue::peek' } = \&MCE::Queue::_mce_w_peek; my (@a, $q); sub check_clear { my ($description) = @_; is( scalar(@a), 0, $description ); } sub check_enqueue { my ($description) = @_; is( join('', @a), '1234', $description ); } sub check_insert { my ($description, $expected) = @_; is( join('', @a), $expected, $description ); } sub check_pending { my ($description, $pending) = @_; is( $pending, 14, $description ); } sub check { my ($description, $expected, $value) = @_; is( $value, $expected, $description ); } ############################################################################### ## FIFO tests @a = (); $q = MCE::Queue->new( queue => \@a, type => $MCE::Queue::FIFO ); sub check_dequeue_fifo { my (@r) = @_; is( join('', @r), '123', 'fifo, check dequeue' ); is( join('', @a), '4', 'fifo, check array' ); } mce_flow sub { my ($mce) = @_; $q->enqueue('1', '2'); $q->enqueue('3'); $q->enqueue('4'); MCE->do('check_enqueue', 'fifo, check enqueue'); my @r = $q->dequeue(2); push @r, $q->dequeue; MCE->do('check_dequeue_fifo', @r); $q->clear; MCE->do('check_clear', 'fifo, check clear'); $q->enqueue('a', 'b', 'c', 'd'); $q->insert( 1, 'e', 'f'); $q->insert( 3, 'g'); $q->insert( -2, 'h'); $q->insert( 7, 'i'); $q->insert( 9, 'j'); $q->insert( 20, 'k'); $q->insert(-10, 'l'); $q->insert(-12, 'm'); $q->insert(-20, 'n'); MCE->do('check_insert', 'fifo, check insert', 'nmalefgbhcidjk'); MCE->do('check_pending', 'fifo, check pending', $q->pending()); MCE->do('check', 'fifo, check peek at head ', 'n', $q->peek( )); MCE->do('check', 'fifo, check peek at index 0', 'n', $q->peek( 0)); MCE->do('check', 'fifo, check peek at index 2', 'a', $q->peek( 2)); MCE->do('check', 'fifo, check peek at index 13', 'k', $q->peek( 13)); MCE->do('check', 'fifo, check peek at index 20', undef, $q->peek( 20)); MCE->do('check', 'fifo, check peek at index -2', 'j', $q->peek( -2)); MCE->do('check', 'fifo, check peek at index -13', 'm', $q->peek(-13)); MCE->do('check', 'fifo, check peek at index -14', 'n', $q->peek(-14)); MCE->do('check', 'fifo, check peek at index -15', undef, $q->peek(-15)); MCE->do('check', 'fifo, check peek at index -20', undef, $q->peek(-20)); return; }; MCE::Flow::finish; ############################################################################### ## LIFO tests @a = (); $q = MCE::Queue->new( queue => \@a, type => $MCE::Queue::LIFO ); sub check_dequeue_lifo { my (@r) = @_; is( join('', @r), '432', 'lifo, check dequeue' ); is( join('', @a), '1', 'lifo, check array' ); } mce_flow sub { my ($mce) = @_; $q->enqueue('1', '2'); $q->enqueue('3'); $q->enqueue('4'); MCE->do('check_enqueue', 'lifo, check enqueue'); my @r = $q->dequeue(2); push @r, $q->dequeue; MCE->do('check_dequeue_lifo', @r); $q->clear; MCE->do('check_clear', 'lifo, check clear'); $q->enqueue('a', 'b', 'c', 'd'); $q->insert( 1, 'e', 'f'); $q->insert( 3, 'g'); $q->insert( -2, 'h'); $q->insert( 7, 'i'); $q->insert( 9, 'j'); $q->insert( 20, 'k'); $q->insert(-10, 'l'); $q->insert(-12, 'm'); $q->insert(-20, 'n'); MCE->do('check_insert', 'lifo, check insert', 'kjaibhcgefldmn'); MCE->do('check_pending', 'lifo, check pending', $q->pending()); MCE->do('check', 'lifo, check peek at head ', 'n', $q->peek( )); MCE->do('check', 'lifo, check peek at index 0', 'n', $q->peek( 0)); MCE->do('check', 'lifo, check peek at index 2', 'd', $q->peek( 2)); MCE->do('check', 'lifo, check peek at index 13', 'k', $q->peek( 13)); MCE->do('check', 'lifo, check peek at index 20', undef, $q->peek( 20)); MCE->do('check', 'lifo, check peek at index -2', 'j', $q->peek( -2)); MCE->do('check', 'lifo, check peek at index -13', 'm', $q->peek(-13)); MCE->do('check', 'lifo, check peek at index -14', 'n', $q->peek(-14)); MCE->do('check', 'lifo, check peek at index -15', undef, $q->peek(-15)); MCE->do('check', 'lifo, check peek at index -20', undef, $q->peek(-20)); return; }; MCE::Flow::finish; MCE-1.608/t/06_nodata_step.t0000755000076400007640000000047412511671246014417 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 1; use MCE::Step; MCE::Step::init { max_workers => 4 }; ## input_data is not required to run mce_step my @a = mce_step sub { MCE->gather(MCE->wid * 2); }; is( join('', sort @a), '2468', 'check gathered data' ); MCE::Step::finish; MCE-1.608/t/05_mce_map.t0000755000076400007640000000174612511671246013521 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 4; use MCE::Map; ## preparation my $in_file = MCE->tmp_dir . '/input.txt'; my $fh_data = \*DATA; open my $fh, '>', $in_file; binmode $fh; print {$fh} "1\n2\n3\n4\n5\n6\n7\n8\n9\n"; close $fh; my $answers = '6 12 18 24 30 36 42 48 54'; my @a; MCE::Map::init { max_workers => 2 }; sub _task { chomp; $_ * 2 * 3 } ## mce_map can take a code block, e.g: mce_map { code } ( 1..9 ) ## below, workers will persist between runs @a = mce_map \&_task, ( 1..9 ); is( join(' ', @a), $answers, 'block_ref: check results for array' ); @a = mce_map_f \&_task, $in_file; is( join(' ', @a), $answers, 'block_ref: check results for path' ); @a = mce_map_f \&_task, $fh_data; is( join(' ', @a), $answers, 'block_ref: check results for glob' ); @a = mce_map_s \&_task, 1, 9, 1; is( join(' ', @a), $answers, 'block_ref: check results for sequence' ); MCE::Map::finish; ## cleanup unlink $in_file; __DATA__ 1 2 3 4 5 6 7 8 9 MCE-1.608/t/03_chunk_size.t0000755000076400007640000000153712511671246014256 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 3; use MCE; my @ans; sub callback { my ($element) = @_; push @ans, $element; return; } my $mce = MCE->new( max_workers => 2, user_func => sub { my ($self, $chunk_ref, $chunk_id) = @_; for ( @{ $chunk_ref } ) { MCE->do('callback', $_); } return; } ); @ans = (); $mce->process([ 0 .. 3 ], { chunk_size => 1 }); is( join('', sort @ans), '0123', 'check that ans is correct for chunk_size of 1' ); @ans = (); $mce->process([ 0 .. 7 ], { chunk_size => 2 }); is( join('', sort @ans), '01234567', 'check that ans is correct for chunk_size of 2' ); @ans = (); $mce->process([ 0 .. 9 ], { chunk_size => 4 }); is( join('', sort @ans), '0123456789', 'check that ans is correct for chunk_size of 4' ); $mce->shutdown(); MCE-1.608/t/04_norm_que_local.t0000755000076400007640000000735512511671246015120 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 32; use MCE::Queue; ############################################################################### ## MCE::Queue supports 3 operating modes (local, manager, worker). ## This will test MCE::Queue (normal queue) in local mode. ## ## Local mode is selected when MCE is not present, during importing of ## MCE::Queue, or when initiating queues from inside the worker process. ## ## *{ 'MCE::Queue::clear' } = \&MCE::Queue::_clear; ## *{ 'MCE::Queue::enqueue' } = \&MCE::Queue::_enqueue; ## *{ 'MCE::Queue::dequeue' } = \&MCE::Queue::_dequeue; ## *{ 'MCE::Queue::insert' } = \&MCE::Queue::_insert; ## *{ 'MCE::Queue::pending' } = \&MCE::Queue::_pending; ## *{ 'MCE::Queue::peek' } = \&MCE::Queue::_peek; my (@a, $q, @r); ############################################################################### ## FIFO tests @a = (); $q = MCE::Queue->new( queue => \@a, type => $MCE::Queue::FIFO ); $q->enqueue('1', '2'); $q->enqueue('3'); $q->enqueue('4'); is( join('', @a), '1234', 'fifo, check enqueue' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '123', 'fifo, check dequeue' ); is( join('', @a), '4', 'fifo, check array' ); $q->clear; is( scalar(@a), 0, 'fifo, check clear' ); $q->enqueue('a', 'b', 'c', 'd'); $q->insert( 1, 'e', 'f'); $q->insert( 3, 'g'); $q->insert( -2, 'h'); $q->insert( 7, 'i'); $q->insert( 9, 'j'); $q->insert( 20, 'k'); $q->insert(-10, 'l'); $q->insert(-12, 'm'); $q->insert(-20, 'n'); is( join('', @a) , 'nmalefgbhcidjk', 'fifo, check insert' ); is( $q->pending(), 14, 'fifo, check pending' ); is( $q->peek( ), 'n', 'fifo, check peek at head' ); is( $q->peek( 0), 'n', 'fifo, check peek at index 0' ); is( $q->peek( 2), 'a', 'fifo, check peek at index 2' ); is( $q->peek( 13), 'k', 'fifo, check peek at index 13' ); is( $q->peek( 20), undef, 'fifo, check peek at index 20' ); is( $q->peek( -2), 'j', 'fifo, check peek at index -2' ); is( $q->peek(-13), 'm', 'fifo, check peek at index -13' ); is( $q->peek(-14), 'n', 'fifo, check peek at index -14' ); is( $q->peek(-15), undef, 'fifo, check peek at index -15' ); is( $q->peek(-20), undef, 'fifo, check peek at index -20' ); ############################################################################### ## LIFO tests @a = (); $q = MCE::Queue->new( queue => \@a, type => $MCE::Queue::LIFO ); $q->enqueue('1', '2'); $q->enqueue('3'); $q->enqueue('4'); ## Note (lifo) ## ## Enqueue appends to an array similarly to fifo ## Thus, the enqueue check is identical to fifo is( join('', @a), '1234', 'lifo, check enqueue' ); @r = $q->dequeue(2); push @r, $q->dequeue; is( join('', @r), '432', 'lifo, check dequeue' ); is( join('', @a), '1', 'lifo, check array' ); $q->clear; is( scalar(@a), 0, 'lifo, check clear' ); $q->enqueue('a', 'b', 'c', 'd'); $q->insert( 1, 'e', 'f'); $q->insert( 3, 'g'); $q->insert( -2, 'h'); $q->insert( 7, 'i'); $q->insert( 9, 'j'); $q->insert( 20, 'k'); $q->insert(-10, 'l'); $q->insert(-12, 'm'); $q->insert(-20, 'n'); is( join('', @a) , 'kjaibhcgefldmn', 'lifo, check insert' ); is( $q->pending(), 14, 'lifo, check pending' ); is( $q->peek( ), 'n', 'lifo, check peek at head' ); is( $q->peek( 0), 'n', 'lifo, check peek at index 0' ); is( $q->peek( 2), 'd', 'lifo, check peek at index 2' ); is( $q->peek( 13), 'k', 'lifo, check peek at index 13' ); is( $q->peek( 20), undef, 'lifo, check peek at index 20' ); is( $q->peek( -2), 'j', 'lifo, check peek at index -2' ); is( $q->peek(-13), 'm', 'lifo, check peek at index -13' ); is( $q->peek(-14), 'n', 'lifo, check peek at index -14' ); is( $q->peek(-15), undef, 'lifo, check peek at index -15' ); is( $q->peek(-20), undef, 'lifo, check peek at index -20' ); MCE-1.608/t/04_prio_que_worker.t0000755000076400007640000001562612511671246015335 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 40; use MCE::Flow max_workers => 1; use MCE::Queue; ############################################################################### ## MCE::Queue supports 3 operating modes (local, manager, worker). ## This will test MCE::Queue (priority queue) by the MCE worker process. ## ## *{ 'MCE::Queue::clear' } = \&MCE::Queue::_mce_w_clear; ## *{ 'MCE::Queue::enqueuep' } = \&MCE::Queue::_mce_w_enqueuep; ## *{ 'MCE::Queue::dequeue' } = \&MCE::Queue::_mce_w_dequeue; ## *{ 'MCE::Queue::pending' } = \&MCE::Queue::_mce_w_pending; ## *{ 'MCE::Queue::insertp' } = \&MCE::Queue::_mce_w_insertp; ## *{ 'MCE::Queue::peekp' } = \&MCE::Queue::_mce_w_peekp; ## *{ 'MCE::Queue::peekh' } = \&MCE::Queue::_mce_w_peekh; ## *{ 'MCE::Queue::heap' } = \&MCE::Queue::_mce_w_heap; my ($q); sub check_clear { my ($description) = @_; is( $q->_get_aref(5), undef, $description ); } sub check_enqueuep { my ($description) = @_; is( join('', @{ $q->_get_aref(5) }), '1234', $description ); } sub check_insertp { my ($description, $expected) = @_; is( join('', @{ $q->_get_aref(5) }), $expected, $description ); } sub check_pending { my ($description, $pending) = @_; is( $pending, 14, $description ); } sub check { my ($description, $expected, $value) = @_; is( $value, $expected, $description ); } ############################################################################### ## FIFO tests $q = MCE::Queue->new( type => $MCE::Queue::FIFO ); sub check_dequeue_fifo { my (@r) = @_; is( join('', @r), '123', 'fifo, check dequeue' ); is( join('', @{ $q->_get_aref(5) }), '4', 'fifo, check array' ); } mce_flow sub { my ($mce) = @_; $q->enqueuep(5, '1', '2'); $q->enqueuep(5, '3'); $q->enqueuep(5, '4'); MCE->do('check_enqueuep', 'fifo, check enqueuep'); my @r = $q->dequeue(2); push @r, $q->dequeue; MCE->do('check_dequeue_fifo', @r); $q->clear; MCE->do('check_clear', 'fifo, check clear'); $q->enqueuep(5, 'a', 'b', 'c', 'd'); $q->insertp(5, 1, 'e', 'f'); $q->insertp(5, 3, 'g'); $q->insertp(5, -2, 'h'); $q->insertp(5, 7, 'i'); $q->insertp(5, 9, 'j'); $q->insertp(5, 20, 'k'); $q->insertp(5, -10, 'l'); $q->insertp(5, -12, 'm'); $q->insertp(5, -20, 'n'); MCE->do('check_insertp', 'fifo, check insertp', 'nmalefgbhcidjk'); MCE->do('check_pending', 'fifo, check pending', $q->pending()); MCE->do('check', 'fifo, check peekp at head ', 'n', $q->peekp(5 )); MCE->do('check', 'fifo, check peekp at index 0', 'n', $q->peekp(5, 0)); MCE->do('check', 'fifo, check peekp at index 2', 'a', $q->peekp(5, 2)); MCE->do('check', 'fifo, check peekp at index 13', 'k', $q->peekp(5, 13)); MCE->do('check', 'fifo, check peekp at index 20', undef, $q->peekp(5, 20)); MCE->do('check', 'fifo, check peekp at index -2', 'j', $q->peekp(5, -2)); MCE->do('check', 'fifo, check peekp at index -13', 'm', $q->peekp(5, -13)); MCE->do('check', 'fifo, check peekp at index -14', 'n', $q->peekp(5, -14)); MCE->do('check', 'fifo, check peekp at index -15', undef, $q->peekp(5, -15)); MCE->do('check', 'fifo, check peekp at index -20', undef, $q->peekp(5, -20)); return; }; MCE::Flow::finish; ############################################################################### ## LIFO tests $q = MCE::Queue->new( type => $MCE::Queue::LIFO ); sub check_dequeue_lifo { my (@r) = @_; is( join('', @r), '432', 'lifo, check dequeue' ); is( join('', @{ $q->_get_aref(5) }), '1', 'lifo, check array' ); } mce_flow sub { my ($mce) = @_; $q->enqueuep(5, '1', '2'); $q->enqueuep(5, '3'); $q->enqueuep(5, '4'); MCE->do('check_enqueuep', 'lifo, check enqueuep'); my @r = $q->dequeue(2); push @r, $q->dequeue; MCE->do('check_dequeue_lifo', @r); $q->clear; MCE->do('check_clear', 'lifo, check clear'); $q->enqueuep(5, 'a', 'b', 'c', 'd'); $q->insertp(5, 1, 'e', 'f'); $q->insertp(5, 3, 'g'); $q->insertp(5, -2, 'h'); $q->insertp(5, 7, 'i'); $q->insertp(5, 9, 'j'); $q->insertp(5, 20, 'k'); $q->insertp(5, -10, 'l'); $q->insertp(5, -12, 'm'); $q->insertp(5, -20, 'n'); MCE->do('check_insertp', 'lifo, check insertp', 'kjaibhcgefldmn'); MCE->do('check_pending', 'lifo, check pending', $q->pending()); MCE->do('check', 'lifo, check peekp at head ', 'n', $q->peekp(5 )); MCE->do('check', 'lifo, check peekp at index 0', 'n', $q->peekp(5, 0)); MCE->do('check', 'lifo, check peekp at index 2', 'd', $q->peekp(5, 2)); MCE->do('check', 'lifo, check peekp at index 13', 'k', $q->peekp(5, 13)); MCE->do('check', 'lifo, check peekp at index 20', undef, $q->peekp(5, 20)); MCE->do('check', 'lifo, check peekp at index -2', 'j', $q->peekp(5, -2)); MCE->do('check', 'lifo, check peekp at index -13', 'm', $q->peekp(5, -13)); MCE->do('check', 'lifo, check peekp at index -14', 'n', $q->peekp(5, -14)); MCE->do('check', 'lifo, check peekp at index -15', undef, $q->peekp(5, -15)); MCE->do('check', 'lifo, check peekp at index -20', undef, $q->peekp(5, -20)); return; }; MCE::Flow::finish; ############################################################################### ## HIGHEST priority tests, mix-mode (normal and priority) $q = MCE::Queue->new( porder => $MCE::Queue::HIGHEST, type => $MCE::Queue::FIFO ); mce_flow sub { my ($mce) = @_; $q->enqueuep(5, 'a', 'b'); # priority queue $q->enqueuep(7, 'e', 'f'); # priority queue $q->enqueue ( 'i', 'j'); # normal queue $q->enqueuep(8, 'g', 'h'); # priority queue $q->enqueuep(6, 'c', 'd'); # priority queue my @h = $q->heap; MCE->do('check', 'highest, check heap', '8765', join('', @h)); MCE->do('check', 'highest, check peekh at index 0', '8', $q->peekh( 0)); MCE->do('check', 'highest, check peekh at index -2', '6', $q->peekh(-2)); my @r = $q->dequeue(10); MCE->do('check', 'highest, check dequeue', 'ghefcdabij', join('', @r)); return; }; MCE::Flow::finish; ############################################################################### ## LOWEST priority tests, mix-mode (normal and priority) $q = MCE::Queue->new( porder => $MCE::Queue::LOWEST, type => $MCE::Queue::FIFO ); mce_flow sub { my ($mce) = @_; $q->enqueuep(5, 'a', 'b'); # priority queue $q->enqueuep(7, 'e', 'f'); # priority queue $q->enqueue ( 'i', 'j'); # normal queue $q->enqueuep(8, 'g', 'h'); # priority queue $q->enqueuep(6, 'c', 'd'); # priority queue my @h = $q->heap; MCE->do('check', 'lowest, check heap', '5678', join('', @h)); MCE->do('check', 'lowest, check peekh at index 0', '5', $q->peekh( 0)); MCE->do('check', 'lowest, check peekh at index -2', '7', $q->peekh(-2)); my @r = $q->dequeue(10); MCE->do('check', 'lowest, check dequeue', 'abcdefghij', join('', @r)); return; }; MCE::Flow::finish; MCE-1.608/t/05_mce_loop.t0000755000076400007640000000306112511671246013705 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 4; use MCE::Loop; ## preparation my $in_file = MCE->tmp_dir . '/input.txt'; my $fh_data = \*DATA; open my $fh, '>', $in_file; binmode $fh; print {$fh} "1\n2\n3\n4\n5\n6\n7\n8\n9\n"; close $fh; ## output iterator to ensure output order sub output_iterator { my ($gather_ref) = @_; my %tmp; my $order_id = 1; @{ $gather_ref } = (); ## reset array return sub { my ($data_ref, $chunk_id) = @_; $tmp{ $chunk_id } = $data_ref; while (1) { last unless exists $tmp{$order_id}; push @{ $gather_ref }, @{ $tmp{$order_id} }; delete $tmp{$order_id++}; } return; }; } ## sub-task sub _task { my @ans; my ($mce, $chunk_ref, $chunk_id) = @_; push @ans, map { $_ * 2 * 3 } @{ $chunk_ref }; MCE->gather(\@ans, $chunk_id); # send to output_iterator } my $answers = '6 12 18 24 30 36 42 48 54'; my @a; MCE::Loop::init { max_workers => 2, gather => output_iterator(\@a) }; ## mce_loop can take a code block, e.g: mce_loop { code } ( 1..9 ) ## below, workers will persist between runs mce_loop \&_task, ( 1..9 ); is( join(' ', @a), $answers, 'check results for array' ); mce_loop_f \&_task, $in_file; is( join(' ', @a), $answers, 'check results for path' ); mce_loop_f \&_task, $fh_data; is( join(' ', @a), $answers, 'check results for glob' ); mce_loop_s \&_task, 1, 9, 1; is( join(' ', @a), $answers, 'check results for sequence' ); MCE::Loop::finish; ## cleanup unlink $in_file; __DATA__ 1 2 3 4 5 6 7 8 9 MCE-1.608/t/01_load_signal_export.t0000755000076400007640000000037012511671246015761 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 2; ## Always load MCE::Signal before MCE when wanting to export or pass options. BEGIN { use_ok('MCE::Signal', qw( $tmp_dir sys_cmd stop_and_exit )); use_ok('MCE'); } MCE-1.608/t/01_load_signal_arg.t0000755000076400007640000000226412511671246015215 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 3; ## Default is $MCE::Signal::tmp_dir which points to $ENV{TEMP} if defined. ## Otherwise, pass argument to module wanting /dev/shm versus /tmp for ## temporary files. MCE::Signal falls back to /tmp unless /dev/shm exists. ## ## One optional argument not tested here is -keep_tmp_dir which omits the ## removal of $tmp_dir on exit. A message is displayed by MCE::Signal stating ## the location of $tmp_dir when exiting. ## ## Always load MCE::Signal before MCE when wanting to export or pass options. our $tmp_dir; my $msg_eq = 'Check tmp_dir matches ^/dev/shm/'; my $msg_ne = 'Check tmp_dir does not match ^/dev/shm/'; BEGIN { use_ok('MCE::Signal', qw( $tmp_dir -use_dev_shm )); if (! exists $ENV{TEMP} && -d '/dev/shm' && -w '/dev/shm') { ok($tmp_dir =~ m{^/dev/shm/}x, $msg_eq); } elsif (exists $ENV{TEMP} && not (-d $ENV{TEMP} && -w $ENV{TEMP})) { if (-d '/dev/shm' && -w '/dev/shm') { ok($tmp_dir =~ m{^/dev/shm/}x, $msg_eq); } else { ok($tmp_dir !~ m{^/dev/shm/}x, $msg_ne); } } else { ok($tmp_dir !~ m{^/dev/shm/}x, $msg_ne); } use_ok('MCE'); } MCE-1.608/META.yml0000664000076400007640000000562712511673554012442 0ustar mariomario--- #YAML:1.0 name: MCE version: 1.608 abstract: Many-Core Engine for Perl providing parallel processing capabilities author: - Mario E. Roy license: perl distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 Test::More: 0.45 requires: bytes: 0 Carp: 0 constant: 0 Fcntl: 0 File::Path: 0 Getopt::Long: 0 IO::Handle: 0 perl: 5.008 Scalar::Util: 0 Socket: 0 Storable: 2.04 Symbol: 0 Time::HiRes: 0 resources: homepage: http://code.google.com/p/many-core-engine-perl/ license: http://dev.perl.org/licenses/ repository: http://code.google.com/p/many-core-engine-perl/ provides: MCE: file: lib/MCE.pm version: 1.608 MCE::Candy: file: lib/MCE/Candy.pm version: 1.608 MCE::Core::Input::Generator: file: lib/MCE/Core/Input/Generator.pm version: 1.608 MCE::Core::Input::Handle: file: lib/MCE/Core/Input/Handle.pm version: 1.608 MCE::Core::Input::Iterator: file: lib/MCE/Core/Input/Iterator.pm version: 1.608 MCE::Core::Input::Request: file: lib/MCE/Core/Input/Request.pm version: 1.608 MCE::Core::Input::Sequence: file: lib/MCE/Core/Input/Sequence.pm version: 1.608 MCE::Core::Manager: file: lib/MCE/Core/Manager.pm version: 1.608 MCE::Core::Validation: file: lib/MCE/Core/Validation.pm version: 1.608 MCE::Core::Worker: file: lib/MCE/Core/Worker.pm version: 1.608 MCE::Flow: file: lib/MCE/Flow.pm version: 1.608 MCE::Grep: file: lib/MCE/Grep.pm version: 1.608 MCE::Loop: file: lib/MCE/Loop.pm version: 1.608 MCE::Map: file: lib/MCE/Map.pm version: 1.608 MCE::Mutex: file: lib/MCE/Mutex.pm version: 1.608 MCE::Queue: file: lib/MCE/Queue.pm version: 1.608 MCE::Relay: file: lib/MCE/Relay.pm version: 1.608 MCE::Signal: file: lib/MCE/Signal.pm version: 1.608 MCE::Step: file: lib/MCE/Step.pm version: 1.608 MCE::Stream: file: lib/MCE/Stream.pm version: 1.608 MCE::Subs: file: lib/MCE/Subs.pm version: 1.608 MCE::Util: file: lib/MCE/Util.pm version: 1.608 no_index: directory: - t - inc - examples - images file: - bin/mce_grep generated_by: ExtUtils::MakeMaker version 6.55_02 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 MCE-1.608/bin/0000755000076400007640000000000012511673554011725 5ustar mariomarioMCE-1.608/bin/mce_grep0000755000076400007640000006530112511671246013435 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## A MCE-driven wrapper script for the following C binaries. ## ## agrep.exe grep.exe egrep.exe fgrep.exe tre-agrep.exe ## agrep grep egrep fgrep tre-agrep ## ## Caveat for grep, egrep, fgrep. MCE chunks input while running which may ## cause the following options to report inaccurately. It's possible that ## the requested NUM lines may cross chunking boundaries. Specify a larger ## chunk size value to minimize the effect or pass --chunk-level=list. ## ## -A NUM, --after-context=NUM ## -B NUM, --before-context=NUM ## -C NUM, --context=NUM ## ## Simply copy/rename this script or create a link to it. The prefix 'mce_' is ## stripped from the name for determining the actual binary to use. A trailing ## '.pl' extension is optional. Please ensure the binary is installed and in ## your path. ## ## ln mce_grep mce_agrep (or) ln -s mce_grep mce_agrep.pl ## ln mce_grep mce_tre-agrep (or) cp mce_grep mce_tre-agrep.pl ## ln mce_grep mce_egrep ## ln mce_grep mce_fgrep ## ## Which to choose (examples/egrep.pl or bin/mce_grep). ## ## Examples/egrep.pl is a pure Perl implementation with fewer options. ## Bin/mce_grep is a wrapper script for the relevant binary. ## ## The wrapper script is good for expensive pattern matching -- especially ## for agrep and tre-agrep. It also supports more options due to being ## passed to the binary. The wrapper supports 2 levels of chunking via the ## --chunk-level={auto|file|list} option. For large files, choose file. ## ## ============================================================================ ## 2014-01-21 v1.008 ## Created by Mario Roy. ## ## 2014-07-23 v1.009 ## ${^CHILD_ERROR_NATIVE} is not defined in Perl 5.8.x. Use $? instead. ## Compute chunk_level => 'auto' to use 'file' when reading STDIN. ## Set chunk_size to 8M when not specified (from 4M previously). ## ## 2014-12-22 v1.010 ## Small code refactoring. ## ############################################################################### use strict; use warnings; ## no critic (InputOutput::ProhibitBarewordFileHandles) ## no critic (InputOutput::ProhibitTwoArgOpen) use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my ($prog_name, $prog_dir); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $ENV{PATH} .= ($^O eq 'MSWin32' ? ';' : ':') . $prog_dir; } sub INIT { ## Provide file globbing support under Windows similar to Unix. @ARGV = <@ARGV> if ($^O eq 'MSWin32'); } use Getopt::Long qw( :config bundling pass_through no_ignore_case no_auto_abbrev ); use Scalar::Util qw( looks_like_number ); use Fcntl qw( O_RDONLY ); use MCE::Signal qw( -use_dev_shm ); use MCE 1.5; ############################################################################### ## ---------------------------------------------------------------------------- ## Usage and validation. ## ############################################################################### sub usage { print <<"::_USAGE_BLOCK_END_::"; Options for Script: --max-workers=NUM override max workers (default auto) e.g. auto, auto-2, 4 --chunk-level=LEVEL override chunk level (default auto) chunk at [file] or [list] level --chunk-size=NUM[KM] override chunk size (set at limit if under or over) [file] default: 8M minimum: 200K maximum: 20M [list] default: 12 minimum: 1 maximum: 60 --lang=LOCALE override locale e.g. C, en_US.UTF-8, en_US.ISO-8859-1 Options for Binary: ::_USAGE_BLOCK_END_:: return; } my $is_mswin32 = $^O eq 'MSWin32'; my ($cmd_name, $cmd_path); $cmd_name = $prog_name; $cmd_name =~ s{^mce_}{}; $cmd_name =~ s{\.pl$}{}; if ($is_mswin32) { $cmd_name .= '.exe'; for ( split ';', $ENV{'PATH'} ) { if (-x "$_\\$cmd_name") { $cmd_path = "$_\\$cmd_name"; last; } } } else { $cmd_name .= '.exe' if $^O eq 'cygwin'; for ( split ':', $ENV{'PATH'} ) { if (-x "$_/$cmd_name") { $cmd_path = "$_/$cmd_name"; last; } } } unless (defined $cmd_path) { print {*STDERR} "$prog_name: $cmd_name: command not found\n"; exit 2; } { my %valid_names = map { $_ => 1 } qw( agrep.exe grep.exe egrep.exe fgrep.exe tre-agrep.exe agrep grep egrep fgrep tre-agrep ); unless (exists $valid_names{$cmd_name}) { print {*STDERR} "$prog_name: $cmd_name: command not supported\n"; exit 2; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Process arguments. ## ############################################################################### my ($h_patn, $b_flag, $c_flag, $H_flag, $h_flag, $n_flag, $q_flag) = ((0) x 7); my (@r_patn, @args, $arg, @files, $file); my ($f_list, $r_flag) = (0, 0); my ($exit_status, $found_match, $skip_args, $w_filename) = (0, 0, 0, 0); my $max_workers = 'auto'; my $chunk_level = 'auto'; my $chunk_size; my $max_count = 0; my $no_msg = 0; my @TMP_ARGV; ## Option parsing step 1. for my $i (0 .. @ARGV - 1) { if ($ARGV[$i] eq '--') { @TMP_ARGV = @ARGV[$i .. @ARGV - 1]; @ARGV = @ARGV[0 .. $i - 1]; last; } } { local $SIG{__WARN__} = sub { }; GetOptions( 'max-workers|max_workers=s' => \$max_workers, 'chunk-level|chunk_level=s' => \$chunk_level, 'chunk-size|chunk_size=s' => \$chunk_size, 'lang=s' => sub { my ($self, $lang) = @_; delete @ENV{ qw( LC_MESSAGES LC_COLLATE LC_CTYPE LC_ALL ) }; $ENV{'LANG'} = $lang; }, 'help' => sub { usage(); system $cmd_path, '--help'; print "\n"; exit 0; }, 'V|version' => sub { system $cmd_path, '--version'; exit 0; }, 'q|quiet|silent' => \$q_flag, 'H|with-filename' => sub { $H_flag = 1; $h_flag = 0; }, 'h|no-filename' => sub { $H_flag = 0; $h_flag = 1; }, 'm|max-count=s' => \$max_count, 'R|r|recursive' => \$r_flag ); if ($max_workers !~ /^auto/) { unless (looks_like_number($max_workers) && $max_workers > 0) { print {*STDERR} "$prog_name: invalid max workers\n"; exit 2; } } if ($chunk_level !~ /^(?:auto|file|list)$/) { print {*STDERR} "$prog_name: invalid chunk level\n"; exit 2; } if (defined $chunk_size) { if ($chunk_size =~ /^(\d+)K/i) { $chunk_size = $1 * 1024; } elsif ($chunk_size =~ /^(\d+)M/i) { $chunk_size = $1 * 1024 * 1024; } if (!looks_like_number($chunk_size) || $chunk_size < 1) { print {*STDERR} "$prog_name: invalid chunk size\n"; exit 2; } } if ($max_count) { unless (looks_like_number($max_count) && $max_count >= 0) { print {*STDERR} "$prog_name: invalid max count\n"; exit 2; } } } ## Option parsing step 2. if (@TMP_ARGV) { @ARGV = (@ARGV, @TMP_ARGV); undef @TMP_ARGV; if ($ARGV[0] eq '--') { shift @ARGV; $skip_args = 1; push @args, '--'; } } while ( @ARGV ) { $arg = shift @ARGV; $arg =~ s/ /\\ /g; if ($skip_args) { push @files, $arg; } elsif (substr($arg, 0, 2) eq '--') { ## --OPTION if ($arg eq '--') { $skip_args = 1; push @args, $arg; next; } $h_patn = 1 if $arg =~ /^--regexp=/; $h_patn = 1 if $arg =~ /^--file=/; $b_flag = 1 if $arg eq '--byte-offset'; $c_flag = 1 if $arg eq '--count'; $f_list = 1 if $arg eq '--files-without-match'; $f_list = 1 if $arg eq '--files-with-matches'; $n_flag = 1 if $arg eq '--record-number'; $n_flag = 1 if $arg eq '--line-number'; $no_msg = 1 if $arg eq '--no-messages'; if ($arg =~ /^--directories=(.+)/) { if ($1 ne 'recurse') { push @args, $arg; } else { $r_flag = 1; } } elsif ($arg =~ /^--include=.+/) { push @r_patn, $arg; } elsif ($arg =~ /^--exclude=.+/) { push @r_patn, $arg; } elsif ($arg =~ /^--exclude-from=.+/) { push @r_patn, $arg; } elsif ($arg =~ /^--exclude-dir=.+/) { push @r_patn, $arg; } else { ## Pass arguments to the C binary push @args, $arg; } } elsif (substr($arg, 0, 1) eq '-') { ## -OPTION if ($arg eq '-') { push @files, $arg; next; } my $len = length $arg; for (my $x = 1; $x < $len; $x++) { my $a = substr($arg, $x, 1); $f_list = 1 if $a eq 'L' || $a eq 'l'; $h_patn = 1 if $a eq 'e' || $a eq 'f'; $b_flag = 1 if $a eq 'b'; $c_flag = 1 if $a eq 'c'; $n_flag = 1 if $a eq 'n'; $no_msg = 1 if ($a eq 's' && $cmd_name !~ /agrep/); } next if $arg eq '-'; ## Pass arguments to the C binary if ($cmd_name =~ /agrep/) { push @args, $arg; if (substr($arg, -1) =~ /[efDISEd]/) { $arg = shift @ARGV; $arg =~ s/ /\\ /g; push @args, $arg; } } else { my $a = substr($arg, -1); push @args, $arg if ($arg ne '-d'); if ($a =~ /[efABCD]/) { $arg = shift @ARGV; $arg =~ s/ /\\ /g; push @args, $arg; } elsif ($a eq 'd') { $arg = shift @ARGV; if ($arg ne 'recurse') { push @args, '-d', $arg; } else { $r_flag = 1; } } } } else { ## FILE push @files, $arg; } } ## Option parsing step 3. push @args, shift @files if ($h_patn == 0 && @files > 0); if ((!$h_flag && @files > 1) || (!$h_flag && $r_flag) || $H_flag) { $w_filename = 1; } if (@args == 0) { system $cmd_path; exit 2; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE callback functions: Error, File, and Count. ## ############################################################################### my ($_order_id, %_tmp, %_nrecs, %_nsize, $_start_nrecs, $_start_nsize); my ($_abort_all, $_abort_job, $_total_found); sub _error { my ($msg) = @_; print {*STDERR} $msg; $exit_status = 2; return; } sub _abort_job { if (!$_abort_job) { MCE->abort; $_abort_job = $_total_found = $found_match = 1; $_abort_all = 1 if $q_flag; } return; } sub _output_cnt { my ($chunk_id, $out_file, @_rest) = @_; my $cnt; if (-s $out_file) { $found_match = 1; open my $fh, '<', $out_file; chomp($cnt = <$fh>); close $fh; $_total_found += $cnt; if ($q_flag && !$_abort_all) { MCE->abort; $_abort_all = $_abort_job = 1; } } unlink $out_file; return; } sub _set_found_match { $found_match = 1; return; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE callback function: Output without line-number or byte-offset ## ############################################################################### sub _output_n0 { my ($chunk_id, $out_file, @_rest) = @_; $_tmp{ $chunk_id } = $out_file; return unless exists $_tmp{ $_order_id }; do { my $out_file = $_tmp{ $_order_id }; if (!$_abort_job && -s $out_file) { my ($fh, $buffer); $found_match = 1; if ($q_flag) { unless ($_abort_all) { MCE->abort; $_abort_all = $_abort_job = 1; } } else { if ($w_filename) { open $fh, '<', $out_file; while (<$fh>) { print $file . ':' . $_; if ($max_count && ++$_total_found == $max_count) { MCE->abort; $_abort_job = 1; last; } } close $fh; } else { if ($max_count) { open $fh, '<', $out_file; while (<$fh>) { print $_; if ($max_count && ++$_total_found == $max_count) { MCE->abort; $_abort_job = 1; last; } } close $fh; } else { sysopen $fh, $out_file, O_RDONLY; sysread $fh, $buffer, -s $fh; close $fh; print $buffer; } } } } delete $_tmp{ $_order_id }; unlink $out_file; } while (exists $_tmp{ ++$_order_id }); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE callback function: Output with line-number or byte-offset ## ############################################################################### sub _output_n1 { my ($chunk_id, $out_file, $n_records, $size) = @_; $_tmp{ $chunk_id } = $out_file; $_nsize{ $chunk_id } = $n_flag ? $n_records : $size; return unless exists $_tmp{ $_order_id }; do { my $out_file = $_tmp{ $_order_id }; if ($_order_id > 1) { $_start_nsize += $_nsize{ $_order_id - 1 }; delete $_nsize{ $_order_id - 1 }; } if (!$_abort_job && -s $out_file) { my ($p1, $size); $found_match = 1; if ($q_flag) { unless ($_abort_all) { MCE->abort; $_abort_all = $_abort_job = 1; } } else { open my $fh, '<', $out_file; if ($w_filename) { while (<$fh>) { $p1 = index($_, ':'); $size = $_start_nsize + substr($_, 0, $p1); print $file . ':' . $size . substr($_, $p1); if ($max_count && ++$_total_found == $max_count) { MCE->abort; $_abort_job = 1; last; } } } else { while (<$fh>) { $p1 = index($_, ':'); $size = $_start_nsize + substr($_, 0, $p1); print $size . substr($_, $p1); if ($max_count && ++$_total_found == $max_count) { MCE->abort; $_abort_job = 1; last; } } } close $fh; } } delete $_tmp{ $_order_id }; unlink $out_file; } while (exists $_tmp{ ++$_order_id }); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE callback function: Output with line-number and byte-offset ## ############################################################################### sub _output_n2 { my ($chunk_id, $out_file, $n_records, $size) = @_; $_tmp{ $chunk_id } = $out_file; $_nrecs{ $chunk_id } = $n_records; $_nsize{ $chunk_id } = $size; return unless exists $_tmp{ $_order_id }; do { my $out_file = $_tmp{ $_order_id }; if ($_order_id > 1) { $_start_nrecs += $_nrecs{ $_order_id - 1 }; delete $_nrecs{ $_order_id - 1 }; $_start_nsize += $_nsize{ $_order_id - 1 }; delete $_nsize{ $_order_id - 1 }; } if (!$_abort_job && -s $out_file) { my ($p1, $p2, $recs, $size); $found_match = 1; if ($q_flag) { unless ($_abort_all) { MCE->abort; $_abort_all = $_abort_job = 1; } } else { open my $fh, '<', $out_file; if ($w_filename) { while (<$fh>) { $p1 = index($_, ':'); $recs = $_start_nrecs + substr($_, 0, $p1++); $p2 = index($_, ':', $p1); $size = $_start_nsize + substr($_, $p1, $p2 - $p1); print $file . ':' . $recs . ':' . $size . substr($_, $p2); if ($max_count && ++$_total_found == $max_count) { MCE->abort; $_abort_job = 1; last; } } } else { while (<$fh>) { $p1 = index($_, ':'); $recs = $_start_nrecs + substr($_, 0, $p1++); $p2 = index($_, ':', $p1); $size = $_start_nsize + substr($_, $p1, $p2 - $p1); print $recs . ':' . $size . substr($_, $p2); if ($max_count && ++$_total_found == $max_count) { MCE->abort; $_abort_job = 1; last; } } } close $fh; } } delete $_tmp{ $_order_id }; unlink $out_file; } while (exists $_tmp{ ++$_order_id }); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE user functions: run-mode = file. ## ############################################################################### sub user_begin_file { $0 = $^X; return; } sub make_user_func_file { my $first_time = 1; return sub { my ($self, $chunk_ref, $chunk_id) = @_; my ($out_fh, $err_fh, $cmd_fh, $has_err); my $n_records = 0; my $out_file = MCE->sess_dir .'/'. $chunk_id; if ($n_flag) { $n_records++ while ($$chunk_ref =~ m!\n!mg); } if ($is_mswin32) { $out_file =~ s{/}{\\\\}g; open my $in_fh, '+>', $out_file . '.in'; binmode $in_fh, ':raw'; print {$in_fh} $$chunk_ref; close $in_fh; my $err_file = $first_time ? "2> $out_file.err" : ''; system("$cmd_path < $out_file.in @args > $out_file $err_file"); unlink "$out_file.in"; } else { ## I borrowed some bits from IPC::Run3 for STDOUT/ERR. However, I ## settled on passing STDIN via open for lesser overhead behind the ## scene versus calling system (from observation during testing). local (*STDOUT_SAVE, *STDERR_SAVE); open STDOUT_SAVE, '>&STDOUT'; open $out_fh, '+>', $out_file; binmode $out_fh, ':raw'; open STDOUT, '>&' . fileno $out_fh; if ($first_time) { open STDERR_SAVE, '>&STDERR'; open $err_fh, '+>', "$out_file.err"; binmode $err_fh, ':raw'; open STDERR, '>&' . fileno $err_fh; } ## Seeing "maximal count of pending signals (NUM) exceeded" message. ## Thus the reason for using syswrite instead of print below. open $cmd_fh, '|-', $cmd_path, @args; ## Run external command syswrite $cmd_fh, $$chunk_ref; ## Write to STDIN close $cmd_fh; open STDOUT, '>&STDOUT_SAVE'; close $out_fh; if ($first_time) { open STDERR, '>&STDERR_SAVE'; close $err_fh; } } MCE->abort if ($q_flag && -s $out_file); ## Send error. if ($first_time) { my $err_file = "$out_file.err"; if (-s $err_file) { $has_err = 1; MCE->abort; if ($chunk_id == 1) { open $err_fh, '<', $err_file; local $/ = undef; MCE->do('_error', <$err_fh>); close $err_fh; } } unlink $err_file; $first_time = 0; } ## Gather output. if ($f_list) { MCE->do('_abort_job') if (!$has_err && -s $out_file); unlink $out_file; } else { MCE->gather($chunk_id, $out_file, $n_records, length $$chunk_ref) unless $has_err; } return; }; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE user functions: run-mode = list. ## ############################################################################### sub user_begin_list { $0 = $^X; use vars qw( $child_found_match ); our $child_found_match = 0; return; } sub user_end_list { MCE->do('_set_found_match') if $child_found_match; return; } sub user_func_list { my ($self, $chunk_ref, $chunk_id) = @_; my ($output, $err_fh, $status); my $err_file = MCE->sess_dir .'/'. $chunk_id . '.err'; $$chunk_ref =~ s/\n/ /mg; local $?; if ($is_mswin32) { $err_file =~ s{/}{\\\\}g; $output = `$cmd_path @args $$chunk_ref 2> $err_file`; $status = $? >> 8; } else { local *STDERR_SAVE; open STDERR_SAVE, '>&STDERR'; open $err_fh, '+>', $err_file; binmode $err_fh, ':raw'; open STDERR, '>&' . fileno $err_fh; $output = `$cmd_path @args $$chunk_ref`; $status = $? >> 8; open STDERR, '>&STDERR_SAVE'; close $err_fh; } MCE->abort if ($q_flag && length $output); ## Send error. if (-s $err_file) { open $err_fh, '<', $err_file; local $/ = undef; MCE->do('_error', <$err_fh>); close $err_fh; } unlink $err_file; ## Gather output. if ($q_flag) { MCE->do('_abort_job') if ($status == 0); } else { if (length $output) { MCE->print($output); $child_found_match = 1; } } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Process routines: run-mode = file. ## ############################################################################### sub process_file { ($file) = @_; if ($file eq '-') { open(STDIN, '<', ($is_mswin32) ? 'CON' : '/dev/tty') or die $!; process_stdin(); } elsif (! -e $file) { $exit_status = 2; print {*STDERR} "$prog_name: $file: No such file or directory\n" unless $no_msg; } elsif (-d $file) { $exit_status = 1; } else { $_abort_job = $_start_nrecs = $_start_nsize = $_total_found = 0; $_order_id = 1; MCE->process($file); %_nrecs = (); %_nsize = (); if (!$q_flag && $f_list) { print "$file\n" if $_total_found; } elsif (!$q_flag && $c_flag) { $_total_found = $max_count if ($max_count && $_total_found > $max_count); print "$file:" if $w_filename; print "$_total_found\n"; } } return; } sub process_stdin { $file = '(standard input)'; $_abort_job = $_start_nrecs = $_start_nsize = $_total_found = 0; $_order_id = 1; MCE->process(\*STDIN); %_nrecs = (); %_nsize = (); if (!$q_flag && $f_list) { print "$file\n" if $_total_found; } elsif (!$q_flag && $c_flag) { $_total_found = $max_count if ($max_count && $_total_found > $max_count); print "$file:" if $w_filename; print "$_total_found\n"; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Configure Many-Core Engine. ## ############################################################################### my $gather_func; if ($chunk_level eq 'auto') { if (@files == 0 || $files[0] eq '-') { $chunk_level = 'file'; } else { if (-f $files[0]) { $chunk_level = (-s $files[0] > 20_971_520) ? 'file' : 'list'; ## 20M } else { $chunk_level = 'list'; } } } if ($chunk_level eq 'list') { $chunk_size = 12 unless defined $chunk_size; $chunk_size = 60 if $chunk_size > 60; $chunk_size = 1 if $chunk_size < 1; unshift @args, '-H' if (!$h_flag && ($H_flag || $r_flag || @files > 1)); unshift @args, '-h' if ($h_flag); unshift @args, '-q' if ($q_flag); MCE->new( max_workers => $max_workers, chunk_size => $chunk_size, use_slurpio => 1, user_begin => \&user_begin_list, user_func => \&user_func_list, user_end => \&user_end_list ); } else { $chunk_size = 8_388_608 unless defined $chunk_size; ## 8M $chunk_size = 20_971_520 if $chunk_size > 20_971_520; ## 20M $chunk_size = 204_800 if $chunk_size < 204_800; ## 200K if ($f_list) { $gather_func = undef; } elsif ($c_flag) { $gather_func = \&_output_cnt; } elsif ($n_flag && $b_flag) { $gather_func = \&_output_n2; } elsif ($n_flag || $b_flag) { $gather_func = \&_output_n1; } else { $gather_func = \&_output_n0; } MCE->new( max_workers => $max_workers, chunk_size => $chunk_size, use_slurpio => 1, user_begin => \&user_begin_file, user_func => make_user_func_file(), gather => $gather_func ); } ############################################################################### ## ---------------------------------------------------------------------------- ## Run. ## ############################################################################### if ($r_flag && @files > 0) { my ($list_fh, $list); MCE->spawn; if ($is_mswin32) { $list = `egrep -lsr @r_patn ^ @files`; open $list_fh, '<', \$list; } else { open $list_fh, '-|', 'egrep', '-lsr', @r_patn, '^', @files; } if ($chunk_level eq 'list') { MCE->process($list_fh); } else { while (<$list_fh>) { chomp; process_file($_); last if $_abort_all; } } close $list_fh; } elsif (@files > 0) { if ($chunk_level eq 'list') { my $list = join("\n", @files) . "\n"; undef @files; open my $list_fh, '<', \$list; MCE->process($list_fh); close $list_fh; } else { foreach (@files) { process_file($_); last if $_abort_all; } } } else { if ($chunk_level eq 'list') { my $status = system($cmd_path, @args); exit($status >> 8); } else { process_stdin(); } } ############################################################################### ## ---------------------------------------------------------------------------- ## Finish. ## ############################################################################### MCE->shutdown; if (!$q_flag && $exit_status) { exit($exit_status); } else { exit($found_match ? 0 : ($exit_status ? $exit_status : 1)); } MCE-1.608/images/0000755000076400007640000000000012511673554012422 5ustar mariomarioMCE-1.608/images/05_Power_of_Randomness.gif0000644000076400007640000051471012511671246017372 0ustar mariomarioGIF89a"+=K\r,,7,M,n,=?@eCxLXj+^,,,=,>=,Fa,L,o,.++2,7>>><>BaBxD[lDmILLMNQT-TE+VW#X[)]z]^mv_`[Qab3&fh6h>0hJ;hioqsuttM@uwO+w~xi]z1|L|XM|}~_9ׂhPŃdZ{ۇvpoa;ޒLypȚm=tMNc+zٟfǢ^ȬZp+y@h׻ü=JœmÎUàý»Ĵĺ˻ö̑J̛^̵͔XФiœĥԼɦʴְͩЫĝūں̪ҭš̳ӺɤձݫiŐرռ̥ɕͩձڵĕҤۺѬ۫Τզֱڴԫڪݸ̓їݺ֫ܭܲ!!ICCRGBG1012applmntrRGB XYZ * acspAPPL-appldescPbdscmBcprtwtptrXYZgXYZbXYZrTRC aarg $ vcgt DndinX>chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2013XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"H*\ȰÇ#JHŋ3jȱǏ CII˨\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴiρexqCիXju+`ÂBVٳhӪ]˶۷pʝKݻx˷߿ dM6"1ƘK6 :Ѭ OD1DO6zj6ʫj*4x 6"{"W'srvɜ;y]?qE2^O7~ }ui XgyFJe?~Z`=0hhNlx].kb*X8.#+6\^I2:)D/ywf,/p? n[mvdΙ(r>ۗrr0? ;!э q:Ƞ8kTziЃM4jZ.Ί*:*=㚸8㆟ <`:ĢJ,n}a0T8Kq3whds_GZ#-(`T6nЫQS8$&&C_x4Er7acȢrbnŸ fJ*!1Il*Z(U)&QBas(Ĕ2%-xߒ`~@mAICs4qjm2U Z h[?';aэFTJƯPtQ#K 1AIhT>ů=6dK:2Ro;3I^τiYjHt7^hJ*ǜjWыѢ#28q/}+2{?(oPq,hS*=VPa*͹AO`⠌k1a*ʢC! QHHH8m9l]Ĝ3p ~n?CqNnNGY#qֳ,Ջ``61q\3XԼve ہKJLGj7Am~c-YwJ?YUUG˜'Jᄨ:1 =C*sHчB}N`7-S:HxXia ڴb(0TiOS:% \۩ek$-E~(y)>5NPߔ'UIU4Z!j28 BHU?乏}5☌hDJl&&?^\|_w,<)mdM^ڻxs< $*m͌*ۏ !]sNßJj];Jig=7Fad7Nm WgcDTU0G܄jCHJE?tDnv#p4F1P^Mjl_v,vTowʪò` n&<" pJ^gUnOP1ZI.S7.א¡J_B~hmd`|`XGfg;Ruҵ:'" i윆ih?֌p-)_R}-hde{ʋfS۽0|ΡsE+_PϬ*x5h*2pӁtȂ"u$z F̢˒4юi8C.gsvA:OԞk֪i$p*QB A~$I~ ]WbUZWn-cq[a&ݺd{= 7 AK[Q׽ wy}޿'oDsĦf#^jw ;?aavahS!ʻ- $hN0 Sߨ3m>Sp2bCY!KэB0?E)ZvT70 Ӑ  @fqepT^m@w|{DM']3bnsWRu0̗wU<{$tB!G2 r`ua/)sHb>c `+d&BW66x5dYMx,c>#V7s'H(9oo\C<R{t# 0{ Gdm H6R  UwWt>zuRZ"2N,'CwC)Ch8ˆp B*+E*1WE`tE# 0uTX cnR<-dd 5 SDFkJdB.54TJc B/T#_uIX%5|TBБy7{IE|K4H-g41BIy18bS_3TPRX&%ۗ)* O9x+0}* e V$ @` s ؐ P) 0 uP Y$6S|qWJYc ;L.O– j ćuk>a3Iyu(0N8VCg=iI=VK(}w+0 V>I!1B1Q/ll{ ua^zdQ ep6cO'O_qk<.'"?t_tVIw}.'CPC8CuDE$DUD[zD*)CY qE "uS' 0 p @ƀXvpf A)Bu¡הAt(_n10Nd7B9(f̗!wtW;:CʎE:UUJ2Nьa$r` Wus\AAjA:bbᛖu!sjS-#w%7p&lRhB^}PEPe ! E9#tPd v%hs  w0 ^dph 6zUSw0@{?3{2jtTX"#{`"3K<؂aΊ#Z:[3 |AGw;zNHxpc#&l(3VxIuy7'ڒ3NZJK%lXpwHfT'Wzb蘂3ى sOUg3wu8DBt0y, Y J j u"/p'#y4>"M@k:e>s-FLVRT൅ZM='B{N;r$f1HW0e~! لN1{81.ecgSS<${R[RB'r04сر zk.t1`tz=# Zh yD Bss8Z9Ų++[ :A~P j JPJ< Hv{W63^Sw/WHܰkZVr"2RJr B(2+?

Ĉ*{[  @ AqG%6<-$y5,LeAM5Sw Aa0{o R#R.*ӿz/Pm #k==i,WE64ƔN1AU6S|7{!YRTRKbw=kL `|݈NEP JΨ C){D߹ ْM ! - ǡ"!K4!4\)e߯ek $X6x(zmR3-m0p*M68ۯʯ\LCaj76# 7(bQƘ:4:'S-0d=+gՋGIM:&5M=϶}pCX0)ls4 UD f r р|V hm ɐ ߋaRs-:B̨ܫԏ=lF#tm&W)qr )G#\{ӽJv0D%p$kw }«E*G y\GHh[=e:7cR*1V)!&sQr5Zk̄Ddft#a8Nܠo$3Cus}P>GdQv%c K4x:Զ^aiVmgRoZ1t(-o\8'ڲX[ o ێ<1hN'/9˨o6*!iGrWm2\xa SW:p7jezK,|r{\]BӁ۹*ؼk׸1.{_|}\4f:xn':DiD)' lC8k)eiVnw F2JՎTldĘąݻR_̑PzWwBw #b/.>D(L*)Hu<A cu:ztaTסyr{( 9^q^8 ?ᗀ o?\؄%X!F)A#7\VIo¸;^{$t-^?4KL- O@3h)8 `i 1r@7FvTF+J(m{:x®^m29{נ&6(@2Q0n\+b!3>abл%](&;}x i"y8$D"ỵ'BAHP:bg7I;IˆSQz2r~i+Pؐ%f @Z,E* J]JRk٩ܰ|f4j^37fBNn u.G^vC%x %IZS#1(CSPhǾz衣] iy,"5]d_Shd*#$!& " !0]`SSFnӡۈPpzem9c܅be[*v"1?F!"9F9"3V "jVS&T@cHmڑ _Hl©EWn{Q^Y$)#+SxebfD"&%oB-],jW2!VA]HE3q9mM[!\t-on D/)TX̒$s.%c+#p)[+8{+vd&ou`:oB@ק%/ao|#<ȩMqj@Z]*p R`XZ{gPRĔtNE ,:ʶD<,JX H%I{oŻv2:J©]wY+'DCvB]9?pg!vU%Q#DgIR 5T&)v$ _MG̑utQYT%(>\ ( @]CRa1LC#FWU5SU:U+&1 &JW. 7[fY'B8GB(#mͧ`eb,㮛H:5`ͧϝdr-<+iͻ>kׁnqD RU Ar^ 혳Y1Z_!x%W䢫'NmE9P2 lM1, lC>@;5Ѐ=| _C81N mHII uuיufИEِwS }5ɲy@i:@k C2rR:w2S7pKi ɏ :;-;?üʳ|,8҈<+*okKd/ 5k3' cA<˿IV99“G`X l ;hJ9 h>9VK8FFh&p,qz뱰 3 T2#P*y*⩊1źeC;22DK[7*ŇE`3 q':)?SP6ě~ JT yď"˼$#2kEY<@$ čIpMA\$LFwcSrB#/7pC94%*X0IoH>d/LJRTI "50vZ;1 s\릿C+YTwzr̲-Q12TB@A a$"1*nÉɹ =Sv+.t0et)39)!DFWŃ8(r3F\BxJ*HL%sDV/1>%D٪IFƨzhʴc҇cRV e>_pS:[ )C:|h-,49ɩ,p"MQ2QƈZp+hP__ch̤"Ft&ɺvE|Qw8MT)c<";J%i@< l#(HId74 O#"IP"I$G/1ɐ6{lp ˈ3D2`ElE1=ǚ14=ѐEhUi>t:MKLX>r _hEI`IP  Iʵ&ԳYҡP͵l Ιil7J<d| 52b<#PlU 3"%1m|C G/6am:SMt1^$`l$4TŎ'0K 2DX=9LchR_0S!iLȔW0QS0WLHU 5\;_].-aM0*~;+;@}J1 $E 9ITѧ(W1ѱD4oDSIv9#hlUҌ5;d_Oc%X7{GˬX4 P`S5vHX+Q_0E\@,pU!W%:EB5Ը;dMA'T(ڕV2e)!\WS.xBT)m S̸s p 3ߍ3ӷ\7'iNtjTB" X.dq֜t`[Yu^3PIG1 +р刹1,CLCP}ȆZrQZpWȅ\b̬RD =Jᄿ}5<$jت[Wٳ$w2[[)R b1 F8ޤjR8Wã6-y7m_TΙL Y\``ݘ"9'`..Qws#>$naz=>4 mITz(l +Ơ>h{ V&&]4ˇv|>ReP=e[v]FU qpcc.*"26!#sM[0)NsMº!@fg<$;sY`bϩs1oUdq=RJ=c<&A ?i=H3Y?qN99ľZ8Q bWH0dev‰`hkF52j.S~Ƨn7I^Ȥ[8#W38__|NW+3IT|{MP3s3\0Dn4K DqGM C4.&t91hc`ZaTL%x 9v&TuebT0QWXo0d!D0a 4h`1VjG^-~Kpb.hܢK ccAMtUԺg_2vvK5εTw@:f}iMjQ焐]fXM3IuSeB띦FG۟ʈJݟtІI0l]L3qLQiPnEvTp_h!, Z0;p׺rĀ 9 [ŒP.JKɆQU ]WːTEUlTm8W%b Z7 ;Og'[|= r=o,wѐ0}Ng>jugiw 8_ sFd.o%evif~D!rJsIv?T+Crbػ\y{xy7PFTBC xS8X7YM#:l{Y *woH0VP`ꆞQn17~j.~❷vzZƙwnƛ̏ﶏ{s73wr}({3"uh`2l Ĉ&gx5 #Ȑ>G2Hn?GaĘ5iaL~9PB Ԛ;@Aɠ8jR sIfC/GkSt͛2n@2 cuv,ڱlŊ>z)&xc}Ɔ 1_fn4iU.T~/2U ҤR5}9sB$zuRZC9gN1$:nҧkx>60OY3pdE}~7`Ps D~ZɧJgg| bxq}'t'x֑Giw=%ވLM04W7QTI7VEUT@?(7UTVY Gn"1vɲ.iFs?ve96'dsee֎4ɐN2x6K-/%K;J)@?A }iMQpM*I"5Q4EN Cj˽jB7tQpՠJH%87^RJH.S"8ԩMPᚫOVdbFr]8^ny浻ySY%QIٷ)}߶>:wXYf%7^$DcL-ƈK]dK6 rd) 4Sdd#<"S/O;344|/2rt;v+ $0B 1?!8*WMAS^$V7GG/C $8{NeVz)~.K;ޜANضr~q/YI˺޴Þ-:r}zNuUD'쯃e%ݹk1cb.Y9踬brep ':"͠HM;M.hֿ=~\/4Ҙ$!Y87&qp7G?[&4ڦ+C X[Calyp⍶\F` m#&")KIR|}"""%xN(S1n\x[!X},^"hi{b~DCGCdid,HcʇgŚ)5V%P GbLV #oPDr#aCDzX, =Ŕ;䇶(h>kC!9!6,4CrK!# y[R&%K.\%.mJV#Fz0='Bީcy. yl<9t1pG6| Adhq `#p)Vc T&k+dAņ$PM8BA؀$݈JzyxJ] CSO*Qm+/oq?]h t^hވq}܂fP>TrLb(5<"ݹȲ҃(׵ Y V!TBRUжqI),b;Vd&FɢAlLp4h0f1Y6hB=D5r_M1/F! LLB;M<J8-WPeϹ2%,9֒sNZXhi>a3b3bE([y E9h5B{:4"..s,Q6<08Enip.#~3:H:,&2 WC%il$A(M<>Z0W1[PĆ)~|o<)Bm{GִGBeᘍ xIsZU"r&Ѕ.́YqP4G&S5&¾}i35bWBHmTS V"hhxJ34{9f4W̕Xu(`xꃉ LG` =th1ddhڭ+@Wt9Q{HM\Mvgk$Jݥ#-x9ctATcuJ_Xm !£? #<]챂c !8+u8<Ɩ.SK6f_]MBd>9?D=0d{YrYuUZίt$I5Sd(SN ([1B0%ڰBWʵ_D?xD\H@iXYV4ݓE@Im E}\G,bsI4ԂJ}CDJ%Mʏk(iȏ_gaB2\504&(PSC-_u_ni`aSƎgm ,ń dX{$a=8&S4]6]2̡tA`HiH8q4^Whi LP"W` {"K -CVUU"@hQ 39Vi0=Y0aPQ+ Edb>9 =pCTVA$-t"E'yb()U?"̑J`,r-9`e 6)ѳ]Myю0ئ L%bݜUĔytۼVQټGcd9eLuThЏvaN/ПJh&C1\:^)$vCQɛDC̭! #tA_jm#ǁh֌LDP? B@'с'j&P @QRձHNKZ!1'y+!&B(Q0%Y&*-ـ"Wpp%(*hpX6*\X.>T\  bEb]U5Jĥ1e^cZb+r̢D4#äYHa5>6,mz!T,JEC/\($WL4FMU%*LOm/L7`,E>32_*H\jGAfi6,&D"QLTg*Jl# "Ў9THv`j 02DG ; 06lv*ŚQt&Hp%Y f\0j +5 '+s`qÈH&NTUlE΢`9?p`D$f-2Xx#ΫB h% ^s=ƓR.XgÚB2ٕ\kXjik+$4\C1~;\d*|B+j-fG88Yi՗3=4D:h&Pe t_ITNn)EDLkN0Irp&RpC0g d-5P04BŁsDmRG.D:Э-Ner6?CeP@H.lɯOF] d=D?,mr ze妌D8nsVV?$s&@C/&uEvYvNQe,x1R^6B'LBs,5: |/ "O\IWpd$423"~qCP'/\ȇ[B :,v'PE +|2q*8LmC+B,$h$KNBn*rh֋.~M ]6`SqG@xIIB/,s<.L4dv ƙz!6OuR+LJOi$&C*,hi6/LQ0%S%$u楐E5(us0 L B(2:ȃl020<Q[<076Y[t$*vT5\S֭."7PLSqvhB]<J,8X D~Pu'0 gP;, %56J΁ u%An8Be3w+5g ON[j((b)Mm:{A3 483;84 1B*iih;frRָ=x'%, ,,lFE)Eb(F1I\f q :0m PB03EnBv0-| Hbwhg$5ܟNPBwT/āJ #D!m+YhCڀ&*'v $e?A!o6l,r5&n0ٌx 25Y86$βͶX 35BЂGlg_HQ yrk'7 5oM Lg~cISp$mv5YPL:\jx{4<4:L |iF\mJ7|@H,t={{2(x2v;(D9BN&䯥p@{H ãX-vjN-kCoBp91008|:z̡AkHD?90C&\/VZ-p `݂qr0|l\%vyոc>|9| ۟ x/B?AP!4rŸ$@CNAMA2+`CC+d84|B/l*&' ;L/4] ,YBYw[:\'t<9&BxBd˜'`2>}z @5 qh%qrŠcQ CG݊A.V0h0Zq㦮ޜmLlB5sc(:&ׯ9Ə9pq^iÇ"&9&ZazpgMǟ"g<]cMExQƎd"L63J#s90N 5|qbx*;qm1ٯ5ѣE Jիsϵ9s()+vhѦǒH4É#/ex"Mڴi=Ʈ4_%];l}-<_ֻr+{~Uys%5]fe o]%l|R̉g 1Ȁ.M`|2 !J&z 4@ !l) 1 .,"Â)Ƞ8jGI>8 >((Z)n8ZVled^i5Z >4F,rǚ;#.cz t D0 j&"JTkV4=d3kh#MV5ШHyhn 2J)mL-O,B#bTlA5>u*^a(uՊk)5@{l 'v!Υ4lt:px*S*\ÍJ\ph0OJcG9kgڡ:IiZ\Q=T~_=_v6;ڗYdz%d`pƗgRR;x "GCID?g$cO c? R4̂¡W41!͢hGғ6QA6dC J֦rM*߻8l x꾻Ե4 5=1$^O^A/z닛tD0d.^XVdžNjиFƼ.(T ZԂo=odQUZm2]jfE Agr) ܁ s#`GtyEC\po\[aAhGX8 aѰsXV"#u2G~(^Ҍ[˴b!?fpKUEZ-&إ^ŹǖӜ+:3Ldp 4 v@<0*n TMР b44|\PKF*`1 h';)IL‹D(f5m 7M=Nh kHԧvVn kD[ s}H0p!CBAtSpP9k~hY&/ʹ 53M[5hQ5"p>_l<]ŕJ<#4 ]i !(4gԣ F08eZh#3-T#p[zH-jm)2eXk]j\ qnQFHnӥ}mpPB^uP[\UAp,U87DZI㺲 j]0‘'`,BG *>x^n_hB4Cb 0muCpT&Flњ8~8RԸ2mR G NUF(t;nRB_+Xa*6P-8a[E|n3+om[h(^;ofA߲!?L# g<й\A*Ni(ⶫKdZA Alc&8-\.D*J^!+⪈|]p'J{̂jD&m&bnFlvdl4~P2v\A d~U˲`TJ!#"2n&Ą"n&V#0,Z<2|B}f<£>\gDa%fL4"e& D@Z&6*Olm4Rnb⑊37; wN Q(V/G nH s@r4 srnr fNB,l!Ć N4'4! S:I^zl `*r 0o3%."B#$NjH'RS7 ̅61O6nOl%AxȸmSAQ,s0h!f0js4X-ЀZ_`(7r@Tob,2pHe1Hom%6:'#Q`Æo?iWFk`TG7D@4aAb'Id$H"A!fCZN6kAHcA!lR>F!#RA:vlF4XJcMC'\\ә106"cHlFv(/|꩞^%4bR7rD,knSÉ0#0kH5K /TO1&!la%}>"3'SWQ|:SmU2!nQUkA' If $pUk0֭OuA^ꖞzVQg2Q(܎N-^m Hڅe"Cq6ܗ==c3jN$u%~%mA$v62a v[+A:tkp"?cCfT+AviPD'N@df@FAii cBa`O$+\/љrdepC#1F.4ijfjT4Wdfᣫ&fjz$ 6HK vv"3ar* IӴrobm@-*]JhaRm1'9#AJ(S`!O{8Br9@xE%P2WgS5L)Sr@! [T3L-l y uɬ`~%se#Ѥ^6,ӽ)̘w8u4,ȤJ u_zhn3r49ۆ!! Ew [^`ܙ;Mc<$7k_~h՝bҥ53b(;#B<*7zIkkZ^ɒհ†v%; E$&4l$a-Jntg-2eo͝.9FPTxFxĥL/y 2XA/-t6A7+d`= !ݿzR5 TL*^X VXhWr3/~K+6}v L/(BH2nB ]S<Y-lZ.7OhO;u7N!ZԂohs :tq ג;1g8 7| `YG|z??MHyRKs\S*ub=.ͣS+8T=4ԚR!O?vMTMDJNR&J洶dմSd>9Eexq;83=< Ab ?NA+=a6C,\c.P* ,)(2 &H򉦗Jʤ"M2QH]2*>5TQcc!hlvthOְd'{+` tc?iCw ^77!65.!Nk ;|h c /Gg6ihKqS^lX04onD0 U04P-SxdҰ r,xʚ 8 GK 38ݗ_?LWd R8rd@б:p q"+=V`(Nf㯟:)q,mRR$A͓v%ge׏9XM7S8lyNӬ# =/>}ŽJ.@bSP;5\3 G|"I!N.JڗI!E$n8@īPEJ:J BG:X u:Ж-:pЂ-Ёf&mP`0G8T+LQbcA6 6A]h#O洄ИCah"؝Y!6tXX tC3漠 DŽRd1+.B4f2]!# pDti1Ь@дhB1,%#-D#[\m G` S, -΂ lV9  dxiH ҐFTmH%MƍvTKZp'ۊ\#noQI?W9nP)1\h.C~5]asz9EA>AZ'=]f'bܖmlbD0£ FzF0$5EUB Q:i]ފ8`M ;f|0DTWh"ykWjN9KZzV#S ;ժpI,Ĉ5YMhs @Xk'&M08DJMhG9*g0U4Tt| la 6EeJg=n4,}L̈́^(ttr'L!OBg=Ќ&L94KґlғgJ8w{cG>|#Ȇ2oP7Lac(cɿ7Z1 |'fъb/Ȇ.T4{Jͥ,I,R `a8 \Da㑔$Xϲ-S-H.oJ3uu0"}07L~5$g8VV1a7/!q>F"!)fGd+чv1vLJccl:r3;a >A Cgs!O # i ٠Phvzgz%zp=  P {87q%@`oaQDS}i{vFV867\>NI`:0@5/qs8~ xC+F?ؗ%+塐$ 39V3\9B2`/J#ȰZ`M(d73)W# X)}bCGx8 i&H} ~0'J`9Dra3"jW,7"?`cI/×7KGot[7&!g8RT9rh:WЉp"'p(@&; q( pP ! Pz<# ye0 cve}Pq3|@`*E#rvh6m$hsgȔФ#Ґy~G?K1C_3uC+q$|E)%%DÔ%I?%)eJ0Br(&>81-#,u}9| ,R#*! fJ/LдuvM,07bg!W  P(Q(ywp#yfi hFg xz ' `=Hza p @ %񌒨LIzvN7 Qw?7K)a],4Ї[t2.!CIF)v~ɹvC#k; D~|Vo .aaڟ)8הVJ᪍F#WRbjGљZJ7dةȕS!@η`٢ v`/z0!3:Q&f[B?6BycP*P( WNҠd]  PkyfPĈwjfsz ؠ 0 pR) &%ht^``7&M&s)q&* Dha2u32jzxw>9gq52#÷Loyt["vm a2쉄h˭fk󁬊aܗ!$I*a_37+xn!8:gVzMaw:$M$wZ2${^` x&(! 0\ ;Pf f0zfdvfh zfi Pף => Xa]>Z4{"K(rbPKXo9$8!".#HD$£Bp;, Bz&Lh/ܫ.9$[ڹᰁ ٙXr"dk?L:mcI`^hg'7;TvV=azƤ$Нq`s~pwɩ5:ԤPN NOS;#۽nnV *Hhfz ofG P۳h:*xQ{J`,#y#T,}쨤h/ݩ[f \ʹWw;ZҶ=wIlp{˒iͺ7kpK L"o3 iD|f{B8Zd7 I,VzXnCouKҝa#RxYx85:p( ǀ N nԠgӌ#g5 Q]Ljf@mm M)[b}DTBW#7<;uĥRB3wdM%~*9F=B!Ϧ|N`ʝ؉m؇w0%G}3lJQ"sr}#ϷLQjI~u{V/vã|i?g?+Yr+= Mc:cBdӐ } F dcg ѐPt nq +~{S gތe է<gס_J  g\纈"-.4~ s?򛴛7ܟBM,-kk Ҙ{^RM,ĹZ]"1ÔMA4:ׇ\ ݕ192?e >'`x.dR L%"E֗Ɖp+< ҼZ9]NO$ \O PƐ n=~0BP@C + p = ڃ!Jy <zT(6B}0%.1rT9B:Ci%U>ι[܉8XIg>,|)ٶAyC<88!u(fȤ{[&.w 'r?фx]:I,L2L~ޢB!ށ,& `qr( ٠V-eVf ӌ.Q0 pА<@ P@@μV×ѝKmƯw3gv) ϬEϤ)97ګ˪X5SC*p-q*g*e#$9^$0TAGm,XOM5DvM̬/*[ [ 6_w2N{4mu .҃ l]m'燛V1wO䅵Qcn1{-S,/oRy+A ^5 )^3?LcTj%3Q`enFוGp3 lF>8ZJ?#Z6M'>QO(4dbl!iG֒a;ӠG^29RJ"MɉdzɅoKK[ҨiF,39Pae2a*HQe4b ՌWl#Sv)UHJvo6V_ؐ;B:'/c<3mR±E[>Fыp }e(ebfÏSsO79bA'1~Y:CTpf<rdL#/ YT#* WhVȰO5֑Cz$Ә.tdB/Vbz!YmZ쓯 aQa28q5ѳ8񾻼qJV^9$ r\WBpA Oe~fydRRoʩ^TiQFK_P"UK;MR%o6e䣙0.o)aE/gaYL PZE-NӎnԍW Rū$Í(uH`GU74i5Lj)~F-K=Z4@! ?xG1ށvH'h55](`ADs5z IHbULѫ\?yVTʵsIJ#>?!e,~%2I Ƥu*K58eYxAc?hǴq?-Yb_uajAu\ 4V= SBz+,G^pDa:,؊7ա1R)6`))ѺeUcLn|TV߸i2F3DA:lu~#ȅɑ+ed% {{b< i sN&l" ɧmEGn+_rQʳ؋"1skV\`/NS,r]QkT@GԽT;qp^f:}8tn2-Om: 6W2;ca\,űvC ֨K.#(d xj]ͲXHd8W4D( X)& AgLL gCAZF ?x:u}YV7i(Xg" c\Z/CxhnSbj,?j{S/1&nxd30#50bc , 7µm?0BȾQJ>A;* +w,uٌKk,3>Y5[x::~&ª7`;\*Ӭ{ɰP3 Ҡ"A's T`JmP)FP4|z{ pVЙH]x LI /SG~Ц5⊝vY v8V`Z(SX3PE,YpXi(ʰ9˴zL,TA2X$;yi{|@1dY1l ؇+.{ G}| +$M&<| QuADɍu[kH1= N= %ͥZPќ(@*tI !z`B?0Sa?H!_*k\[LI!X^(J9\ }"h" P\688bSf/`yC ;*<"6#/lۑx#%H8r);;P -}2E5iɑ̊V@Υ ͎3`4bL%}URW`[\H^&*=BW-ˬ~Qf)?F](37ә|p54k*9vzn0_ -$^P4@@JbhbDhP3 ibIRU-`"kDW#Y8»_H"V7qkQf$/%>S۩ۦf)oI 9¯DI9W]n8^>ҷ4IwyRWqS`rީʊi1 ݜyڅi9P+:Pxyۍ:V c p.vu LJXt7^ą`<:^ lT2aFM%[[g |&X_!Ed܏*Qi[Ve}$< H yU ŲY6[kr<񝟽pi`0Y\ ?gXsBYj+Ib zPJL|ZT:Y9S(i!j=iQel˞G=pQf sa]ɋFJ@V-Q a S Sɔ6<IM{j%ÆWYy-Y&3Mʍs %/6S?i -CXf+H8NhXYKu&cr d@zE]J]ZPiK96VKxdX}1~S0yREhC v]mRK ܫ*%d[ >pwWG c|PXЎ%屐\$ӘJ p֨#r}k:$pN2~" O^OZ_@Ihpr ?=ST|G5*iENd748`HP&J2 N)`<+дc#- qe뗻!l]\e^gBĦX_fQ+=RqiUq ֹ9j}4l߆0%;(ˌ)PΕGx̀0簤 5 wNiĵ~> YL-6~fH@5=Vpo ~ #VEѿ9FP$R%wY{ h W]ۑ_y# gIH a\cEV^E {Тx xAAg-fM/WcaVeuj ӣP+_&(O8}"$pBwBhMBr3o#ǎ#&C#ir%p[(!My)S˕uak4zÖϗZd9-Z4P뗤B~M5kڬ^%kdAk랽{lkdrMڷV=Ię5K2„e\I^y,?g2Ɓ c VX Lzc}(NƏ#n3?s^g] /do5wm.X(ݒeXO!q82ZY%Ϯ'݈adl~/|A%[}eL4Ѓ*k^3pJG܁zj$Ӯq2C>`m//Aኺzx٦DbAm6oEJ%ďJŁ nMMޔNWbO>\O>;$K,1@?E&4646M̒ʔ|R4X'؃O1ń'J-1+B$=+QzznFElˈbq)GWFN&51!~?3z[zWlA BTmK褫y=U,c) zcﳟwv32>vo+A<4j#p͉J/C7cKIIF1%0)$ Hc' zq&;i{VlEA)&1XdnAvofW05:l.\; yƐkq5$@FTK#kL!J_vR0ܑ.qR0=Sȗ:0 &3^bjs#H;+Z7W歺 4;HC4/R] %}$7$斾|I.:|P `>Z40=m(`LZDP/$>mHz e-dy5 W(ÁF+j H"C{*52rW -enx+II $h<$Xܦwޱx)3.9짢 Ui/a%)^##Tċ/:/#*Yd5W kbzhNC<]IO!Y>pLFcCb!쏕T, WJK%Tjql`#)\!k</'Yd#.,` BF_:]@0hj(HUŘN|( O5)dR3 ?1'?PuhQUt|d:JF6>5@PSvD?<\E\㮄ZkAj;EQiHT@jML&wh4)SqSFD_ kB鳉eLxbҔV!nx SmGIHkvzgdp;dIز8F4 _P/`ט$Ptd#(4R B1gAa_p S0aAhjˮS2],"ؘzdF[يKƢT>âmGSN#l\Ȱ3 Ð0&;{E[7N$41ܰ<,cE3 +4+6첂!e80Iں >jrf27\C GBRFO2J0/V^JYaʼn"kߌAb%-:q2fo$6)~##uAjZmbvS0:EZ\wҔM?'6 ~.t_,zwpW2kXGI;JΩhZO H̎+1!Rot8ܺ8mTDh|gYCRfb%\Ju$8v;ʢP4fE,&aDzrk5aB`E[_,bsVJce~cGP癝AP)kRcbyF4k}@WңwyxsO9T! ^7q}U`pC*(-0;$|CLd,ӭJ4D#:QU%˔T4ѥey)Pod0БC)_z 1=Տy=]STPqO R(EPPL٥]Y))@6|/4]_B+<`?;LZEb14 =|C77ABy8YfHr;U0JAy !xQC$Q adQuo

eA S qinQ(=tb*THr,hg$Rj26*Rimkl}cid%1(YVU%BK)i+ `᳢_M cvNoBvCwMR5C8,`m+#(P-+Za)$Q|C/Ş5,a5C|S!B:6m7C6`B. ԕqH0ʃJA*a!bȔR_ϖ&8?HtܑkzFl1n\L& A?|R,Ș_h\,#CN벬ʾD yf#RMZ200*AoeC䋘k< ;-N^ 8RDqFh4jWIVJCbuRd1ڤUU/_},=5B1hndCb[7ndA&l█K&k,Nn)rSH=pnmH:쯌VŬ@簙kDo?D6^5Hg.o4I"y|NK&\,z-JGv}qvyf)anHJ!oX~u#|@E(@"|c:%-⇿Ӛ1өh2ưQ[Q\/# R-LÎݘ :CM7h)0l< wC7d1Y,*/nf"T[F?B(^Wbgl8 kH,կ \Nbצ.c@]!+$x"7{YqIՇ N5MaĄ=|YbǙ6c$g3ND7cĥk:B@Xb9<[&(}_c8J}/i*Wc?%^+}0 DdZ=T>kbtC<=Cҗ$+=~Y2Jh) 6YZ$EfmFB-[<@$Kk׬ivJ6m׺k G/W\[2WA*4rdB{߾},[ɏfM^\_ݏ 4k8gV^ íT̬akG:뇟tT+żzYJtǜb "0ʞkpni-s9ϱ=8ñ~C0 4uQ2t /(Ë+FoIfihcjȔZIS4A]ܜ tPAy'V_̨i\qSHS@eB`$QѦX)pi/U02"u뫰ǟ!1k-)k11hfBa%k8̇NdԐ\|xEu Xf+ Gǖ$kAV*9~6c\}c `^&aaъ`-BB AهG=JUNk~z15Q H$Ldt* xw(@X6#pYB ZiAwVgfp؋O!G:No"U-&1pqǪQ>I*~E/8f5nibԗvJ #CF^ZfZqd&kHzU%K0tPL/؈v||&|1ec =#`G/>i"ӘF2@:7\8Ʀ. RȤ@X & vғc{UN1Z)2;dAER 3`ɣn@ʡC搎Ѫ%C(RL522G>;55ύv%0_/L1 < "~XOJQ`uBy*rf$G*TTL4-j.,w װ ax> S_h@ֱٵck ?n仕tmʇDi|`^5 Q*LVl.*wq2t4MDW6.(_ e3LWUPz'1 "\Iգ \J13Q5&`E7h7&0"bX46&H>g)Hc78Qu#jQR$5 ('At*BaKUQE.|QZ@JR4 I"U܉ U v^6]ITbDH? !f-,.(1NMAaZ #kT"0S>+K>&JP #E*AM$cj f-p!t펵d>:ZfS|ʆxEQa"e*C1"F$vE[ӂASG:Mȓ=zdg E& jbΕ h%is>ˠ0`T*dQ؃Ь eBMRPЂ˞² әJ&@L9rʾ65Bߤ&\XjRzZB`.z Avq4eF'u_"Ɉ4&"d$pXPDy@8K.;qT$ `c`Z2xYĘ#;]HǵkuVښ@J'g @莻$88Z dH#8bxmgj`;fdݠi^Տ KX'O-۵z/K--rj>\g/a{~Q<|% 0w24_&4&'iq!** &l=91?*l.:pJ&NjZ;!t.z)=BF1qbИ,R!T~jZ/6 R"@E(ihW򤯸dPOY"^y)e[ɮ Ր("a-NV-᧲)mb'aArI'DNK?e)˼Odɂ5ڶ9p8j(@؃rc*.ׂBH bF=a(xz+|mH N7B ^;20~+Eat0¬a`P p.NBhR.vEz!Lf/,n(fX ɅTC06$Eb1> e%" zbC>Cf%TҬ}}a"Ha@aaz!z4 EP"Z!eaTnwpE^Uڐ&%ըb~)rزlm+o-C!ڲ/!ő&8E[Þ&LJ(Dħ%^ԠTtdt#֢ i!C>֢k(-w~!'r `- Pn]CvAUH0@9+c&a5*&Xvi1p.0 d9Xq(= %0v؏%B)rKHzEΰt|³R2h)@UbW\@yvV|Vl>,&F l%}!}oQ"Jhb!MD4 RDgB!^d3Tqf"܇>Ad~Ԫ:GvBLVl`.TUK\Alz%Lo+*^%ʦS- 0%9r' PFF/v9\t(@bԡH$$å+wqha\lƞ2 $@wbhJx R>4F0̨è(҉kg\' .A7Xb0.AO24 `ˮΰ xġʅIn4GwtND=! A(Üʲ"sTA8dOj"&FҤ>A5WS5 B*#\ Oo[M NSJņTxJ&M%%9 @GAƬA\N>#;*Ű:`  ۣ|1"81UG"^6?6vbTzz ]D{ze< :P"Jh!ed (v"a(ֲG?̧gcHw^sd`rƬ"Y.hsB"%(|HlGJIֲ kLN!6Uz! v&ȁlPgl 6I2趏_k iǧ(2N0r!7 aj!NU fB! baNRB"Z!Mv!&HZBX%&"G&ɭFe>!{P$pA.S6 !@a] <.*c'@|tU F $E% Dcn4n^">ACV' b:!T~+ a /bB\V~CS <6FdB.:15`#S;iԡ zV7#WpbŌe!V}%WA73oY:=pz)k!D1Ba!v%flY@̚cR``-|j@t nQ8 u= ^tI&!'֘LkH=sm CHY]F h"-z|&UL}dMJN*Rr!&fA R!!QY d*<ǪQa9 XobpY{}E22 E:𐰇76 0/8.wy%aYndl\-¡%aX4m`i(6v)PՆŸYYÔ'l-*`Ђ7^;LeCn%!p+(ʠ@G"0<^%4f >k u4l; .&(<@0E QLR~{Sa[{V~4ZZau8'n("=Н{Wԡ9؈X' 2( "-̲PO1fzfX1DV1|:Ai8k*eB-(iԡ+VB~/Y`8KC U Fɛ f wBha !=-D`lļb2p,C`hhD2QI9X vA1(kv&csu,ΝP]Z얧Pty~R#@!.%!"TuYww#jRĊ$vYYNz4޹bc2U7En"}h'[ny}-+jy$! /5˓]6CMP<R@\eD;Lbע!uU$ziYH'>% XaqDnjwe̠M6-`Çpc!c䝳Bm%FH@b͹e!7Cy_şä`V%Aa|>R*B~AY %S*}}6WMf<"0"N:%MH5f!""b*ubv}QDl}2 saAval!ZSsywYٟF)ԏs2ӟ g<.la8,w'Q'``H8}D`l=xO Ym-ҁ^^AwY*dEVU:9 ԩ7o߿̓W ZSM`=:봱cGu^xiƏ̇6ŃaOwya- FҧYIjCwRQf+9~҉Iz"0m-d[̩gռzUTեj辈_ifDͨnA[1 pMH-5~(V}aZOkkk.vч4'#/exN4lAŎ4WBe 7_A ]ɒ]K_جTŮMZv۳c)T =I3M4ߴ" (5EaD6KbH7<&ᄿ4FRSpI9_vۅ %pme0I[pSDM#DaD:wtGq4OAE?n9?U6PHPZS =O`J&fX tRuܖaQAydPAR5"O}@XaUP=TcXcD.k)# 0MՖgF:E> 鈦oc$o%3M2/G)r4L 6Ǵ4Ls5w,Hk_;R yۑo-Xgx6 ޴OBTpQNJՎ>CjjINs? [vb&RoVkE*!LꑙY9%7|@cgѕU.]g 5pMaCSZ5?v)5H1[&L䔬r,NܑW)0"/,Lא'/ *nɰ`/a&< Qg}ݨPpqNCxCO8 DVNt6:QNF*y{8e 2ݍJbZW9 !&Z˔@1dlۛ,'157HT#TQ@wHD8E[(W9/U!~%2RG2G,CꚓiD'A~Վo`G?,@ XbF۵y\q-)cA;vvl9F'=.jb?2׹|MhɱQQnHPZv"ta䴥TƄxF6 mGkRt#TS53ҖZU[’ɰ'%pRa2" Oi谆&<Z ,f7H"">p*T:H0::fVPJlUT㑎UTR*qâH+)-w= mR9T{A_Ģ!1!"9,Z?NrT4=[eЀu2H O'&uQ|?qElGo?dV#hAd@ VIadl|@ "%>1]#'!"E4|AHqhO0C^4"Ƣpa >k 7bScZ'~INƜ6UV5qpCw`H\>?Mm+l9c1S_moYaHFa.xzFD(_8=o*s)as{5 oTdʙ݇^Pv V"5IZ/Qq]u]uobH(PY˚+ Ϸ[utA @HpH; Ġv0 5=mor>!FE\c8P|g#(2lc!VOחcfBLYRN&% N@j!0has+rq'@'zzM4$a!)$F4O9:jd7AK$(*(|%3es}an lvE }\b)1hVWA$h&L$iBV bIG ~-aPPk YjA-(. `v hi P&G5aCEFF: !817w%5ex8FRQ2ȅx}$NOOzGM@jyE$g0nDȄdC5Fz Zr%&ir5Ny$ȅ-doE~Qw?mb2FK%k61QMlztX9!qivsSԋ/9~r: Z/ذ<"/T1 E`0`k :qupuӀv`l'oxP2bo1>"r)^q\xU4h53C3[h dN&&5W@@ƗQd5e|Mn'deC QFB3sI3 x9Pg,l~S:0bCq%vAҵcӕ##)Uf$arBE\.u <4vA}Cjj, W:dbQ[zc)5ld7kZ q_ `atS ۱8 @T%.ٲς[G1 v0$q埠FWՕrE1&nAzBm$3$<0 WF$R֬.EDk6plӧaez4&$WK]5Ҷt{p kq `}9T,O` R ?D$iy) }QAViK۩)5. j0jliv vΛ PGAI- pU -[GΑU9 Hve܍՗ ӳk+2i[<<5{>W"A|$3R$6R2ߚ1>3瘥C6Gj08CBEIҷ p(6bà K%,Vɘ ;^Q#=)#ȑ6,])WD,0Psv栰.)A VFt{T: 8u u1[ 4 DMxɃzRMQKԆaK$(,«qDS8&5IE=q <݊Fʫ L (_݄p#7j}` S{0P׉`"6C\Q(8%"a&"ҿ %)-BM:$5ٛwpqU16A52mH P g J*&y Vx.0ˤun,d 1nmYp1a+RЃgҟh%~T>1$3ؐ}"Pgcdʏ&0@ Nvܹs94S|ęcX즏֢@ y To^ H0 Wyi0֠nX")2xFO!7q?1N$c[|)P,%1ڒ1 Jz+&)rE:ց03s >nREj*%|ERI݃9=PsRXp2PN)>)%w gr1SYYԴVZ$*?Fҡs߇ x 3/FӰ b X</00 % #= `a4/ UD'qNehޥEA,F4!8r4I4 *RU<:u)3b*?DF~YL@1ܣa#P UP4G!?pK/&47A}m09;ӫ??]F|:L|Ex(ݸpBkw5)i K%-bD=SR-, f}&k9dY"Gļ)`${1~ܺm/`AK2둭:q::< J kNv!QcŖhQyƛ-*V( =_gZ"֊!네c>F*Wj-9ɜzZA&r2vqe\Al2|ŕ_~1eigybag^| 9tSR4WvFzI&iJֈ? lKtN ,ʃEԢ&( ^NRp hraa( ZYUy8 >/hYl2bq2Ǝbu"DqC8QpA]g:F0`JsL`ФHr$Y 9qq ix>QbC57|ꦭh\7蕯]:$ ny()9y"#l Ӫ"M֬[q-b`Qpl jf}1t4,p>tj Y`: `hfFb!P*ד$hestT:A<<2A €fX#Sa稠Pd5F٥#я [xb԰Hdg@#YM䠄017 T8ds4 #vahVg\CO,*) K(,@>(KlVu8~ĆaD>.MKo) (M{Zβ8/IKak ֈd%D˷o4[;p r쭛ۜ21xp#ykG]ld~˲IDD7ŋQPQX~(APR@|ېjXf@8JJ:,>XE<&ð@iD1St.)Ї9ѣp lqzCDl,M#񏖆jѝ ]p>ʮX+yq&AK+:I?F]t3>Q(@}uG?\ü0,XXcmpx6Pr##:Qv=MCTvƨn+QP'7 L4P97X޹Jc;c`*seD1kU&XRH‹`8 :A >3q2ܚ[5*$;XyB  S 4,H$>B:n;<v+Ec< Cn߰Y; ÛQR 8{hS-#4ѱB-V6# T"1'$A_rԑ ܐ 4Vy<yuki(… u ;UQH | h 'OZ@ Cc 臘댄I㈑r?S)q8ĜR `a2J@}D~ű`:c c XŰПw+Kj@`[)|PYM,R2ȉPH$YJǑ#̘{RP@I .6Q_'Iۅ_zu(uɮQk(QlIgai !+Yv8]8vpIH8?@ 6y 0Y_v400>#!@#3l< PxC~4;|>kK:C8LL6a̎?m+uDtµKB`u (58 OLTQy2TЋ? JM Pjŗ0NWx=HYӶ qL4O(t>uఆPS1nHgLg"]K}T )6i"j#?H - K@< QUX LQL eJQ}R_h =R#t%euxw`VQRˤCzH@ziJrzs zC@0:KxZ|BJ e%0N!DRzMXԊRrP%t 9L (-[R]3usM/Tr9\(~5^lBCZ|NBcM1:X=1T" :s>k}St? ԎQX]A<-8H"F28㥉 SlEX)I_(uKd](I̚]ɴD1hv%*lr Bȩ$-TȈ}PY2J9pTuS͍tjha~T"p@?2x4H~ 3 œ׵::s0+~?/d2h8p?ˈ P<*[~ wPdHcH-̓lx0<λlް@eޅ ҵA#Tl<y5j]"6ǙPϮ92@!388y`tS5;C?yM8`;0}]B^D^#Z)%:)ksw@l;?xd_dd^`fɚY iAi^q*ɇ" $YJ ކz16A=B$X^X؆? 4A=FZStș B0v9PT%}E$U $ 'ͰXpc /FḯXܘMf ߬"Ε`O\54@T܊NPdMU;p%trk [Vn$Dͅa)6~h>W3MPi[$#竏~嘹%,~i{j? #._J* <WХY2Y]؅ޅdfmk`^ؐz7ts\ȷ_pQ~Ɔu8o*ebhS`J6qp'|I?( A`YA#kRc'p7-- ĵU* $Z͛ pk\yXi^dpە4pZL/ۉo&+ cj-h γE:\^˭+AP׊о;턞uڅ21I>J#nduPrP&Zo$v=@@@ ⺉؇nYljn؈ɾںl|ҸtZHkX !dX/낆|p_RiH&AOuQ/Pr8o8ep/e_thHem/vR*Ҵ0r+ '' t~9d\5~HXB ppIh' U56 1EAkCbZTƴW kZ ҵ@B"N*ծ͏KܰDp9pڼwbzpy:?ٍhr*tA!7Vpv脌J)hrbk+DXp,*䶮diL^ǎs)Ho(Awr_U+)ލ NͤRxfb{`vMn.ti(Q%x rY)6p%JpKվ&jmنd؞^u~ n&n&otVg؈nV}تzi^Ը@NΩ##M3n@S1;Ьʼnk7W' D %&)"RZ n8IYx#2<Pʨ~(L1RUaWz2l8ʿ~bOݿ2az C0n0 ?~M!#Ќ,ƌK@maXQ<CC)ܸ t hdDRr-_ j/Ɇ Vq67R pox:ȄA7hѡBq37v,zYGuزa_N'(ΛMt3o~\r: '\ro6kd`5|Oyl U-G_;ҰÎ3Lc4HCO;͒ $0bpnp MJװ6j$fq԰0~X3(D"!3ՠ̹`t S"ٔ: [a1T,oڔ0<MF4xE% a 2ڰEDca0haW5LUbu|rNxҖ]ڻ={  .:: Xj[$lR[RD Q7/66܀źS۸ -7ᩁd 5ͨÉ4X N kkC1Q[%^ՙ9prŹ [rꪝdq?~lG/fq |\F2dF3,8ŕG lFr4v<-jհ! u V-M8H_b(Cu~2*>8O]QeTp<*`awC@l(C*hd r_{tqu(Ѧ0řz3` ?;mRfɲQce"Nݲ9&3P~1K"[乌XQ$'J6;XřF8ZVul3wp)1^L}vqk;ر,=jO}&d ,e2}*$ gA RhMC俔oa$ "w/NSP*Ha{ FܠmyO-/ %JN@#SD9X ӊ*#q ?|oL?A@2lytp3vYh#Hf22xyr(HT4֍l,OV2k$w&zF4l7ծaq7m&iWG{>L͓df̱81Ι^޿K6uH; P-|Ai+w; fyHsy+BwGG;.74:83L]5B+4#<U}H?d]_FZl[EnqݍNT^ܔ! MYYj`%q mC0D(A4h Eq H^?F,3}iHAEI^F݋ ڒm8_ ԃS_9PFs4C8~#9: ;: =4#(tMFա:tFLXn|%Zd$NJ![ԝ/QNia^ Ph amdŃyibG`F D5xaX0[A!Y~}Rm}!øC7HxbzbtGa5m)W2MCZ9 8!HAId`LHW7cj]jMFX[ )ٍV*N!,C#&\ׅ Z!U]m%7LMm<HN! "LnŃLzUHT iL}O>Jnڵ0\"CًQfd#U1άp&6UnEi@@'Cf Ya(Q\iUD7Fh% p`eF-Q(iupx~`+$;RR쬈U'Zߑ~gS]"beq! xE!vM'=sxZ'̓'8!*x]1G䍝|Z}2[> դZ ΍%68' W@FWhSF@rԤRPT@dIi&%xŒmhd\UFu g'BNIe>UtHisL^*IbDiPa&ivDYY/3<Ö韕C*@=&N4 4„B+MHhl>d@Y;\Ml\~$ \ſj,*'2",׼I>^JH끉Bm 'T&\5zNSVj1!{TZdg!9ޞ FAp-ao<U^ƽ ߶jߓ)$ p*@ܥ-]Hio+^6_),elQVl]7`7jr::H3E)4)d6BbU/Te")4l#Eԃ &h&(QZ+|IG5nh=D^8.pEe4 &}l$ L"eTSj}^Xũe NVNnU-BF nM[q-BMTn 2 \eb 3 OVԢ՞nW?歟Pt5вMo.Ĥp,NVZo \8\J-z,L.4mnd)MbgDqTJ꬯H UF-/r"DfnUei ҹ-&OtNb=}fpF^A*F,Fm w1KgB0g g'FVeq pL) e&OTfpMj5k,2o4HƋ dt.pj9hJf?4hw,t,rNC$C+.Ѡ+6;6B$ոX|5j]00jrE=4ђY$'pQa(F&g<(MYIAa'՛G9x HJnyrVfu|'P% m[p27T`f3"o W۰_Þ.ϖmՉ TTtra2RG{7EiqO=6owF'Cb1VC/uIWȚHǛ"]5ܬ[\ul|VoV)%P 5s\JLaRu4F6O+ 販5b,G$HhJ[X8P8[Z/&p0>Zz34eu*0UpW-?(3Sd??֐PPU'"eQџaH0v:Vu ?lQ{؋ B(ibC{`t/yGb˽ʽ}ؑ4/h& nj!Rkr,Hvswuh{" 7 f?b2&abhĄeEhxZ<%ء,^PXMQϢtIo:t:-]U02 2 o_/Y@ p$x?S%'s\Q6wxRpX<[@>pmP/=x-NTn Wv99,4LC*C|!5ÓG+$> 2B:;c9#=p2l8,9C2 ::=L+H'(I;Hcg{>h u?.a!1VLYB +ܩ+[nÍ[Q^\t@!g-Ej:wքA].;%c OG>ȥª֛9}W}Lmjr^{ a8|z b6I}iy~b"J8GgWm4Zyaz*c0sI`GDxw_B spaDu֬sW?u-G)R)R9n 4ǏetRK1n ' Ǯ L JT)0)zJ+(zhĮI''(ʑT*Kw9sH1GJ*r:lJGhш`|$KH#\kx*1LĄH20촡0 90 W}t)*ns€LVH"8~1eYTKigka&謝:i3E>SZqEW\i%]T|?zt7AdiXM%?LW)meӒT/i Es6H"r $-yI12 ˈ'XID*K0"OuMsRUg:Ob=lԻ&cϗaFK3 lydK!+[ dL1RPBk0NZ VG#J -Z7-Y={%^%?&?ҥȕtqWg}~Ŕۙ%zivfu9gsعwH&9\’O\M^8! RnF^qk䭒4,TjHѽjLOPִTj #/)-HOdreK.S֓HT█*~HLAI -+Y"-F\.)b {i%2yǔdlAQ!^ J Xuɀ1C>F2wծwCi+ykQzLT$+P8p_WGh1p/mS>0%5b[h#Do' APgq2ъ@rMQ< I᠖ >D [(Yb?}MR c\WLcrĘ;ȄbȪH&)\OTIW0Y-JN/DIVnrlI4xF,y}^#_ϘQ̞7h*^1cSF !A 8@8o y$KW?}t HH5;Đ#JW=e?3dAn7 Px; Z:%4S[U2n T"#C x rRP ? ?v "RFu)K(BY})͑(J*ЀW8B=K>F*=P_[W+4K*M81RlÜ]d9L |k="! M ܔ6GkꥻciRqݱ?tBXր>f{i`)$moe'Y%Dp'oN*AQAӢ䚅6F bdgטF2 jqnj.]T9ɨ~Q W}\Q H>JS= xv WΧYS>̀k|W\ hi*w6bcVUf lyf>\2@p"Ms*#!JS@t407@˴0mi ,.#X5upb)^Ke!*l* d (P㯘@*Depgj n%XD15ojؚUb"ԉ(R+l͡u;b;ſR FC8zI ePpq7cz2|akPu|e~ yD5LMA8=suy Ld嶷ա…X|@m%occlo;CMKfQˁhǁQ|e&6F4(S%]ӡ¬^I$0?8 šYg#s>V!t4q*|n0hG&ܢ1XX.6 -Cf4m0!'MӲE>|]LP y4y\-ytp֐pˢ FމaxHR 7")a.FH /0c,tn1د,)IV& T$V%)bN7dE-`"M P˄,Xw V$)fP!|,j#,@ ߐ~^1d00l!Zb"Ɉ&kE00G@(Bh2"drޡځўtxcâދ;+=T*cax1gb#?e#hNTAP "T@D<ąH  $O.j %v!v!/tBX$40X sKME@7ܤ'Vɀ!Pk2P iP^2HZaq{**ꂝʩ萢nX}J" El r-%/88aQ|A[&**<0s;HjxLA^^꫼@r0i^(m#|0E6a<*#a 0H a;8lS  !b;5: %pbX6n(&X6%X)uD!(==A-/n+IdjR07 *9Oؓ0n4סa4abG:ō2sU[u4ExD-]jPw@y` A#>A@a00 @/p8\9 6",ټ :A+XA?l<@  "ƴ,l$`RAJ "  C?oW߁Avrj?cxu5A!Zi >&Rop5 8`5d{$ N]in_[!2![m`@j/+N, mRN-XрA#4 7֡k &:uV!cI, `m2,_9+a!w: PiC@w~Œ uʒv\ w\`d(}00{QҁG /R/'p*U'9H!ޑt:|3eѤ\53/se]]8Z]^5_>rM%xβ8]d84"L(ԕJTXM!ƁJ"M `4,8` WXqq  BlMB] 9[/"љ D5`M@Ȟ a @hx@*B_, z !|A }ىnyjEAm` `  *v`]q- la]qGΌOt'd-*ʲ"`BaLVA] @/-;R0U!G-.0uM2 Q1f!u91[aX2=aڡ|Ax _>_ zsmt50& >@RB8 p\AbS}[+" +^xxL"^!,X ޸> 3!Y 0 2$EꡂC']#~zBLT vL "yvjXl< \ɂ D]@ &"Ơ좾;l\Z ^*r޵"@na<*bB2ْ!: 8éE1o)"ޘo* ֌!d!)5/Ćr0BK%C?"(/02 .{7<;ՎUx; `M略'e%C-Wq,8@ B,62 T_%?$T~a=%|.Z {=\d ⲙ-#› kœ# ݬn!V/1`{3Dž Zƭ qS$9nqx_ +/ <Fն8BJjtL©CB0/@֯D?.f5WndU!SsQIҚ9pEf@:*Z 2K 3Q"f,@@eS 7kvQZ }xfP+%D=m zM}@F9= 8:|ַWox3WOT hN<7h|T߀\W<.\sM/HS,L+L/8K2!41R/Ԃ㎮N;Hd94L4笳ᆟHHV{NcAIAv\ɍ7ɓIlY?N p> p  VY?֨#QdAHCwq8il)Y\T 'U#Gbq;6aSb Ï[4u %NIVMYc9U}&P/ilS52 !?}3dݥjB:(z*syeA81t|viQSPPVōmnO?gM6Kp=4@uD2QI߉Wϩ_l~ul6%RAJ+NXCh[Й`&.N`-/CbKc( 4s >׬;£28)})C$6DIHcΰ4H (TipіB]yk;CQ?37/'Ї'k'~F Q?z$ƃFz pHCFTOuNTR^pp:/+d /w$ኞ:論kV?A`E+{D1 D?r#yq0h,^X4;PaqCn&yL==9Ìft!PCZ\'F胕`2>fdJ} o>r_ %*C,pJ̉f9՘, O]OϚy$>G/}dqӘfLzGI H,f! lT2E+d$4f iIϨ5wG+@)^$#Ij;4i8X/07 IL" >%trLpߙv4\LrȵFiG<7񃸒,8)vȨmE;h%JEtwcXxQi{ D/+^8;ֱLvID0kEc=~^}pI瀢Cܡ\B k~(Xt(x. KxZF1^N};+9VE1^H{Փ$4HM,DЯMlRprJՔ6yȃe4JD\PC~{Z`MXjZ 2@v -:!BcKY;|+g[I@ gPA(65 f X&iͼU@[Rc 9bDR-[ٯWOR09a@8gjs bjRm߅QQZԆ'<7Ј'I)mw]c)SDؐo|Kv7jou 0p5Z3>uY %?B Mmwo^I2 )gOlZLP]69(,":w50* k='@nzjȬ:S׹ q5Y fkt6KOn,a@vŎml8pfOjsCS,sD}G'e]i~%#516`v\v3roG!I@ dI%SJpJ8dR 0#7 ߰!$$IL` H 6:G:1sbNN>AbHvV_a3*A|p"uݧueWa+{*pB^Xx&q&w(%R=' iR'#?wQ/&sxAgiVP+QSDfMqR/BTb&D0i"V j!"u*Ķ&Z5D$UU=!1,d[g иT 1 {H Qss=EKn57nf1 b|$ea'tp`3{ 6 @_m#6wA6 ~ %8D8P#CL%` y 09LrGx`\U %~t%#N3mF\>,4 /OACe #``!f|Py3WO/!>k1\_mt%kY00k_旣dF A\.A51 5Xh'.U`@˵Lұ5qiU !x0\:I'F ;z`s)bGT|Ks0yl!O!óT>u aTa!, Ck#P $>0 )  y0 m7s?nMZH/)Q۔۴:W Jc% W! b!" `" J$b"!##Z ` ړOAǕړ[9BZOj'q[HMK:b jMU/V;V  0 `q C4: + ; T9&  @R]ߠ K7{_6[>B9 "R \` _^"&[zN 8 H8cN3k}(A!hkzSs!NGs~yrX ʜYb5i@gf:0FjHm{ :KzQ VFFZ[Q3JCeZ)hᒞs8AzV];Aa@!cˋ knqGJJO7 \X\ڜY55" 4 D#6"R'r9aӿJpG"$5z# P bC cq>$B8[nR W:5ƅ#xqsq ECZ{[ȴikkQA5YEQ[Faev [Q!%Ts+ĥM{GRUᗢJY3\TA-QQ7AJL8CAW<\j=nQ{PzSYzYr RRIӯ#N?!C$ $p` MQ!|s1Gl XzHA?9衬 qm{hֺ ҏU{3W,{L XN +@7x0kA@r"{ 7d@KYǭ㷮lBNX* ~r4NK5=[Z50.)Q33TRz\z ;K&%5:[rP\3K,'8s™ Jp_Dl L,Xp&rIdK3"~ yKTr%ۃC ʹLp 0 @` y{:ISп찆HYn#64aˎWzU)cUc x4Lݨ+HA-szs@Rܱ@߲G?g}džB=sD-{ _;ik:V0Qր0ݣQF|\Yx :asHx2=G[VrbCߝ\p]"u9j P^JApuq9_#U'λ$^)$0 T;ZW%A\2Gշ l{4F5= }+žQ[%< 4KDMY,j&4QA^lh B҇{x® f ZrғPiVCK| R}Z4stC2Dcj-QH6nM'|}V<#: 4e=Xp5 ]pD2Pt8 ZJ p^$K $p_": ^Ł @8  t `w3*q}VVBbHPz̹G$նV2w X_"ʃ A“GV tA^ek|hSX@ @}:llzi:W=9AQZOo䋷оک~47"A8C:͢4lɠ2DSy(rVr ɷ5آ6-\c2ylfQdaӖ`$?hFIfiRA$d|"ӠO;Q^Ly>G> )P2~Avd JhdHE cVގPb4rMvm-!+\IxbjLik1UmGUUNbM\7 h7Tan'*wL#R9U˧$.Bdxr V1T/Mr ]"EXcdrIh^Sd%tnL ɗ/ȥf\ Mt(PƘ.ZӍ5[3, ɧZ 'cl|v]0l+r2ɬ!Ug@(ܥ+29&Dכ ST/>(IڥmZS-5yB=>抣\$y#ުn@[KR7FLoT CŐi%UR;JE?`"&&}cB%-IUU$$Muy4rM@gN*J>T({gȘ"3H!=HV:Rp+.#ݍ:D4KH\1WLipUJP.ywog~ɀ ѶC'lUmu;o8?_CE>P& Yh4^Q(野p8 i1؂/}asb,TÞtKdd26˿L_ٓ;UOOEɑPdWjc◲%,zW)c#xT5UL{G]!#Z#s4j週#d=0 mU e*V'-&]1j8^1^׸(ZXWmٵ4ڑ >(kϥ[!^m^[ZDZf yƅa] ]ֱ?PenʵqmnFkQܺ(an9]8({lDE' a̺c3Q[&IX)^osQ.p%+F"RTV)1x$wj¡-w.K+ګnƍ^_5^NSd&z}{I{童T3c6g*k9,^r cy/*}%Zѥ݃3wƥa 5]n;3c2 ;+~U3+ Ƴ]cTcc2[5j  F +pJ<ҲYɑR**0i[V / ԫ@\: 4q5)Ad !h˕㌝(/q/J@ =6C]|:;s=Y H4FTp>WxAdLciNT])-fҍs[ R-)iy$!Z5m;x7ؙC,lpS(n7j^ ]X1Α*4L IB_Ҷz :Ήx x@&;;;,Ջ;֛8Z5׉(bOY[' 숹ӓg`@/Q)oI Q82)3nJ0D _DXpyqe0ʤZ@\8:Y i!q!P>$@HƔl+4?` f A3@\{(Uast鉕5q슿LbQLh[j8Z ){1h 0H DTiKp?#m'0~ :)a@2 x+d5a-^6#klDsjtJ'l مb`켆d zzϖy+ipoI9s8(hҬvB8&Hr^8؈Ȳ1 @՜iġUTNCکK5D+9RFʃ 48wQP<I&TjR{dM 8,,њ5'Z }#8-!tƂ/bҼCōs;Ҩ86+{ӟ2EA]w(> v :VG#_Pc Ҡ h:vxzr!vĉ0D!x% ڻ ¥x{8N@10>x>nj#*ԑ MwL;p1jړiy@f[}/ݰ=Л~XN@719<{7GԒ%mF7{+!҃DSX`KF|La12 Ѐ `"Ih PٕU8 bPY'X}\`:z7 :|0mÓUNNY lPzzbrDk(_}*(32iϊ!OVJI( YBhX"˻5 n"Sq,}M@>zPڸ  Hߣ}PxȢMmI #II!$(0yDP"dKÎ> M ,R؄DQ=큲t ï~h]hXu_30ax_3 BX\@j8^@s5Ud&LP:Z۰S7RkrCt9E-q"JYi i_.Qb{.gJv Y%㼲m]YP\SaK XĻQ` [X/a˜]ARL=}݌Ta|p`fe\ .x蛊u $1mHы0pܛL9%&?̪ ad}c+^2CM88_Zv_-EXx驆F`Xȝͯ6)4,Q#S[h2%[^\8؄Fx]\)eBTruS,(Ija'Mnk&_)G]\-Z-_>>CtբM6Ɩ|j ClD޳ a6wN4(:xô]0}4vnOB`כg(B<=jWXkq񆺆"X\ 7kWzkM0NZCjm\MAW8pІk{;Mwmp{O騆Mȃ<f@Aw{CݽZ[`Td@f`WTY"82G{=hFpk+\J0$L<0~C1sw~؆m wN`iwa`PUhr)9nP{Ax9{g cFp" x{P`MݿY*+k B.5n(m#Ǎy 2DGU8LDu)%Hܩˉa-"@m!O K#~:rsXO]ǧݷTiA~`Mjnۚ#hԎg/W_^Ҥ%kִY`% .|={ٺkZr훯oak/TۥF'ٹi{֮4>Tܮݚ4-fV/h.wn,a<5v.7<" $RS{hH'`DT? `wϑ" JR`Xg?9'c?b0!J|H V?D`Iʘ89 DPHW[4Sl͖VSTR,v g .fa'CIL8Ry|ON=TЉtRv GO?3S%uQaC%BxWbѕwꘓ^}u4$3c`c $5up2J- k<²;<3:(Ol\$0sۏfUA.sYg^ . aT|Ğ47xBZ^Ȣ"U(bs ɢD8"PT#BTǢTTvp N4$2O6#>37^㷓xk>ӦЂH[HՎN p (e;C_"(/!w;mmt]ATud΅a1ع52@ZϪ/ v|82~A_(C0EI3vG;h|b:v{#5$aFpd2 ַt+ёueG 0GD)K~cY hs@>PRXK5r@;p&C}@_- hjHT W%LЈU2g%V]9 U!yw>qի#KzD! h4fq0I,z]$d1/XU@9^`IʦQ3dp9t ~~-8Tnh,A hzlt"@3ND@?`I"<)vH8AOG=D{?A^Vch˜~y2%z?8pp4EBҕDA%h pǢO'cC'c (jdJ^Z-y`x$pqj\54dYYpGDy/o(k4 c)sV#B8}F/]8׻(n[kQ0Cj ~9"Ax+T]pg!Ru5-BC.}BbӘ G1q ,rmk^'@ qfSl``)8؅И41VfsNi#]x?ÏiҖˑ{uAHEC Ya2i qW*@%`\K0C\+f?.dakbF,tIx"ԡ+hT+z`r\6Qϫ$*׹ЦR;Yp@]r+ϱ\ok#B d$k4,-{WpMUy[cYxǒC*nPCw,@~P7!c_5XZŒm϶Pij3Kfa! -xH7{2dh,,R!X(4Y4?wi|AC@)6F088aTg87׀ƶۡoCfΡֹ^1ũ m^w$AďvL] ]|aK8ΞqR QEs63+GdHFc468c[f=[Q_;·²9h998;X9\;H))ܫn1?sVM]4#G(R"|GZ4uId^l-JsP­άC<@Q~ODڄ;I֤8< |@:00L _)t?wx鐚O! Dv # d' 80ゐL@h>-N K8-78en13<SmQGiBauxk̀' 50F =@E+4IG.4/ crEuC:0:1?ڬ! ICcB/`C5 c).X/[ȼ9b\tF@rP4NG6 h|2y'PT+W:A;`ovXUG+~Ppi,h:;aB;C2iH.͂gT(7|k27P2+@UQ;Yw~ѴieV>,AJBAʮUDgk2*_!kGW ]" Ϻ"f'E@U:1G`؜Ovތ$9 ; 4x`\- 8It ÷ښ!\PURVCFP'x-q06CV0PCC02HZ4h<00yE`@xM]A~%w_޽KKZ;lv]{Ҥaˇ ~vZRʔ/`f؁FkgYg󑦘Y|fRfiBC 7~,H+45G 7! F-Pl')̡)p*-%u[Q1,RDZnD!*Ɩ$,4 <%bȪ-'ӯ/^"'$JF#]q0 g2"9 ~tDё80ˊƺs(#Uid$mK|+TڴA4IPzɳQU͙V:밃%uYGg)rygk}^~iv[_v˳vzžuv9u>va|I$d_Q7.}}0\gdeFHgKN^یvIg(jD˃ ,ZQM6$cz z3Ĕ[ZqjZO22ވ )g8'M91 嗩 a[rHhehmImUPԌkgucau1"Si{vV[qvF\b/wVP_\^_ŔZua]i=',FcTiI٥J6$1 I<7ͩg!ݴ*(G9Jx&V#H3lF4/GI]dhqgaHl$i8"vN6ZgE'PVA밓];zG1^we B c/H#a4@ ehC ŴEk=J!2%O~>WA(91~NR{"\!PҖĖ&$%6b<GiȄ](3Lb1X%ql"-o9kl.%¢cTj&S!fU5<=@ʕq.GjJHF"pug}L/1 g4zюos/ T+e=.RyJ9AaHf Nr<2&B>M7l"n4(]~xR5+Ut@̛ʍLeRHFBP-K:jV9Nb".i(ppNH 52 %*$ Ъ蕐8k$yR*>Q(9zsծAE21kvE H^4uKD. nl8j1 \h[[+DeC*1DuTtL4\;u&kpjPlF3QdGȢx'oYϨ hp: D4/J6V2a&GtKZ. Zz8 TB?4DHv *]r8W88t2g d*Y]Z*ݷFNb(F1QTPLw>AG;ch(C E; ݋dѐGa~#F5;fHir$0[ƈݤ2*т2k6*lo5uci.3R(XaŴ4]\-L&H jDB!TwIfEGt;>b'jU`DeV`~lȖTgʥhp;-Y* Q@ 'IQd29.z1.uK}\Jck4‹iP>אF+nVTJ*S-YKJyrT4Rb/ ~8~3Ƴ׼K΋f^ $)p8psL&ń$0iKNx]ؖp&0m^I6H- B?dr:w{keɫe2|3~V'90K<\{9!}s 1/ hE2s X,d;"xާJx*|1 _4^钁4I4Hbfj*Nm>̵݀ *pBi!Ha BB,*F"0O`J"^ gJ|&9j$c"DNt &bmLF+D/H,MO/p6Y*Lgne"e1- I,NcRTh|F9oPNyЃ`!*`aa*31z.^c?L!LAp*zA@T^dvCГ@0G0~@Hڇ{pPR0"P0M# ̄pPk/B(3$ip,Њ8YȎ8'qgp4h¦>,r Rkp#F(156]h%oㄞmנ!.Wp߄#!#RAҒ`t'}(S~?\dR]>]az\@᣺'aafgC9L _`\Ό@p" `MPmL\!i1iβ#㪉쎰k kV&sH ".'łbZgi&ȿ/mZpq8F+ل}&gS,p22 DrR*Ona'j(YQ<{'˓zE]t)ӥ@*$+ֳRg,!VC0[#lo|3RҰVKltt/n.(rTfN)L"rM!i2nD<mrPcȰ"/OiH@q^i*\!j# [p#/=sI*r%%oR %69#U c>`*~a>SzxKb'AeC4I'a@nzѱHfN2#ɸM32L9k4!jЮL@1 o00 y-N/8 FԘq(*-4 8<"|R""$ۄ\5KRJH9nJ7oisdhO"kUZ%Cf % '&=R<:*lcŁ$E1M?5/P&q=!@s=Vx\rJ>D` da*aH?~&a*!oc,!sg48$Tp!D7 Z ZtiTFpmu: Ä8M>J)#"#-3J6p"tF \lFt8EC8og!r! !@ZF~J+z#av4Å{2H RqlSm7Ť +EmY>cYdnU&r 2/L%ri<˰_V@PPy8D)(؜c2 %sG8[c.N1\cd^k())F,Fhx,F3B,ʖOeb[ 5vY!suX^X$z !mx0Վee,(}}h(mw>x\~_>~C.Tہ,.?G,AiNo Yf~0)x`)nH&\bqJ[={MkG09/9*:*q:#ZR10#DPIs0iJOsyIVG7[L=e{C[5w156Xl3DiPB#07WU[8IbAQNGh! go>j玒=~z Zgo[ֻy=Lp88K$t3DppVظp c[9VѝkmP%#rt0B7׆|&.rUbɰ/Gl:ḵqkv94zYDwbViW늟eccfiYEps K'VO b0K335[8\BNn2"gC5lyv'v]FCyb~.BiA&`an1D<"D![mz[Y#4u8M$kڏ,V8 *BUАj,B%U<"$pn59Vh;ß) 'J5BuB(Fxju[kkoyS:[ Xsxq .ۇI`7PѮB ŊqcУ #w\!\v\fǎ!ZxOd>XTճRr+!A]${?g{Al'Pz]i>"`%mۭǴ$wgX/VoP4*T/,W8\_s8:+ʄ<$Cs-6}TR|p~Vs2)n&̸0F+B=Emev͝DoZ0}WfHI}I${E]6#0d*}ݝYEV,[nQ2^{ ֽ{;vL˔)2De vk'M#d9sݹdћV엯d&v@*ŭgOuqF|WOxIիJgNT`.(m ö->Fz +>sYVj5}Tǔ:qGqC qО;lpSܢNkU*?Zu7,zPV_u~S 8? 4dUz m:zh8$JU SpHUߗfQ:2g:K[6U N+LA XM]C\⇯1EL?JxIަ!FiqgpҸS;YLí fRd%rHPxzWXPeY9w6}iLsFd5n/<^yn^^x]>}Kp`_7(q.yRO_Jˏm9`Pc>Kڟ5;,eȆѴLӼCB᫕F:کtq'%a]ѹ,{1ީs{IF,R]ɨn-4jLY$]L#H>gH`G(VK$QJlLCvPN4# v^T+2`#8̏VuB_of3D(!wjxȰl10 ` RvZ Uڠ ` ܠ4.ay>T!a & eU?h >= 0vPAt21#E p#9GJo]5g qLY%y:\e+rNx2_VA<zCB}(kcHyX!wa57 E&EnЈ 7am 8tRUF5 523 C;E}1'> 'GgA@ *BBv pI |z e 0 *ܐ .`=  r *#:0IhfPb}0t z * L̰+ M0vP1Sq$)*! o*! S< 8*E*0 G I  ^*`I/RA'`}0#ޥfPyA})Qe)~1X3/BNZ)Q*ufg!Cċ2PCTA3!C4ަs)D]3D2vaxqP2$6 [$V}pc4xs @pF8/5!& 1/kDHvEY, S*#@~Ѝ- >_ْ - WQ-)f2P]:1 % ڠВK V  :Z@=!ڒ Tvؠ4.DAӒG \٠ :* b3J&B?0\:*)aLSLoقu0vf`*0-Yܰ wP%s"r*QFcLDQn:+h}DRk`-(Ԏ.mk&!VJ{p' 0 0 @ ^T`"&NR&J{&i"'sR'&Űz@|G X|P2PQ=7~9ՠ =Ҡp >*`kL9C#Xv .=q`U`p#:ܤ+L K:K Vs (: * >ְ aڒ !ı"@-%*=@ #[Vڗa: z Fے'`L@ɄAQ "Z '2p- *$*L~і"IDȒWSnKSn093(YJ(*bR WvdžS351~Gn&h Ϻ8 &$ a9 ZppS|ɠ6 0 `9zCx^@:<=Q/ *& ).ԐC ` U!j$h f' +f *O eܠ O aR= ! f1PP0_`2AxP 7*`0aSY` ;i@!=DQ 3q r w *%@1pPdl7ۭ{`%VpН0 p7*  P"6*"FgW !pL暟*Y`SpSG'J Yqc a!J< *Zv *LAJrd{~DJR}E*< *4;PXp*`Q LE jAPI~n^M׍wQp^ (~i 9To-0 q+tkSIrG1tEҞ'XTw%_!M喱Z!#ے<>2f0)#y~@*# vVZQڠ5!Z ) ٘)^QBaoDB\CQ`C04g 5T !OCrrwkɆ0&+F'a`p q7CG780 |p#9 d14\CdkB#-E?bT@_1"DVa;  D#H;k )BՓwfBCH渙SEC)ඍiH"?~Ԑr%nt\~l$f8R,ɩ!*駦hfyj*B*uJKjVD"L:Ȩ ۢz#H%2"@|s-0`'_0h-d㫬 !Lh"Ծd=/aC !2PO*Yg?0qOQZ lD'F‘[`$ D" v^>cyaaFŮ#B%0rp C"paÈ= :3ɡ0o4 j;(&Ct#K2牄mIť!z&tĹ]1l+! @J,T-CҠ*\#F2i' k%4uLe٪F֙Շ zԧmP5W`/4^jl\ly[`X3YW m+A ҭ$(fhh0]"d%EKG P03 &XD@6opJnX܉oCˏ|D exExB狞?x8Ȃ: XqUT)}jHHGbf"9b/XtH!n }vs NU'a$Hawi1*?arDKLucvV>g/3ܤ|=,8KM.Hon8~h }ٴ j>H.D l FV kfO~hIc3P-._Ss%,Xvۂ*7lVx)'Spbl[yg>!`IKó[jI?D)2#"(6=Ӹ %*:v8Q!s q9fhsw#;HJp9@XY!T PVy7Ȋ2 s&# =1 ư:H8}a ;߸'Hnzh 7 s8'a- ۶=~XjC僨00q*RҳʳI+Lk ˍ|i@hddiHTvo9eRePA\@DBc{X]h{ȇʪ5_zVH*뀨kqВk;ᘺk4p;Xs l/"k0xɈKmh@'(*LPXy{K~ɘ '覈x#`0x9n(H1QЃ Yz tp4zDuCغ ((~=@ϑ>9s3JH3 w2@J,+w΀nX+[{*xk _ʆ$@߰p !;!hBxj*#jlkC?6Ù$It, *۸+lުDʣk5R3]@5X |Y QVU?ڵ_ۃ>kM$__#WxNF̉"q&r T Zܘ</=RT ,P*YN+i=I m94um*0хkC>] $ٟQ؂ YO8")dp:{?( Z|]%;$w5.[q3I#>E9}I6+PFsL?U!;sGŒ۫_*IX?R,,Piۅe%$gaeDY\˚YțF?`q"P̎IVTPR8W Pt}Y>ˠ--z= | =* w{}8-7c1ؒћ4n/Y ` ؀& },Q!ё(R8!^|_^pjYm]-S9#$Zv¯+.:KҶƨZF.϶W&٪Zݎݱ AV:|SUҨ q6‚c=Q,lH_@[UDLbus؅s`vykYPVQ3f1THleXCra+;/gj"/[SÍn!ԍaY|@&rr:Jjߗ^  !-c`~MQe!H@1>eXnߝW,_{~ a$Z%Ai a޻-+/i3`4 6FZ~T-'LU/ IT -.çaѓ=F,k(bx,ni9[%3*fxC)!N-c\PЎ.3=\!o!>}X(Q}شՉL;N>v6]>x)h+= G:Hf: ў@W\OR^!%""e5٢ю"^{%ݪ@^"Rei>9fvcE l~fp ?uI+B bJ ZHjm Mf@t`Bt-}&gJ(kYxkkxg@hfuWM1Ve=ϻ,Ȣwikxq\.6XJ6!ݗ;^[ݶ4 3e9.hWjvt'rn}賙N5 iɨ䵺'ʭ^_xR3eQ܊8^QL0 YY6qs\f|`ʃ]clYAH)Z6!MfoF-BbD -6LkKeQ=؉ ӒYWRͫU]YX徆uxwhn\rX4BtVZdg{أdpNRhH0C3EK]c4@<)Sލxii9PG;bv'o^z _ CeuXkXޓђqh pd=q3-1ee$g'Ggv4m>j'硴b_I ^s."v]ʬUâ6/nP't_Z@z_؅..VxS`NtZXф=l؅nu8ob[iSB\Zw"ynUw@xӏ>vvtxptetCpCp(!W]-%?]"*wO."h9wq9Yv@}_`*93 qӏ[>T}!Z|yMHRp0Ju 3&m+o p*ϝXlҝ{)y'yۍA˘uչ+X'W>ei O\z6fX5Wbh]cEw`SXb]8[f%WZ& Z*V\̓hkjAc:.Nhe,["] -P-R/Q/\s;[^:^L2شӎ4Pc/HI/`#|sgb34L4C'ŴBf+b $02W3Fg>h$rBfiZArc{I.9^xVE!e查>¦cz j أ[q]c^YFb*PAB|%ywƩל5ऺ[tXj: z޽|wZ;51#n뚠9kseVdEZxbr}Xo _O8:l.rj3 JbŤ )/sc/퐳K|;<&>$7ظ2Sr/M-hC4`OҜ- ;3=()(U\1\e[&.H*XM~?, i-0[l]]z\uk&ƍV^<ⶋXyA6ύp3:޺ '6iZhC>_ rK$Z2nX2_a͉Ƃ#mS몙[ D0)W/2H=k0INۖ%=jR{5q EA& Of TkH[TMr3i@CL|1 RQ *AR(3I]Dr84qEa TWO@RC]z8vbd@l'cik7< S-^*W{#T[~!SlQ1 k1/#X/}c,5Eˉv,cAa(*msaQA'$GZ,st:M1,T BST4Ά)\ZG1r¨ 5M/LQct7S2A jAdXҎh$$1hj$̼=c d!cϺ4聟^c+Xx>j6zםnS-akzyռT8Tִrq$ Q)VdY QZ,K"qTOFq,B!4dȱp#Aw`B^IѰ'.LZ*?+H|tEƢo$k'ZqtFd4INJ?I[ibg*FAv#-,ЈN42b`EI|$ %gp-6g$aSqd:s)f 3bhͷv7EvIN$!=2ݶ*MCz)=r]ZSԟs1*Үr^#Zbh[XT :z3Ƙ wFB'CPlE ],i$4! 7Xk/VаkpnDk I,@J/Y.0>\,">.eL}XLs<FWߗ-DmjPYE-/c׸jtɻyP5s&R:: kQA։v1h6ƽQǂ^! xUGi Y_(=K/,-cQ!ʥE2N65e-3*&1rQ֘9rfVD'%.IMovJ8 QJraij+.Ֆ=ڶYM*N&㒱*Hj0ዸ^lV1R+ q5)q[rMlap{ }SN9^iGPűސa}]0ԣJPFsjCuA~%CzUU.2#q'IŽq5ݟYd W3AB)3K@"4i4BOEQ}_DJVŜ_%)%mNwJ.6'l P9A'3p\-ZR zd9|XxLk|EqVXQGzhNUm19r]KbtϽ Fvp]OPmRTCw| :ޅ Eƍ?hg lFiP țhET7Xp _ԑ,`[ gtSdiDM,I2|rMC*!"(@D>aC9DJDP2/"RD>M2=.OQ@C2D+0U^QSyʀ`ѓEŋva$YFF_ԿYmr\E=̃8 I°UL\M۾OA0 RYL`w^舁 OY [) t0ہGjXVdcIaa$Nʬed&)XQJq`-Ȁ̏]^H1hHӉ±H-ӏcZHvEF x59əLe_OjOb.\褞M ىI;8__Y, b`4ҵW_.Ţ5X֍ A#TKt L\V \.S,YMheXY@a]7_Y#±Q!A"6Ώ7Ra 4Sx"_T^ m *0ڴC ڴg;LCܰ44C+JRKݑma=p| tԝcӅF]]ש]Dz[6.m~im7=elUoո<5LjVUTHR6tx{9 YfNxRE]"Θ@]di7+dXD4HIEb6,2@49`bh&|"- -$•E4hTDB[JJmLA3В⨛ƫԤ\I+g2B*y-Y><*8qgK4L ;.;˥'4=HJqNI$i7,Bn ?C %}j$7Bk@l򑋚^ݢkfC.3ot NDY *X^Rc`y,|EQ)Յ. ̈́oł@ԕ̯Um*C[f#X~y`p]>,AHv "br"537`P>DmYA*6B*3T;d.#ppYaM[EzYC6܀Jx-0W驔WP? ~4'yy5P'~ɵI9۴4'})|+>ѯXl]crCdR? R7v(.-(h:'X]@d\nqi2OuY`{rm4˒K)a4/-G8) KQ]i^]¸pԃlJFʮb/3@{7e<ل6xc'fbVQ ̔vio= 0]@ɤ?B o3 Nn?$r?s܂4\B/`m l  6MI18ߜ$܉ 44DB`W2_v҇tW-?xC:@'rt@y紒ZB$!QlbƸ1oޥx`B*OA9uc AN&z[) (ai,yg!;2Ws#CB6/(WbS?|]E.DʜgH_. 4@9W8p7:"6i*/>$w"$& _hLLȄD|I$,D;;JQ)CaY+Bs\-Yk!69x +B08\aty=C## #0a(Zh6& #tr{"x)5B09?-d-#xB5ؾ6CU$R?l#tC:PB#lxC4 2@C0lCG:l6X ~ B]|7 D'sԩO[A^Cq3ׯ_:ZYм˚in0PNk3AJ0` (͵LS߉lLeic4ޜLێ7iɕ+Or/Myg^zI&YGuZ/?_~vF_ 1fh.qDvyfsΙfiiR(rh-}rk5X"u<)(9ƪXG(r2skB& Lh;~iS7-' jQxPg>($M :ʜl8*a .XG|p--)"hl]"XQ(TI]Fs4h1Ǘ% du4V"젩,Df1(ќGcG a`is> XhFudQxv{w+7̝~X12!?RLGW%.Q#H3O;LRǝvEF#`SэJ}FG[}᧛狿kIna܃O>~qEo$Eu!lf PwyA|Iƕo,l|(_PezaŧI{RtvYQPH) "; 9qHqﻅr=stCL p,fPH͋4u5V*,j~;;] Ѵ0[10X+Q @&iʽxD}ܠ| `F1p.b"(r aJGD+iIKQw`* GFÄ\BRhvpL)|*SL-`2P!ƍBP\7X>)Q.8iRoϸ5auj9Y@F9֊_+L`J#p愙k8 ¦3R7 cF^D66G MC3Md#8pD,]{&LJV3BF N0P`8XE9i 3LlDf&2l w.UMЦ0 I'5$B'Yjj#i 4VQ~u4>EQxh#2ZA+2`60D Gnį^ `v0Lm϶( p_{< z U(dځ-fQY>hgP*&}#F[q4{LV.NN[ N_XD2kďp&ڹM8Dh&{JI?p{\; Ќ}a4`fHp4R[.HI>K+*pZfzcODcxUi `A~x ?%pCt`[H$ SǙ+v('p 87^N0(4i;,C"7JWt4^LJ/u7SXvj ģU>kQ'rN*`# 4j#cܔ&(}dxH3 0/avA?7bBP1|c䈱8x7(buH 6$II C1y9^ Te\"sZ4n Dl@48w&#h֪c, e:΍lJa#۰\o1RԔη ϣVeC?a"w*pq;E<ם!~KQ4W9, mpG^ԬG$|ϭ!^.*`wRc  e6d^HO[j;ӢK,a$8zM^A9k4H"!5 ~ :0m5lXC]`^!^! 6aP.kKSchH$l,I5N ?Pf>GDava?v!j)CAZC@E畦AiFA@A Î*hǏP 96xg8X#  $L.M"uFfB<V8(4)}T c{E "e*zM,~'ɪȡXVTȟ /=*wi MF`8o<$bZ:v-N03z$f70r(4 |d2J 6+  A΀%Mif!01jA]ԧiT֜4x/&R I0a (i/w̫Gh$G"$cşܐkl/0Aۺx=EljpƖ !>&0sn)P8B66aasLGA-(c8Șl,:Cf@J#G,J'{<%Q\hNMaPQܨ+zߢ ހSje(*<@@ q4M aܣ3>>3 CЄl1* !AJ 2v KM?'*A)>7 fksH>-+T>>`em`"P\l$=GlE/c#ڡ(bL >gtAmI:bCٴ.Mvft6FfH$ BFFՀ-!/}%84rzGPGm}>~AD@a\dh)1BrC?#jI(qAq^BL!Lky5 oδRr+X,"aTf=k&:K~9G #[,:{W( Xyh-3'GA2M(hv%hԅgBa.@>?n,qLAnAZ*ء|q!|Rx[HfKol+f&UhS3r"Ay#귖sSPN:,},ϕg baH-od N hGD L"4In"W VbA:"*ds&,įܡfCb_T6hjE[`lb2A]PFߌ36DaHвLn|4́ #ҡzVI0lJ8 L8a.eM 8jn!Ζbjmiz 8Ꮃ9Lce< ,0BXIR$Aoazp)tgATwOySrT1NVR-DۡBNWuUwF5ajd_6kg%ֈ hAZ20/j he"}}B~5'Lh57^)ANVd.{B<YDzPY!R!Ta`!>[1 >BhՕ@T{0B-[6g΍n&>lmk0)CXMm΁`'D] Ѯ0'T<վsʥ|)|G::SX&IVML|xF,IX5tJiB *eDgJB!4$|ve2n/);;fͼM>f_k#^%.h86Lm]XjݗubF%D>VX/7J}pGG^qsHCӷG(&6PP r`{!gd3.*SS2$r2P2 ءBNsnWGzL̠y=/œɰu3@bd$N$gh`fg46}2F*'zͳca.:eb`61m :. Ѥݿ~z ACgŠD vYTID|tRnN0Ds> ݵQkg+U6܇.F"4J,;~T%%*;vo!넛ZpV L-x5@ٳz8<@4 Y+CM0ܜDduquʗ~5Ciք3fOD6P(iZc8XJZbK\bqNhgÍ6t+PVjr^b%9YHns - ۝+VSa$*-Rɒ+ki&h:鐔~ G]) <ɍNJ"JD(n`.vq3n?vBsS=[Ɛ]{lzcoY([ws\Qkml8nӸo\8hu y\.?Twď9R?N]*M0ga4%cSH0 2nLuLW*9 wMtC&nz$And`=i."`a^chRx'()d8H"Wđdwɫ/6ez)Ldڔ*r'E^K} sg (t&Ci5vth#Z-ZP7v|C| PUkT=0LTCZjz_ %˅:\<<)QF_*dG:@,Z+j<6[ɏ\+ZO7SN*&dĥH혪v+QKmF?]qSLZ'd'g bȡ4R% 1ն64>|أŰ>Q ~(6H e(/ e<!9}|T,eԘC:y3UKP XLݖPg׊j64oJ8a ]5QlJ 7)R RXlbZI!,}ܱ\J4x2kYp4i",$Kin(nS-e,D|ZFQҐh}t#%ж+Pv>T'gv@c=ybB%( 6(>a ȻcSe[WvVVY6cSMn8cS9:UR]MMznk'}d/d (I}]Ē^ކhZ>%gQ21J»o> y͓ IVq\܂4Qll4V4f,rψ,)TkX*gE:'Q>G/ .@X2a8\fɳ+r7pu`Lf^!KQrezdX4{WNb!Kq{ A[0Űw"b2  I#dCJs ~ !U"$" ]0~Xx7xX?I18_jP%uUmTmeI+v' XmUĂwtӦe+/+zz%wmj*0sS'<(VCuXQX*(-,UD&8lRad5<񦊹h<6)e59Xˡ?*ϓJ>7F"y>IMn?1Uv7zoTzhSoTHOtׄRD-w2JqjvA P5/}4tOЅ93 Y3? Ұ ǙhÇA_3NqW5Q6Г)vᰍtxt4UPD>MpbR2~n9y2*d `juW`(/EPb1KhV &vru\tYԜCXh*Y&G7AsyQwct3b*3V-#n@Df؇A!4/'CI($rxr&g(b9 (A zh"^! DY¤eq*Wk’xo{D_+Y<9 %iZp%h90QG(F=#yaq<wٳLNmZr x{ x)`9_ڀ1rjiZW4KRlYRBYئfDhL%Xbʳc^|UYADd{fBg1OdgB^3 h"tg3 Ehn6Ss6 'IEj#桉xE>(R4='X+A/eZ7Ua.$z:ru$XGߩE c+6;ca;;.SUs+?j WX +n+fUjg"y8NIvE1iVnr#@vL.O"f3G es\[e Z5 e @5 Zl `s\@] C%]ٵ]>10)=(1(M8zM:;ӳGi4f+){JxUȸ92[Uxpx<%fwIRxz)x*~8בdǹ1XKX#dbå6iF>|wcuxSoM*;갏O$CO PA]0 g eBg֚ zh A%IA:X+^jIs?ViAcﲪۉE~BJpXnI[F,{)v42c(2ەӉ"&ȈJuXm⠧75kH_л094Sb#Of! S#T46,e$< KR=k.A}."! R'{ " .S!=j4p:GIu9Ǹ֐ck+*Y/z~NZ Ӡ\YB"m q 2erҁ}#Mh黸 XS<~oaD/-`q!:G#IZWB-7Mz4qBg?>Zqݟg )9z+Z*0Ab!N`) A~'P"-SGF7&LD'Z9Y̋9D<&LnHkS=4TA~)(&3-w[)` Ûw Rɜ-(A,.%|EiI%E$iHVlC|iK%],watElZr|)_HEov@Yܰ  &<DT L',9Ɍ=Zm@ q'h>Hg),ƭ>㐟8zXw}'|ңq -uXl$2R!N pך١2b6Gh8^⡸Ld(z7il-4/g%Hлp-c 22[-#GQlYjhf6l`[k&Rnp]ziz*c%2Y,Lܫjȝ([ꇎ+D}wDgkZ9@J&J8?ՆىЧyĎ~}O4.}-Dx9&5`quvjDUK"-1naG8B§EmCSC\'  T+A?n|mx)VV &(,_,&_ 9LAиbdjLщɨ"69|5Pj|A h~A'ZRX1lk!iaW? "(OCe>؂Q];:"/?`8J ܘv3̂'*Ġ)D_? P~ĨQ2e*O)C){# H:$aC\2@l(xE#b:דY4lġf xb*`1 hHJWhF>Dz`+mv*ӖTRh/\ טpt)HpAЄ28JW0:;XH`$`OWM4a8^bHEat"Od~;  ky'~A C GA >&?A DcI: mPj[ZfAܸ͠mq;)+Vb1A8{)„`v/56rEF8ɔ~JԱLlMD96Cu]zȋ 8`ЂZb֨&݂~?q]MV'8!x,L.>1D c':a[=8"`gU*>? 3~m4ē33CM`V# P|o[H,~[XwGy} 'P| AƊﳥrGRA xGB*N2$BB ,f,}'C4R|#/?&h$8GRn(H{6&p(@u0?g{۾sjy db 8AG국X h%Ї{ 6" ܖ{`gZ5kX0jZBc,9g]{?A( 'Q)|6/g1 ~H3xz@ )13v@#l(i?)iDS4i5 r[ڀңq!RC }0- YC #U~$n&7 h 6[ `Pl+5%(puh4-X&?x`h5 *#`F`lAP膺nBȅ Ď|4}A~xȃB\H=9?$QX(P!ǪJ;}H8hyIf"9z@(tkq\r4G@sJ{D|p Ü!.ܖ~GfLHO\DÅ -lpXDBPG`OxG$|+RM&p94x'Hx@ DB2hĎXDo8k"Q$#) W@0rWT#6vO":SM^$nEq7 Pmaķ>:I:wR8xqp .s_ 7x5k8HP@ =GL4 gzt+nnR=Rt} ! T%]QZڑn-u` & ,qӶ2yLPJ+3Qӵĉp1! MFzIX sX" ؑK@H+-lH6f.N _U MAȮaxh][@V@f \8I $ݒ~zјT7WpX :Z$a(p N7) Z 9bkgvp  Z H q h 6HՈ≯2&/e`$GhA>e$~'GH[~c&$9qF`napz3KaR4҅C((y{RX:|n`B)P/әxX06nxz@WA]әl#ڈ))bi(6,[sxMfKmexX`]4ITzPpd  wx]h@[f@JۺՈa ~ cr5& s:&QfV%`Nl8B6ͅTݙW Rh*K-z`?R!LD BlFxY3H RЕ_k{(b|xͩڠ W<œ^ Va\{[e8S0eP[I|6;VM+Zl;Z?JS805cμC) 3)u c[ LyioSZ7-h:CMʭ;^Tu;t\*93ݖ8;5ဈT7K6e&d ]TX8u{'}I5p#l%w;o+c;ky@!P8»kn$F5&KvT0G%0=9_M&x` %HpP>4luXYPxYȐV4]^ k HZ YW`}P ޣ ?9PŅ85C(Kl;x@ 5f fx6A]n,~%Uh[> dFޛPꞬI5챦VTT;8b49 8`HKeFl Plڡ`L]mllǎW9H9XMeyjT] z;>kh7g؅^'kŽ6kN8}e) -8B@&L@hP騆M[ L!ppf>;uxSyaǐT)D!2wpgְ=ڠ#0[ Z`(⨈жpCpM(XhƸ ! f!}GtFsP7 HM$NʿH \ZhSǮ1`7 fuƢ)ΞeƑ\XG(֦نj;s@p,F?0(@$nӎ 'жpc K$xZ !./[X֮ pF9-,s@nn! @$Lho}s\ "Ɖ-D!0Z:'Y9Pa%Di wg(01REKЩdh_(EhiIxŜ}E%8r-]PQKrpjat$BҤ;MڝQo+ȇ]3Q`0xP edg0RcfKK enX0tyqЈ豆zTb 4Csz8ᇺ;ȓx8 I&q/sNSSW釽OܖSOqH֙fStW_ˇ`f umK k 548U\u '|{^V~fB3y?Gf@w*_ep33稉2310o ~Kɕ+OG]i={nZT%=h>_ص d^zvq]uw%NZ\JjDaժa͊0xq +v,ٲf͚-^V>~ MhYgmpS}!knq:}8!T'nJu <9S:k>X(K5YK%,qSWOiuJ8VOݭ Vq#z@Mq3gVZ<.x_xp[̃VuOm!$6@qw|w7dX_Xr !b35_~t!vSh"O+FP5EBz`fsgbOA#}pGBR6:pVyfYY"E:du\gR `f_NM4o@bKZVA5C8t8v@ᗜ8&Qdy7@@4L9uȄ?m7(?XsWKDIXZ|r} -\XbWUlM^c'Hji³2̑J5vK QЮ|@w)P?DU&jĂ%9E, 3HWcUS^Xg S?f9D N=|1YL#,1 X$#F>zB¢ؐ/%BBcE2r |\Çp*\ WI!]ኧLKh$&K-#qTU/~*_, YԲU9a\?@5Nbƒ`6 ##hǨ񠭏~1ےN@u4 ;Do  =M9 *bdu*Mb DQ$O'vd!{rXizP`IׄCCycbd, P:|P#bg sFHv: iB& Ov#5z _F--ri.Q PL"hpX$UBVѝ10iK(30Z_äB-bAeX,5k;!O1Znufn A'& HfDCyBBe!#}AV ?ZTh  [䈣ᖱ+Ẃ !yo|c)s.zMcdH`uȂ卯(sь]E8=vGm[zP`Es0h}LqW>Taat c'ucĴ@ri-n"zG#Nƀc;SHqOZ.^vk?Ő V>Ng;[p3Pz! ዤ4hJ;Z0NV@\k]g\ 4MCaЬbCa x 0l _z І6rb*N`P0!]AcV DwMM [x-lm=WP>Gr`3Kl,c-<z+41< j#>i!W(K{ [TJ,;# Ә+ĴwFp5$KiF*R1 i\# ! C!E2b(;RǤ7o8h(R HyDM@NsHE~S抪 AewX bmᇔT瑶fUH|LߊG|$A_J[Zm.߀W 5\¡Y`ܕQZ_ZQW\@]òز_@h] .zI%ApSA\Ų r)X = ?RԤʕ хU`Ԫ Yi[`V!EQ,Z; ٞ3\;6`C/䙜OB/EZ.P 9ѣPH(˱! W$W)i .[BX ]aA,,A\[VX,F zU^q1Js-ܼ4Ӏ0[μ%μ=`F`Ϊp]2ʥ#+#aL Q z .:!/F,+b+}YB"La¤b1f@ӭ:UaEiYt_Չ Ѣ僽1DO4NP!NDP$,O:TTCE9W}/B.4XDL )b]}4O=L`Ju#FbEB2dA.G2^jXIEb? ley n? cnQE(V?[ht NDqz0L.(b\l崹&G:KZ?>ܸS *5 %[}`VL|ҕ6[_Zĸ!IEލ- ѥY㪔LR.PP]Q14CH /P .L:|g2Ћ)4QaCSB=Nea_$~ L ,W)WV!\:O̥dFj`qxgYF d=#@)$d(i(36%XL]2N("h[xcp"TqM2ڠc 3">L21nԲ^l%VZ`Y@!^$?*_I'WYHGJrp("yhM<>̴ѫѝ+*FZU-ByΠ>CAAPDR*49;`NO;MGhtDPHö˷(1)E-P@呃^ü܆&eFiwP) nګNyE-"͚t!"a)K*c~'L*y|[ْ˼~fĮ_*>i]Ǟn]& `)Hra]tZ=S+q& Zur扽2A`EP}EXDOHI +MM kD+|F)/D4@4a+HŵVVsb̓X-A-v|N;֬bJOl&e+WL.,_W6Ǘ&rH Pcqqf)FdrbE-l ~@lr*GjiWZ|n BTv%#R\=DAe_|D Z^lE &Cjfjd"V+d@PZ9*UeDKÁ 4eQ:şI$l"E`0$bD e"EUnf\0JG |.iAVam\iq/! t6C8pq,/ b nsq,lƦj {J;qx,bF*J0V0eǥ%)+аDЌ`jgKfV1~VMB2|N OPSd2PɪT-4)0fZ2*f u*.j5A վ=cJ29qE冏()ʰ/=n^]GJ L\u2`/E$B,^K}^yq?)3?r! <苾b,AtbZCV槁t`8P!R#%( !0JB,gև4=$~ 1QbE# :L.T(aWO2AQ+ Тե9oЪܶufsGOU/ڹsì=4&3  32vPkas sdljka4'g"J'-!eth6Qe^'`$Bqe6ZcJ^ HzeE!Y2|@M )=+zd'95u+NL )$/p1(O=;x1%(4TI5xM^*ط2)7D;؞2/ރB- -e",Yw? '!De ?b\KmŤ66*#߲!FvhgJ8@~8{JCg sr*hkœ?Vh[ޣ^(RH߸b2è3!ohù"l7L7C<zDw] *uA_nN!PR/aSĠ-&!&ôun2 VVϵCEU),fqWHC.t~5 s_.z*[[/ FEw6JRv|H ɝFZ2G[*K;9kQ& 2naJBy`X:vW6;oq4zQ04ֹ6rohir䮟t"~ߥK:}CT~á=L,=TFLPQ4̂R5h14@ ًD3޺YQs,?v4xov74RP%>;O},&7lp9&7翹*Eڔ|,tbLrs jWo# 5胾! 滃/}֊b#$[´XH`CMeF?L6xW1!xGdƤBDЋ T6,%@`ˇ헫_Zͺڳ]Ӓ5 ,X葓Hq/_ɦzZwP &׷_PRT2zDJjR?:qǍzEBܠ?zjvHx#j5uY+N89?9׫;mPb?~i=6AhNNogCnnя!_GgO[n`ۉ68AGxTk֨ϽmҟĘzXc ش@)p1JB#0 *ٌB*:uvǬ2.ū**kYx*X5n'ϱNk1fɟD~8L?Қ?b'0堗Evncjl)`%dzI;Y O=ib)u v :1=h3!gQ@(uK*)b8Qє2R5RX0)K3:l*W:lO#TG]-<g6o{~vS_** 8d3J.BަR^ϊEidi5J[ۉ)MW++ r3'(ռ6)?Jc0rCQͮ;LI j.,JUn2UAz@Gq1(2JKLY1gˠQBՒK/eLH1Ml-4:sdGw9gfYZrŕoIfPHkmӣC5* :N1~ɝcASJ ?. 'N#>D KFi EVx@TIE2:T\iOVk`DAؖ^N 賆}XtehSefģea(91pKG-DYŊW"!`.-`Qmze* ՌT!(_,0Sd CG5XʢV<Ƶ=(lb4?.Ë`J#f TQB;مY 6V hQX 83oTQ}j)|rJ!)Ɏ^"Dg-z>.vyp!<IryTseN ]4!m-Dizζtz2jVZ H%H{={QM9k Vf$mu }Yf?f*WBCؒW- E#[dF:B5/?H,{@=~|+/.ڡDPc$ F+Z! PiS2i+(.rxpCv*zb$. xE0\klp2c&G`Le` 5lvI #^!ZkcaB2('.)Jö}֐K~iPA 8U, kbyҽ#n*5VG(@`^g ߑxY#+HIЈ)a4!M0Dg(C27_kpl"=E!) laGa EA!0a Ц.R1pf5_MaAC+!!cF"m(A"G"2Afꁣa?4$(3j*mV (\ sl ,fǾPbl̬Ίᨠ!ɜȍn \L&$N dhJ,jH`GhZJ 7k|@\@!`| V;z& >, L"! LQ*A/E%ML\-*f\ -WƧ<-, * Baqd!F.q[  i KA(a[Nb 2\@~.W1 1WI0@ ah0\R}@2f!`[ `g 8ig$@AZ1;!|g|2R .r[0zFNa>,)@b Ȁa^Da楷a"G\ cr?kd (fD$rit>c:3k̶$ҐM? (:Ť%s`LXB hq54SCsLarIh ;ҩ@&RPV `[@ fL !Fc @̅M;s2^FWJ @ ²@Z_!%O[) a]}He.a` 6.E Yc* C)t[@ \ @ U_da>\ "![%؀FvϾ2pRcR M>y g|&5n+xbYwuBA__4pa[ Ƞ?L~ 6`| eGnnE <"53b\́N*g `q[fjQFv[@a|v `e._" bq (X O_!6k@nѱd5CDt):j(fQ&mLnG4C3q %q%a()P&2ѨR4ED*_<2)VH;O7iY#C'*\4ڑ/`AQdd5ȕ5 :A^ @CD+ڱ"$kK6d2F1/VA1uVr1Tl)x[xT6 f܂ix(,m<1 Ҷ"k ̅8bFك8jro;+D+PW⋥X<*I3AAu\j9&b0ypErvMG": v*36-o|A MAPbQƩ(N޷SP`}ef%~MAU5r)G4 ;V4l%վh+ 9ğ4L#$x֜pLS$w[|?(A##,%òjxJ}`Ej%FUaDH.wࢬc=ma˙0LaÔ3!r-^9zYM{k5ſ0? g-ApnMl d J"lǸzLr }U˕\IÆISN"#J CWϚy䦎:q,IS&7k$oƎgnD.<Ɣi co e9eqпPUW܊+Wܜu*!!YJ>-۴ YFյC̦Lv9.wO~ ɊksGn/ }rchy+@Y,v D1^?JS?,v!KDK3.'#. H?o'Fep qͨcw\΀e<?޼MX@l}'XZ!6u^Z! v@t;1YSaZ:?hvHh҇'#aBO:aO|da3=Qh_1FZbFSLO;4@/27/J4ҼSL1c;LJ*L'Ʉ4K;"螭)J+fQPQ"O3]Ҹr1ܰDRbL\YIAڑ$IF~tp|T"R".Ba b$e:P<&9&lkg!CF>' z(P_!ELT.q>X$#HMwӜ ~^B_ؐ$J?;J'Ey#В J0#utΓI:tk}d[Pl I a8s"X !X2U63HYS ~d;BbI1wuAZg4A1D@\_CLZ Q@V`!"$~@+6-"xBf4la*Jq/HQ|̒LJ `4bH-lM 0 @ q`LhI $L4qZhil9F>1^[`ۄȒ Lլ !vq,T$]v[#)l)!WnĠeSOm)mSGJcШ4 T0EIMъւ EK)_\,`"kns2ޜBj˦p7neR c* ë^dSI\:ntuIU#J@9k Q{B~C?+TL,[j*d8*k 􊤀Fï }\I4xz0lWDq`5=G5em$F>$8ch3BH$LŸ3OXǼn Mk :Vp' I S$2Am \Z<0&bc Iܱm~D3Fb`Dƺ>qkalLdzDsFi;I-SqQiR`EgҢ(A>EPNF/RыTlw419!d*HrRwxDr2+=b%.:i99ņpD4wpVbBb\i&y({?>W :&.#a}Kȩ#>Xd 31Roe 0]G}P^2sV|:6 V$I$j `ģ;h!/ g 2uc s";CW".iA2ƠcHMT-QNBH%3!/1;5Uq8z~C| (#_"N y !p3EuMv'}Rvu3 Q uZQ SR3ekv8vX*P @ cTT%w ~xMU@6,3 .#Sh4$E,Dx@A; Ma8N0d ;Q @  v&;  M7t9Ua pط$LL!  ኯ@ t @ ѐztnԠ;Bq  6&)qZ̴N8h @S(j&d7ҍ@t 0 Ah&cT0M];V`[Ay3 5(Q@!,@ 2I,EnTnreOg0XtT&SdSb&QgQQ=Q _ C ( g dfflfC>pfPӘ&g]&0 L5Zp)ĪiFja7\w MI8RZlʮjyu w ӱPSS=f<ç>>P S#>=eT%;=CRX ? 2H3+6+א Q =g3kFkŒm,&əT˷RH&YDHLPl}Զ .y' j} hۜtЩ 6³`PFRP5\ -U 9(:T:YF g!ƹMkrZ `HVut7bLA~ @vBZ*5 e <-m5+0 0 eW>ҰQ0 ϱ)5ߖ9 )zfhJ>} '׀4 ezVRuy nK^4ly8j]h$& :A<Hłw0H EA3^OΉyKCiOd7߾4!.8Eճ&Z=~In\BBpa%)L o(b*H`++1BE  1ȣɰPBRCߚl*+/rǫ[o2Y1 ?8DIkoc()T\1#FJKG"6lifJ/mo1NvKTZ.__\ 㺫miw։m^v\n#Tl~qT=tҼGH2I*ϥp+:[2}**9bl7I,à(D(Gj=rTOF2r1BFq-rOdp;_Ap}4^Zȋw_.ff vH9"{Yguagbɧvvەc#e1e:|dfy{d>Ifi{cjadF(YGG~܊\zWA}A7\YeyO&wq= *:!4f^ 22D{2>+u(4gIDӧjW~-kj\2x(Dӝn[# :FO[^r!3DǦuFҠ,us\C6ةXJ8HamrQ^:!Zo0UYQ!|2MS^:o)O>%u)EДL/kd -&0,PiݱG]123៛5 1HR),[x/]1H"q;,FS{BŸdhV购HlOp9_ǐ`GhC4q;Q8jszj8Oq|f7ݑ , Xjցq6X 4 OeN@D .ۻ`Z 6ueWd;B{$%SI}&Y &0LG?RX&-rsyx_Kx!όLbf3W<#J<+/%tK+xȢ=E'EY ,;SFR3^fjF g3ϐF>zW`*B;:+ZOf*`1 hpL1M_D"<3*clb< Eϕh*J]iCH4#cJRudeL9~fGLuWѭ$ơ$IJh[}A(+!6$hKhQ-$lZ߄gтX?GiZAdI+P!,ql7)lS#7ыT⛰N ׺j-=ڗq/ >s.ޢ?5^GiuaCSRcI#Cs]<[N-??neb6jYOY:Fd:sXc%![ $\Z._ 1Gj])*,w?<62mrc|X'7&a[c@*W `9q#{q51rځ*ܠ6l`818gql|)@9*[^gF( a;W=$- 0rq&5m$$%iF Z[k> _-Gtb8YX( mɋZ.#2x#>e|٭n,zwQ!@yIBFZ9<̺UM>WSb7$U d%l|AW۴F+*kdKSiv]Nރ^ͥe*xIkVxu(u0̺!=E b5hsJ8m]$~")"+;fG+#}G7Յ:.$ .)*"tc ;) vxca?zh WX; i'Z!+֐6c1;ኇt@h<](A/<+B ڱ%+ `4pX93MA5Z0Xm@A.M#~XCp g/HBBTG{q>0\2 U[ -<:A"ٖQ; [!֐"i؅0Qٔ T rs+Y_P"ElKpO[A&{A:(Gs,G dzûxB(G*pKwa4r ۙABڊ ">7Cx!lr(Xf K۫H7ۣpME68{1/ur `4#$QJ -i6Ե BD8 AZzM\[G< -wXJ,Cr_2wv ^dr\|`h8|ɛ_oSrrpSyiHƚḋoS<%yY91-&ʄsL@˨ $EG s2"D6s3; - >z(;xG)HAD눋2qDz N(z̀t s@I P3MJS*N+tAtI=Qz*9s( ⑿,3H.-,Q59tШQ*VH+&K MiP OQ i8:\`PZءf܅MEc1e9d %ĞN,7q)̊LpJ0sĀ߻,sIy*p~) uTB`-ܸr5؈:DN4$ ?N@|Ȏ @NІW(4"Sp O< I.'L4߼ŻعC7L*6Ȝ 6d9?Cx$ОLni d{xP!a:b?ThjV e|`r]1 ^ h\0ZPUdC˨U%s%j&=臐3I!-R"L/[3]IT(X)~1z$Ȏx L P@B! & p2,I+Ԭk#zSP X򒖵QR>%8`*F<u(JMA :1$yըמ/|r%VXj/u:veֹ\5x%Rxu"Ѱa 3kC "tenKbhKai2qL/Ŀ\ts pP MЍIM0|IKMG`ЙM r̂5 [bS4Fm>n! xApn F.FnxAZj 2nc\ mcMh MC؄>-`axU [AF$ ㄀z0MAȃtNXI^eFV(}Wh`Ȇ"x c[3ZrD1[]H<~*b]_\SS' Q]_ zHiS9 !}e_LG:Y7r /~Ѓ4G-Nj.G/jȇ,-GH>G%8G n05HL8kp0X,@C>G; 801lks40iQ=VX.yrFDj)SrTf`f:O-M]KZ\+nҢiO!rZsפ%)/2Pd_5{& #Y`+W(Ը:;¤`1AnV)\g|g9!j|'U:ha2Uz1]Hi80,<5aR R0P 1(jP 3~?5 (h;ͩL(TursT~:yX-@k@pxrϮd-lgh}$^x;;8ȇHӞBi2H2J{4P%i q&]5ʆw斠Ij0U1^5<#Ynv~Vi oeаPkǩ+ &Dlp L'W\yuo瀎kwʑ@$Sx"Gp@`aRN*!0j`xQpHdrt>0?p8jX&8GX,$E$0.G&(-[xS~sl>x)hx`Rx?x&or4CXN*zMyU‘r'N 4M@s}wxbGNCX(Px(H;/9)&=&eޑݼ- T7%FtTC/AY !3{ 8%|%dk`JP rks!ݰlo!rbi[qu!_8"J[V_u+ ntN' 7U x r|K ,pNH-P|;/u6.EXkHs@zN~ PQA'` u bƍ۶6{C x}F, $К r@gDTQ(E}B;U@ ~Ư߸aq >VWk= 8kLpǯgA7DOWnml_$= ;:/K;B,*)M;`154SL1N1I=$Ȃ{x)qÉ7tZ%Q -]?ԉ T[])<ZEZa$O^ЌryPa0[CpbhD7af7@kpp:?p%E< $ZTHQUSͮԀ:YZ]V93-) 0ळbP2U$EEV^=UL }cmN$5+<q#TdFQzg]ę#&jԁtڵ&T3%*y[G֕p9\_} _pV1 2K+K+\s4# 33Ӭv[" =p56+KRwYӓL1׬׼WS (<%j^p,ǏDܲhp'kRu [r9?[~bLGRvppRAE Pb[`dMGE`M?4*l =S.-fl`E::Opcn;p5( Btf#aWV; 8I5ǿX(T.a`Ms$mg{pv']8:9Gd!?Lf#sa#{\CJH3lg<z2)цJb@ci 7|VHjPߤl`3fi/v9Jh6 SPtS+~ z'; o& Ib|WB#"]BbTp4yh~ 5n)H:t!:bOC @S0lȼ"`!W?XQz x4C삚o9AdYgA&,`m! 'p'*Px .XBjyizfBu?V@2iB pf9 0N]ekqJ%^L b '1\g#st#9HjApx+I!8p* mŀ1 8-o{;cLZK01UJQ|0ʖ NǼ* iOfI=3&)"R"v8;֑XJ.hz,+͵oȅ/Fuc5z! I`ݼf ,rI=|lG:ť9~S/ LA&EPw15U)1j,EuWApK/𽔹K`R389,<%Ȑ(UO1c17 %/iEcW<ᣂň2׍Oz ,ʥ,3!n(qc n/C"׼0z~tL׻.xj'jc1ANQ[ט]\MU<܄ 1uHݐ]QUNG zHY4Ḯ_.@|4|b984\P$p l+($-fB9 i]Cj N*,&A!qpLxnG) R?Pk4N PA)52=?DqGD @B!4Ʀa۸-4\6p @ 7<-&9? ~0CX<PcAhT  T@ D!p lC*|؁94HE@t@D&89$2lT8  Q^HdZ E8ݠP%bM5 2LZ]l O8oYϠGZ-nIz< C\B2`M] 4bQc:\֠|/8B-/XbR(uLC2.kA G uY"Y\Ȝqa?*XK),r\s[ev  F31@3: ĆY7Dvϩ@PU _=py!&݄4cD+6De@'P&or3[lDq $e<LUXgx U}i r#>IJKdWU$ =Ta$،T]|gO^RZH U1f/%+:B2|a*&^v`^Qȩy !vll0P0uVV֚  mDakì6-7-C 6`-5$gÈ<Ђ'M,P šrU\%dpkkT!,4&4L(4$)e+tf%Y>H%,@l2!^$\vMRLd+ ajpa]EF=(3zioxjGư--H1K9'{0:s83qN`50=4{".386nU|c 6t L.~u^"uV4t["WW0z$&MYt)"}C+He2;h/a1:.CfMNMRHSS^]B*-r2P$+rbρ5u. 2)%lOq  ثz> O4Gr6d߳ǁƳN mv0 &)+/xvtxR8>n'N~ܐ5t .]`{nx_naTі۹%EM7Tucrv b}xMCyMeYv ]2|%}S'B+p~@0ov˕x$B4 -x;.'+z5ʰfo9묳86WDd[ǎUsslts oGך 'my:uUصR]y"S IHRFT6T44=TB1ȑ4@M%YBHZB_UeaTW5b,)pGXReι;9xHuǸy.dfk'i[̅s̥JVے8`Ko6cmPЌUR`%)Ǽ>Æ][;v!~ª<4#C^$|T,`Ց'4CְCށ(䂥m͌d+--(8Un+^#-W]ڳuͻ* : K f=vkLv`W-! jof(;=CYɬ;0?vP˴U gQ.ݐ[t\6Y3Xo ?sbć6\ cFn vDH]ǃI/!?+QGƌͳQݟ'S("Ud|UV&֪صvIcwnڳiM&m4iȉ6Z\ՒZWڕl׬Y]V BJ!Hңc\r%KqXfhѣIkG1~-rάs'?u2Am_g #&Pg7nNnp㧻:Aֱg.9 ~1ʈ3h~9p >$nk%<^m3~ '#s棎Mdꡍ2B&M#OͶ$^cm~O&lFtɧVƒi衧P_]Z'v/vYguǗ^li!35!e1oo|I^*{FPZ)BF1n$3̰ Sz$=sTn@TTRo"$FN(1[WOAH $kz:FHڍ;GNjjХ y2GkI#/Ɛ `/D}hs1rGo-,1Y, WQf[Ed_\T`OS_.Vg-8C0GU& /nYm`{-ɜ޸?={(:[cx/}Xn?YάY'SZ ̂(;4g`ʒƂ|H(Z;\ T僿 UTV#kx;g\(@07H6CHImBI^z5<7Rb%8D6~ǭK&oԁ(,qKڕvB%q d%< pdYJD‘H -]иklf3&%"C 5!>r%p&utT6z10QId`igՠmqB%Ico2_) ʟъi0M&ǂr}wPt}%WL1ǩ R@y<4ráMT%`"huL0!hv->¿;5EkZ:ƪᲪܕ9TV|Uᬏꆩ3)S<ǖ40?ԡ.9] B!|;J-b3 нA3AJ2 ÀQ)lgL+䜆9uޓoɇ4 SHJ4z좹}a*6?v!SI1[ϲiG:@m_&nگx#'[%kH.t׺8 3kqT#v#\O"ڣvN; Qgf=ցp[Tֱ9&dMW12Gg'Q&:"̽0ۛ?D7Ֆ`< L̮-p;Qf)EAo~/\^#E ZG_:i#IUA|=Xu+zF0TkUT2hǪOD~,X0XȺdhWdg>bpjmT^)`{I0GCDI vB%`Pt离&1fiC!VoNEo]ڧլ;U AÅT(!h"fn)*F+aBOj.,fj-,BkfRA&AuZf:0b! .FP"*`@zP_Tn%uL>XR"OS(#$' Qjp?pjG!|g[/Jc sM<,ʐ2hcC5DiJW&G.qBsA棳⑔^2Ɯ@AjAʹpC6VK#v7*vpB,Ɛ 5-^O%`R+(4fXHjOMFf,jQ(x~aVffRp߲ &"O(b/K7L zNT |* &V&>¦1Xn^P/u0LTjp2n1VJL#žzHn†KT%b.$c eܰ=:@az T?$tS ])a3b$WqE2F]ASPԔN 91`aN 䴚"@a(,H.Hb*~|ajVf.ϦjF0aĪJica?;m#"AVRt^u_܆6uHh,4~ .3nXE3] /+puXvCpFM A]13,S[iq#T~B1᳚0%:BIE_bI.D4U62N5A R+i4G ^@x7,A"KJzAJ"j|zo~!{d,0.XQ`a}%}WSK>mSAA]nnpzqnNQj_R_n6\`O@VS8JczU.ts.s\CA"Đ=zلu994Gma q/"%M_+ݴ sKvTAP~:1(LOE|iRK*w~Ԕtb(ZycFJzf hv!IHlj.IdNJ\N&hVaY n&?gS^$4KP8/6Q;AjQPU89-ETbI(hf\!аAiƀa~AᎩfvid.|+jnvah/5SwvmWzB`˰JÓp: ~8\iz|HEl^6YVJzg#X9 V0wCCT;TWx0#!t]s_dqp6vpCLwtYUS BaE!box2d0p*l)&Qx)3(f/++jNA#qi=.Fhh3yEE*'b9Hmh.r gH\jS٦A&6Hצ]فZ J!2ܽ/EbG&DsWf1K[ f8y-Pol1xTb^Icl!;C"D<49rvG8d;|*t*f!I>y(@g$KvaL$D&A$NvÀrM|!Ma0AQ:׷́veEc,Y8[30ٕPTI#O^jn~B Q5S ]{:Xf֩](FB$}C1yg_FE 8Mo%^zϝI$)8}סEr̯.HXVDj!ɇO(Fw܀n&(/S|+ޢ+hr2jBų!:^=E9́m8&#^' 8IžbQ s>IiuYAG\ZGR2՘Cl;(Ğ ̧F`56)ėWw-w  PTnsT*+۟KDZ< wkO,`Z%p}ޥ5)Q͋g'XfB4rz嫕_Z%SB\ͺ5iƎs]k'aCIo/W\Bo~ ,L@;jn?nL2N5T?s\>fV;6X~7.ZU?uܠWOӨS70y| W8^Oطr/޿};:wbպN9Dֻ6-hөϲVZfSsGQM]pké﵎Yjwxm>X{굢cKslv)ϻ7jU U~S_M]O?t_<❧ZR)9cU=_UQv]Xuj+|}T%6S̳UQSL$$6 ,0,H E̲N2Ñ@ 3%M#MJ( 6e+4J+*M9bO'b[Uep65VzwckolcSi}J؁LVχw5h)giiiVMhkբzn`TH-'wFuj+luxk3Y2Ut^__c; cB؆U*lkxڡheZ&(!Sራz ptЫ?Qڗ~!E&#A57 $A%J2M:/l33s.i44f֒O)H4('Oi,jhJlpifAZAHW}7ũuqT%x`aE%e70jUZm˝[b|mYWlqΊ%}.XJw`j+bYxج0BتsCVXVeG&V+v?'Tff6l"6u?;zf#7çLMQ; "3P8:YB Rd #? Њvv$%+IƜ_Dm&Ԯ2cHE׾( &>1ST 5R77`j?B`r)dGc}A|$x'v &0WR_Ԭ8{rw`g#.EҐ=Zǯu;+0ͤP+k$Aǧ~ȷ 8㜨1̡&Rj*}H/QP(5~tٳ iJ.>eDm_|*)!J_Zh"p/@qI$b4 z%jR<QOY<zD!tL eeO^hD3˸X>UILԱsT{qda9%=gU~.i"m%[nC\a>!Zh /LpC5cz+KGVkь?oS^LW (\1B^G ٻB,GBT!:%^,Ûz$%Hlcy^Sʣ[5fx-\Cz5ŗCHH2 _8JAWq\wЬ"!5IdN(MƉr$&'>(цm)"bQU= XzW&G+#| 䭺`ced'|rJ*p#i_#HZG>^3d+qé}F'>/w4m<^M;/2QGQv/<9zåa슢KV jt^Rs7ڝk#B!]K_i`ю$MSrZ %LZ")\ U2WCdwSPiUXm?hS㗩ld9 m BM'ޝ?R!4Q{F7tPSF4׳>ʓ-zjzl-שGB͍%$E+6 ,9^JQ;kZ 9Ş)Ѡ q{]l- `r8oVc]Mi5Cx}-Y(@#aub`ήix;6 eȇ+,RSh Eaz}eMO߂WuM#m}( 2Gظ']Rw5ٜj#=JuKzz:h:҅WBohZEbTE-0gd7hkW|IBK.e*%LxBQ]q^Odu׼e.xGG2#' h#>yٓb2UJHY(d#/a5<0|M+&ĦZrfL\ HE;S/\4N)^R9!:]֝GOfVaAdDV8iD.D`Ke+1EllX4`֠w0Sa,` 9KU`a\~ԁ+EPn{rP/7y,M4bgIF nb65kuO{qcyeVKRbbT?l /UKIXq 1eAAX5%Yp }cvZΰ}i ڔ  5v'3t[|gq5.aQt73͵p-;:njCgKu% QDj"fvFTa6MvDv+8RV-EeQF2>#"j Tx+(;vU4TR7mUG82biGdjX:/(Jt)R`$"$Lg7T>Fnj [y!u$qL3 3 WL$Z2߷5&Md& -AQc nY&^sNBX$oc9f{O?O8Xwo2"YEdN!<"bw(SvNeЅVwSiU!E2&-syJEQVhY)^!rccI>#!2< A:} )X"luCnTzKzWS(b1DQ2s.(#$cAJ ؠM )L @t@Y }x&lA&`A w Gw∑'9I5p[(WM!O+1EhN=oGmh@Ft p΃b ]7=Ԁж*V -^kz9XbS:#7h+vr-$iK*A|aUy\_ ]n);n8&!/F(Hm36\=Q7u9VoX 5$3 8̈́Z"A` s  &4k p[Ba#C&"smfu7HHe@4?Dfd$Z\Xʩ j)|:&D,թ;mT l;=B"nkXӟåCFGj+}6(t>gcvGWO!Lqa\/-`(vyFWfpY$MrYL#$MP 40&+9fefB'.!'.fP Ƭ=!P\oÔoi6i 8`S)J] tl겂LHdwD ;h^-=˗yDiYm#E^U1x(7[A:>ΡZaWV1tG{uG6(C`Q2$J?JpvƮ!$wYyY0 0']!Qf+A@~.1%~pBxPhyEiGɞMIy}DuX$QPԳ۩GXRΒ:3ˏsEXi %Pe m<2XGyaDeQa:y +Un"֧5[1$!q>{\7TKQ~yYufg ѿ'}HreKferjdv$a&jF'S nR &~ ~@2c\kJD){&! *HJ:8a(!3uzhl+%2 X2FЂڊ;9Q9î9WeU*BN;[VGK!VNJOHD4ƴ*MȄa4.֭2\X `3>qq@ L@&P -gs#Wr p*q&rds:gN p 5Y6W%9(rR3 {U4iTU?xTQvR<̜z-/4cExig yz\Ֆeyeǧb; ›B{i(/vr>Af~aP U meg,7 [և  #dnLsQd)hh-ǐW/hzE<3c*܏\K>7Ƭ*JK;a[ Xn ϢYY#M j̤W3^ߜL ZeBA#4` eB 4fЮP PU! pazdk{`1 =q5DۘxͻV *UmΕ0r>K{<|d-{-6E"ST =ŜƇ21c9-N#] \@$] eι=CZ]FAMwyZOA0 Ǿ9ZG ág=Ya:O< 0&murhFSGEJm:R{xcxƲhμ1=aNǶtSޒhƗ. y(5^OМ%>W+U al:Yqt~@ZR B4dB3=1N>A cioC\mu#E/ )ն 9\E 6G!;a{"o" L"ml^,*uHnaφyD$J Ys!^ [%;L5\Za2 H ] ٔs^51% ,NWaaD"+")vG]J"<"LN̜JpEE cWzz9[Q]_=T-)z)q1>g)nߓ,?ִ;,*4iӢ Iؐ`Hlz|d/ ~ !1%1[а ɠMЮ5>XƄ߿}渁PBl\⿌R?~%M7r_~)tQL : tС,Yt4\͈/ʓUQQ]V7 Jjա*Y[*ѩS&-iT*Ѱu{pN<9QۨeEˏ]oTQ+ECԙPQʝ-k?s Dlu#^n5ɔ)J$kWVɀ 'nʑe<ٵvIvܴg]VoVݗZj?)HD!8˝? @kȜy,2NKHhb)/SGpx(Ċ(y -ZK2[6 Y0;.D"Q"Bk)-ƵfJ1."Q~kÿb)ͪP|LSa)̅޼@ E&6jSvj,ZrKt7^P|P~FRJSM+4IA}v|i{vم{i5||ESۑhQR~QhI&akO΅迿'Of#@ƴuI K< eJ(UjE%L/Q! Ң~A͵2q,)% 2b zKʹ  Q9E70&i%ȵ ́PSb2!sϙ^>ODJA ɪk~+fV+jٮufioʮNcLazm\@d-g ;#!1>Bn)uiuc̋ "64 f GaZ+g(%$e=!B}Gst`OCAW>äo΢mͶXV-p"D;ݖe~)nZoE84fqk;;`4P Z+\BmiE-1R@g4 Wf \ 'JATfSFr#]s QXvD|lL3fSJ7EI Y`p4~H}P:Hq%uwB+)F 5JZ% eOT_ Et~qd:q]ISA%0c`8dbe~2~[ Jǘo[ OmN܅"0&iD9:k$|I4aafzn=V'9!Lؤo6PL$:QU`_67­o5lX0 bs#Mō6wq^6(6vE%vt@*J֏B='iub8UXӐ*UUb]*X T"m8lh9'gn \r1N?4M#='~#%ZBGñam8Q+ld5!l̢]$WN9=xq R?i@~NuQ<`xKi0xsZp\SfKxC0_o0%nt~q6jb6L,Y'/ͱk&^?z[.\5![Ǭѕt {yE2FeHgXs4f5ҳ;C 6ƃwFӰPqGdPlӣap<8{H)WbϜ"A5Դ{-4.nJc% 7!WX]<7Y_+ hk]}c_g:wI0>>#[Cc K0j0gl; =JQ+4 6cd_(wPO!&O "8vȕdWمu|[&ePS0ثIR[}ӏ>R4qk T#@x< } ?/ u5 :z#upz2=)&:a -5CcãCv@b04኿J)S+.!i lQ21+77j#&7!I{ۅa @/[눎ի˅Қ(C TB2-j v ㋱_D"i$ӜV+xh,+ )pw 2,i+)lB{R沵y# qp,q(";,cH̑z|zl>7j on 2wCp Gzj2Ԝ2:J\Śʠ i(R8ZilA*8{spbiJZ9z2R(0&wj~Y;júj7KvBZK0NAuXBUi3I_ ZƔb@|h^8iVa{\F|x^(v QFh0<њӓU cs( ]x̫hL'sb8V:,=b/dIQrۣGb( q8c[iRS6§>ΫjyܢM͛xXINٻI 'RkFԃe[f0;h닲G}JOkRQDw8\Ӑᭉx*VM  kg?mæAhSu`ߓvd^GForuB6rK V?ܧ*LMЩ\v.MF a"zv@TQxco<ӂ`\NW_F]8xY2Hkp,olC&v#]Dx˙V-FM YV}#&>+{_$5 x_Hh{uf.ȇUgKR/<<gw#p "Lp!ÆB(`'S3)L-5$3F  8h*>$.%!.& .6.R)/1Rx33Bj8=C9k/:h;U<:'?U$@' @8AAILAsFS`JL~0MRO?.Pi.Q R<S#S~ATpW#WP?Y7[)^js^_R_k&d5'eii:lA4l8l:npSqonsm(tukTvM@z}=}YN~# @V4GgZl#>ቯ͋kE{tqg~+=.c>J5홙Mj#;{?RS{ߧaB4m4~+D>chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2012XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"'H*\ȰÇ#JHŋ3jȱǏ CII\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴiρz DիXjU8`Ê kسhӪ]˶۷pʝKݻx˷߿ Lva[qclE^ ycʑ-WƌYy6gn޺yشǺװc˞M۸sͻ Nȓ+_μ9I m:wYLJ:v{Ͼu/?-6o]=|g 5(h& "xKtUWwgN]iyf!w觢 C 2 (4h8csBE;@fd= E~7dB"ydD>ydA޷~h#02ʗ|dihf=J4S}9{vYz'Y'~Wf)(˗z蚐F*餔VjmF(咛:i$zP_=peͫvY˥j뭸*iԑ%vxb:H6ª,DcN6~M3hs1k'}V "2]R ֕s66Zi1,0,3iqc|;ٳH11ڈ]w#,Z( 3G-TW z_(HsNw<\tmݴb-" ,l~+66$h쐶mxWnhnT9LNҐV'K 1n#Ws߉{'|Rv9{9h8M6 4-w~.:{JS·ㇺ6δ{oۭw{7KQ~Vủ:iRƯǥ,rV&H . k26SѣƯ8.l#[m%7Y0avw:' g4feٓ20g<MD>Q![41OKbH2*[ɳλF4R!XH0 drXqRWHnHa '9 nT1ʆNOJ̋bi򕰌%k(5/~K; h@:=m+XoԦ*Ll䆢 Iu묇 '85mtX-fg)iMkR +J׺a6T/{G~ej acz΃1cUYAck+ZAw.ufdGKAo \}]ZI?]PNٸdѣl' FEmsZA 9R,&MeLaZDzT xut;j#zވ dEYwaiT%T:#?P?yKѸCAP:֞SNM-dCX,tgKBvzܖʜ4 #n?OpAKCJ? o/|Lgo Ղ ̳mtC }\>s/|82Fx{K~ G}w{b2#qdm7fDBTff(f,_`|B#h0ǀ Wp4}$4x6/у,(4Rg6d%%fq=Q&LV(qYXUeqŁ'h|G1?Ӄ_a w|8@_wK ǧ7uxynh4 IgJ4#%ZRr*o#hrjb|Ky׆G`   h pz~NX(xLj3∷&!ek~_N=~gVF|TИx{Kh0P P ȏ@Gt' ؏X  Y،?h(#(T*[eTL{OzKz(i`7a{8 ЏۀHP ɐPO>@)DFy K<<)koǍ2akhiwiy1ׂaa{:i#YiK V録GY L{ٗY鏅Y1ґ7y?j1rre ` &-wkklfc{7 A8y{`&yY3{P T)9 ]Yv&) z9m/y 7ـx`wО Y ɇXPH9`|_p wi 9Aw 񙠜Y| Wi Qџ \ _p):4a =|ࢯAA $z;f#W`ɢ ztGg^sp ? :9Ʒ6w隱|7 A9(:z|pxW`` i :'Jڨ hh|T Z Z©hg82u8ِ"ىzfHZ%1 y nzA㉃H!{w*zZ Hh7ZєGP Z9yz|iИw谱Q k| ՀKG:a89Hʲ粬WꕮёB fQ6W=Z؅T(e[N[qX؅V&݊o*㉬ǰB蚮 ~AK0ʗ0' {|c*"mo  p۰*{dȨA;dȇېHKpT׷|h2Iኣ2댧ڸbiA ~ F۬) WJ-z?՚;JIZ[rz1CH 8P ЛPl|ŗxŷyٻk:+}۾|*{)G ,Q: i2YՐppД4}+k ܋ ۺ:J|0 0 ڦ68| DB@hi kfVjkY]Zd|ɮ ~XZGl-yAy!ݏpP *fvf)"2cwjِ=٤ٖ#[ :9kѰk|@؂y lkkm1̫]# ڷДՇM}Ryi܅-eJ,] SIB{>۬۶  ڲ ; ٗҽR#DɚIȸs ]ϊmD_otؤ)N٦wZЧ] k|8#̲a|{# ָ\d]̼i| =X Z d(jL`x7T7^;,.Η]l_a>;|Ω z}+MmmiP eRIa⮁ڜ׸1,|cÊ9ۜ>=xq(x M;=k֝Q p~{Y?p ^jęN>ΘꦻכB~9^|GXa\$n dylՐ({6,-N0~0|K4LWl *ZݏXZXNM)ڃz.0ɥÑ.(}圗iU<֮Yo9^ɛ XW˼'{[rj<Ӎpz|@qZk!oXqiO z&4wҮh .[|Z;)Z 㑊|I+׸w?_ -OijN|9QHXuR{O[OW[zl BȭL .N.՛܃2a/h* Ʒy?Րܐ,j?.AwCP5JP[!oYaE_LDCÎ il`̏"Gtʍ?YLskɅ.eSQނG3mUk6ie37k٭eϮ]֬[mX.bRbSPtbQ4'-,Vm[5 !XK#+Yܓ3Ј-;SqJK)LbYAcTzݶb>#wní_zo\Xe;Pڶ"dm5l,j82К-{4͞KJ[tA**2' 3 -DBDETEv:a"܈2S.S(!j l)7ZmDR=Ԡ<)k34[򈍬"@-[vm欦N[73bTjB&KM/ ̥+4Qo>):-맦 ! 84kԷ:"v+!d>JF,¡)d'X-OD%h%*^aX 7ԄEo%B;,G-)gYV4r 6wm+Ƭ~֩n֐n{p7hmm @Uй"3i3uSka%?˶ sbDb*Chs֬n b&0!H4h8iDKZXvp0%X.23){öח{j磟0CkO0~!/wY##C1q ;tT IXQQ0P չ !Ij?d3pM3d8G= W8;s-qc[Ե/r YTkFC! _rCv 04RE\1|b 'aB8g':PSWg)ߘW (2JSS0hhu\0 LaD`XĂ D. ,EC kYɊ lHcǨ洨*#}/PӒUZP/x#%40sB"[ "LC,gFLn~yIs<)#UK&o)t2`#PrnRM"t=lkjk7L 2H(8YЊERW#iM@:mnBU$o3+ |"rЯUT0K\b5kQ K[s%/M)H+ ڊ`)8nIX 1#w-~qC2K*nxXA 1 -;=:,j雕jywo̻[d9C <',{vڎ-̴YA)AD Xf,ɿrLg/Yń3a(8#SRHKtn3#dZBv/;f,mfm6F>7c[=}%О=j:X:m s.9vHV,l#;ҵޓHS '5 oY{;̞znIThۓK2vrA&U81xC2rrޯb w+$S^wW~1:1 .K^Z#8V˅}TeX ֐9zDX]gipC9Qn]n{Yzϰ;VΫ'FTg?s(F?|$o˛{9x3>^*oy̋A? >N$9>7c]jWSdc%\mшYaGj{/|]re1_'~__0p0̭,Ke~m[ ;++K\c?|@@ @ @!!c"™l6r#u0@?K>iʅ@AAA: @"&a*jcr;ADq{;ԆB*B+B,Ac&Æ\zxoa+m7>+[Blq{BmHBCѺ khࡱ0#A>o 0lؾ\ ?DP EQE4kl8S{{D6[E2&4:;$EaFb,Fc!bhˇg;h|dB 6o;"kLN\Q\ po )CDclGw|Gx`Ĭ CqbSDX4B0o\li pڰ`GHHzܪ :zk7tDr{3[>hĬ\ٰNBII?bBeZB<#ƙcQhY`ĞJJ "U@sXDqK;0q|b B*ɯKKlp̒Fk*#KܛcQɾL˼Lyˠ&14/Jsķ?\&,lLٜMCA\qr+GxID囪pMlNȍGsxHEaEk;E:,O1Q-MθQQ,sQCMMR\(}R(R˱DʵkR$ q(-S3)"">pxr0\8"ƼS1]3S>=l4$TT4qi>TIMm,>mӌԎUQ'TU]Ut e>DTW}\PdU_UT$Pd0?"VVgeg3PVkVxtU\0+G@I~P m[ 1VvmQPn(Fb`U>>dDNHB002])\CsS<d5^T:d(F0D>eT~E.@dG LHeZBadFmD}$H|e 6P(DeieZR6ejfZ^G&2VYލ;\KݐG ڕږ 6X&nf}FN D݅;}.hTNBK%g&Dugc HCDfS%"m |6hNUcW~Y.i~6^Tmm{q#]f$eRm(BhicF,神Ogbt 8! k Nj~&FhXk@;jkE&@h]- tf03> vʲƇI`;H&Fk.DS]p0]lVD[!2a&_dBbfavY(kbhAXlޖK(̆YkD>n-bÞ&';u_ap}E㮻c_X6n.D݆@i~]IK.d1LLlBIm ^}U6p╆oE5Yp NpIhn oe>uD@j>n3rM%p~FBqT脄!&ys;u gh'kmLrdBm`!h. fk݆&0Gn(r=r^Z:ork@.4.XS0Cqj@qE>+jR喸\1J>Y(C3>bPBPFp!'_5s_ τB`so;:WRԪbhhxOmɄ~8\4uFGvGt\Dߡq!sul:N vhlsp!vHulNppWyGﶱoVQGs;)UiFxddEWQ`t}oBpvx'\?htD27_/rMs\W`IDxp7L[7Әyτ:`ےYh@^Q}PfW(hy.\Vf(pGqp:\r_[>LToj~vP#7 \A/igWz^~{9y(gϞ77\%'K 1}̔}sӵɅ|r`E7`}V7|uL,yZ^GNa_BsmBk_&inGkK%ƙ WOsdí`XjXoj \kPP,h „è1l!Ĉ'Rh"ƌ #H@+i$ʔ*MƂG.ќYӦ9E;4b'XN4.թfӦ8El%֬) e f )k,ڴjײm-ܸr z3/.l?D]zl$@t'SlreBt1 b-_̖/߼lNg3Z5kDŽ<Pl¢>zuޯ 7MZ\;I.os;Ǔ/o< iS^;>XAl/h * ^! B?$STP$9B6M6=ԇb,TUSG'!xӎ>9$Ey$I*$M:$ӎ7a]z%(v%m&l&R&A܁14S>/b1>S⢍b4*ԤJ98',M*l˕xNz+bn*f ;,x $9|<9F;-MJkN.>1JmA C-b[j+-scc: .>/.Ye(XR(|0bY0Ʉ:2,i\$۠N@K42Pd6MLASK/D3ЬV '\R5\]=5N#7_r5 s2Q=El 2nts.[Gp}}=W 3-L+Ο:nRߑK7 5Тkyݞ(Q4͸٨b#(vcI;NxcF+_&KK󫄔y1b,:zPgcާ_nM.bxC !^La:$oy4L<鯁.elE{l ё.(8[62m)RzB6o?  #quI< @dZyTk@#th5 hp3] 7wSE'W+SL'JKӣ'D n2 !nY]iB gR5L7|jq|#Q"#XF8@G`LzҔsziT4%T>2Oj:5lkн\)SzTnVlTčult "f=-4cca|knWø!pWh.UxKD,jҬ_Akb҇8NضjnOaST6-T Eפvd!Tݘ6ExS?o Z"OЮXv6Xe9_Ѿ^oոQ @:V>XrfLEZ'd@'gVxeЌѦv!gGhv$}z泟g \{Cd<@V@IҔf>đlʑ^'1d5dx5ť?8Q"on!I&1XGBR$< L 5[ N>Ҡ~-h>nC>c8n0Aܪ^2fL"AAZ+# .q+Vю0eZ`ɶkl^ /8n~6όbt$Rns\wlOXv5`9Sfr ְ #GՍoZ}GX<mZP6 H: oA3v_1 cѡN37jQ4&*k)]F8ME¡[@}",- K`@0e&Ђ1;@X?@܋iWlWt}¦H',<(@m`@iy $ËmE!epN?oe UP%L7dSC9G@U)EU@!FA jWf@+@_/_D;l ÒymD 7( x@rZbhb 7x@v>C*P:u /t\,,2M-&->l 7К0^C bVing*hR29[AnIhѝ"Rȵu]}Fzh(% C$ ] ިa3(&^i&ީc{FGeeާ" < `P` Lj`i@i<lJc*у&ѯ [s8Yb%٩ ?8\i;.fD@hqv0p鉎gX"N?*''H`,"iꭌ*Aܤ@J@+8&ڃDٝ(R&R֦Mini6Ȗ, Σ=+=HB$ )Cl3B#W<,Y-䁀kᏦ$h z**=` =2öZ0#lAz)Ƃ)q,y=pHV>j"n&rRsʩsBؠIEJ$^'"fjvI$a|,FH@"hzlޫv3!ԁFN/V^/fn/j/:ؖ-adC/\Ë.Eݽn10hN!טKo/oVQv׵RU0(\\T6I<@ =Lm _% `Caꊪ#^/~-/1'Ձ(驖]=, Cqك80=0lJs*QsBXsV0! q7'B/X}<=.*0le sB`w*`ON3@B#̦ +4y7yd$(yߚ{²5љ|towH7ʧwt|’ $9`9%B!p K;h 8yz6#:6J?u4<4zJ;{@9teft* dz)ˮt5z\5^g{j{'_G!;;;ǻ;]5ϴ{MBk/ DF'/<7y+:J'RB)B+<ɗɟ<ʧʯ<˷˿<ȃ|7w)  !š@\& T)O=W_կyW"nzh zͯ=۷ۿ=gE,B3 (!@ 43l@ eB 8=X;d=7?SVcyyc?qC>臾C~|rBw >$= 8@ ~, A)C듵1x2'?'%)L 73A (A+lWK?x1i4xaBrbDQxcF9vdH#IZxC9eYL3iִy3&A(ԔB:12%H)R3y"u RgRMB*+ٺeUi̯Qg2@LV*X(*K6nRXp` {,7-7 8fg\>YƬgϙ7wL:ϥG}ڵjԯ/;Kc " T+]É7~yr˙7wzt]Jr%jl=N5b-9 Ek/!hio)" l,k1!(C&O<?KQvH -pkPN8T\]\U3"v $lszԬ|$- H RI!#lGv0;p7%P 2<4\6|8$3\`RB'[D&JP* 0Bd,E  u -^ho lO $"޺ . e*XPHL/^FMVτ&$،eRlP`oI'!9%2)×^xarfWp^j gace(o V9t6 KcEXk[ \߆~}eUEzۖlgl< 7q ?<%_)|pۢf |m];T` ;cA kτw١rI;PM2Z]g<8E%*'/&b-g#ywfsBiGv]Pbn]゗EAN]%\Ћ-At _)T! w, p>xnB Z I [@ `OTL8A&ǫ3}R= &Ժԇ& @<-0q`"1)%?.KG:&GK^Rn.CB5PCRX؟4W\,SI.ZL|Ј SZ`?ޡ jN)XIL^ d#fD#<:1VkXr&5L9׉׸S6Lmn%p.!xf3Onv' 2 S<]מ@Yn9F4@P(B̢P&Lg 4 "MlNy 1蛎t 5]G5Q;Ԥ6BTJը. =IBOO}c c FcvaF(Er2W2CK-6>QfU+h(LϨb+kY9vw\ Ws$NRW UNUM-kjnaJv#0ژmء;ox䚋 z(,}&<`oa Nʤ# fnj?DI26؍afMaS$~z)y#;9x,< |u7m>iVHp^"ʭI\JW)Bgh 8dT\^$F4,I<-T@ S|=,ZlE,;V{$" )2_.$\+5R»fm]kcLqPx ]v /hqU7&Z4(t1x[*A|΀ծQ @)hyW"zP֟]%9Xkva]s+}PPs_jc3 n :&;dC]LцATfP@)(aP:@ t*8Bၿd;P^ ¸1v.{lkXέ3׹Ws4pEWHa-c!~vP*VgTczHRgżϷch) # sq7eUZu}-v^}]an?g}% Sby{L'[C0WȷԮSa>Lfɗ_$Iڶ7MD=*E8 NQբy v7X4m"f (-_}܇2ha#~} 1BTXNC"B( >/ΏnO/x7| &Ъd*е(pzJ:/Tj=*@0IHh8!Z 07O! .@dO.xjC x0vf pV /MG790;Pې ;ж =O}epm] ypks0|1P!PO#1XϟA~LP4 d Ь.̴ֲ*VZ1X:>"0v$z{1 mp5QVuq19䲥@LGD(.C.!릒@ pw[.-6BOpv08`bPm2!"R!"#misp1ox;%QiDr0 r ""! &@IqnR)2te^<T k2`b +c!P ra q ]  q0ov'%WDZ6`$!`4@ ~L N@ |=$ f(0or5%b3f(6osat2`2/r V-QPr+qP&.3R$^.F0S: ށe;!R @D@ P>qQsT/X^.*ABRB}M2 PԀC; !8%99QEA ` AF C A0sn v/1IK=}=1I(3>#$ lF !z?G=T@ݱK.'O3lO,'B _'B@IT9S`\:K@Bn2RG](0SHsMIc~,?_ tK1 m!6@M/4GF @TfNab[[fBP]GCoU] DGJRZ9wE3^E4e@#)U`URI2xdUb o Z`!"r |4$u! Ha‡(({ p {V 6>p\+ C 4"&;D/,u Uu_kGK+!nJSv$KLsbPh5lU2Q >7`18eMeM 4/4[v }6h'7 +7*q h8RD,D^cVa_!"` t#uR1v/1xnbJ=Oio߀4E-1 p;SߒZNN1\۩[ǗlhssiDmtIj6V` r4;u`TW"^j6RYa1x'&axx2@:C"@3o@~#`SveY7d&b q7)Ow؇u}G}}uTD Gx~~ߕjuu6#Y=jW"pHm+`.؍X瘎؎*.PJ9i>[b`zsp&cQfqSqA':>)mBB/l8[T+ND ܲv& Y9Tj_Gw#mwbYٚY%֒!R?#$a- _哒.%2G\S7i7e375N-n/|SPs͊Q8 @ Fiy` A8h\@ wtZ&B>]{Ќoh&Dn!zڧ W+F kdR4Bn` h 9j{)A9#B9ה3. M="F 9%o@ < [`T$ Q.;eli>LmbZ7^oYX`i Dѱv$@ v';^4Yf0g u+)OH8v  T ` 0:f0aK  `_`FlCr;2[³S"Uu1akc1yyMā#Dݛ#|ŗ|wV\/PiPu͒%xwK.gB }Q" }uX5$F Wr-/WѐʱJgƩOqb![]"]J# :#'6)>7Z˭ҷ~/"'K^7 >"+9"16=Y͚zWD`4AYެҪU}ZwM^w!"~鳺gG{ُ]uD([&H~۞ƞ b!/7W5՟=="JXg@\&ݾ|a?>2uv=r=0F9AY7+>+_-Vhaa6Ra\rSv>!Db~su! bBM]~ŕi=Ÿ <ПvB ĉ+Z(ܿ;z2dml9;-,[| 3̙,\$Hӗ0СD=4B}Az3*YnhLe3\W`#k.,YjNJm{Ys5ΜcDŽY}a Ō;~ 9ɔ+[7\ A$,5ѷ Lrլ[ :jM|yK9ݼ{綷PRqRuV^6w\/tΟ[.=nٳc.X.b=.P?ۿ?z7VKLJ.`&ElBH^afP+^t܂mE63O6󜘍(Ȣ)O'0(6H8x9D4 M2LRNIeV^eZne^~KjHrehI,P~IgvމgQ Xuu]G%"xK g^i^D`c/yJj5mj* ڕ]nuUqJWޕ^}Mj,dO5%ll^ :8ze8gyk~ݢkK.;zkd.~1$ 0N2{$p&mlRpWk5vgU*Z|w|qu1!M4(/ 33J+3 sV%bV$R\2'k|4lE#ͱ9)LV_RM^/3b=Ё \kkݮu{9`d_NLa8ƭz+\F+KWw~':ςNz z.ڈaT񝢅Bh.%i˪_:o,뫥V$99B(=koF"r.#O~Ɨ~ʫƼ3j{ߟ㮗x޾|+`'~}^7Usʜ+X 젥hN˃$@ڭ^q[!Bxӹ|< E5!D^ռVicԚ4A1iN"w>ύ\S!v'J D9QE,:q#;XFQ/[b&7WG=qn9A^r&)I j0١%(IJR&~IPY$3C},K{tUqc X"Є( / cD@ 31 @$0b'N3ح]0_ 4m9kj3 g !VF f%+~ lZw$` (NH"j(\H[Q}:KۿxuRaDnwk݂G-&k{=Kb.r_,nLxFԲ%w%r-*$~\E$. !ӨG|eO217P^jiV6*,a J%.+VJTLW:aP%$]T60%W6]QuF"H;Xm+] m[]l"`XV"J7{,qcvGa>uZҦqUWק>X7G=w λD\\X-!z|K0R~ Z/],= ] @kY,K  牰 cYvQf6Pu5Xd]<~~\|>~7i6ciEv`p}Alpd*plfhPW~'f1Qpcv nV7hLiԷr8w%x~88G`ܗ]F`gzp{-wk#|#ToFoR-%TrD5:W) kv` V[omhWpdCsR:6jg&^}˵{Cce 8MrH< xviUxps_lp~c{|`Hz7d g8yjvC&@{CiP7@x0dž@q؆pCdUf&&&7pa 6`.Pw{:hxb8hǨ60 d8` ^X7f+%z}[OK`8p и $@nFQ`M0Li]wqa5c }ݧSYgpzctl+}(9`XL x~ʁZ9S) Knk]Є;K(xdPh us4OZZ9UU{[&aPbIWhwqF +r' w'FbubkXLsxezf*X <ʓUJ.Z`U` ]`X+y1}ˈ n;`}k G'̇:,dĚ۩}9ʩ|$hhi7pŧٚ[ȸx"v?:p b")~7}A̸Aa~:u79~,e F+L vY$0BȣZ{`gn 렒騶 Şk %b0$˘z^Wf͹i:FCR؎.UOZh#[|7{uzJ*hlSiۇ.[ؼFPʛըFPdkh`yhw;Bƿш֮d:pV-zm[KiwKnJE M9,n%ʙ :}\`7` Ec,ȎȲad0H,aM[VqW Hܐ]7LpЉJ7;d%us&phCXc Dl2`s0ЩyaJ`5p jFƺ;pnG jjAvfDƛfߋ ?&R^Uʗ7{VGgP"@.@]g7g`^K <Oݑ8'`MJg JBV%PP~p֍5UJs/^,~-.7Y@U\3nb@'Ommi ^͇4(MN'xO> ޱ@^(',랴Zt)lPusP lf0\u2x[NUse{Wh^wWxlmFcFqA]Gb>[mn^ب1ns9[zpܽ.j%}N:ѹ!<+rT#>B5>+&L( '+?e+ ::7TH3<1_;(3_GNO>J "LJµ_<\ej0aOȷso\+- dm o(4ڦҤ҈5Eu/6`/)Gӕ?6?!1oFʭv?ʟoW#G/R/H>s&l_xS_"RO п;2Ǐ֯*a\Fbv$T0؟)/OoE/?J@ DPB >QăØQF3b *̙wI*4%L+U|y%͝:gcgܱc<E1]M>UԄr%ŚUV]~ Ue͞EbX E7NhJs^ʍ)nܹz7o`{a2Ҵ-_ѪdΝ=-ylfҥM]ۑUV9i*lݸo &E;&M5Fʧ?.qrխ_G::vQ͚o}/y'_ԛ{ǟ?tL;$,@$*,1l P1"S?"$D;0DWT駖d̋FbikF"d$3Ϝl,lG=>ٳ |PDD=PHU4PǢF刴ޔ3ԯN:=E4<9K12K2eKX]*Ks5S4PE%VjܘSeU%RǜqZZz)RYq-Xs:vX\v\wz-+ĐBz#¸4,ws֨:l %%8b.R-.F8҃/c+o>4WO*WHagx !F bwY{xc>_efi:jjz_2s]vdM9F;mf;_` g潗|%{_`!A㔢_tBGz駧zWޗP6ies;s[ aIJM|A{߇?~gMJBjmV[uS^[WfRьP"PB9gb2RMh uCЇ?> \lNWFEb+D'FS.)R/rD2ьg qĦPqb!JçѸG>ч&D!p`2RәzU2|~d&5Ao0vqN1.IVr|!Slwl;oEWJb(b=Q,/3#Ã4f6Ad6BIbW毽oDfhME(Mz3D܄SB=/&*}1O{ԠLDSgB ПdC2t xԣG3 513TRnMrJ"|яԘ!Z5]D:T(2 i6Aa,L)KY: S:4H"TUj:B2NuonW[!vnL$ʺWRժW5?Uj S:`-XX4u)n\ڈI/Ή N-ZՒ6YJm1s؊E6#u޶JC<5R^L_umqAǦS򩤚HIiWݥ"w+7[L*ܼN׽v" rp¡}ٵF:1t=^>H@`M>/c'&N,/#3{7[)` Cם'XdcGXWP" |7χ~?}W׷$Юa3xHzgǟ~>QZȿx3ؓ= -ȀV}0?3???8;ġs kCS(#t$b4ѹ˃[<}[ q؂Te??{;TS-~q8x6~+o#sa01$243C^ (16Ӈ7T{ yh+ 0?y H5@B z0@>\<%P#&w#MAj<AdĹ{5 m;#D7[7c4d7}P5PK3E?nx6-B{$AċSE6kI~[G̠f`0GztD+?LxIx>MM `3NX JEJD,k1ͳԖ΋Ai7US kK: K%x [{28¬G3O~lȬ  C;xDD~sU"q`L:/͑|3 N;' K?ȡF{`NM: MEĻE^DϪȺ9*5՛{0"˞U84yodLTIPMF#' l3= :Ƥ: }LKT[jÄ}G"DZ{CISJXAZx--10؁R#Ip5lN04NBt3Q 8% J&JXOr4tq(uP{SSA{RST5H^Tk)t5HP9TLE؄LRNCXS;ĠgH9U{'8QT ҖOEWTDB )IXؐUČ#`@nJVr%W*rτMCӇG785ڬ}< -EltPG} : Uضu24b S <855Y|L zQ0X}FQTyl ϑMMU֚R8S|1276=(Kٵuj' 9it4=\ T 8[F݇gЁG[;HI5X`۷/rX[ zO(6{ ЂY D!SX}8Xv ^]m Z']a>]OHV{Νܗ8cIX_#hֽY ڰad] y~kr#xD0hgJkT DΜ 4D M4É]] JJL^d/ KjΤ3w"(N'xp"H%mh5Ε"p<("6+@U[nK%I(6 ` e(ԎGD5UH45ӿ?mV'w(Hx?_ Zqx }"QvEv\-GdZ׭O7a]]]MĠLP3vSGk@cNLcܓ=Sq"uVԇ늤 $_%KqK4#qIx{<Խ(U==%ouo )7*?? !7r"= bd3US8]%'7K8M+'Ps] /0pcrp5O5uqt tsuR'R2nO}nl-k5fSOtL\tɖ1Pt uqLdWed7u"-Y'E"Z67]4]6Ų v*8vK3HY(W\hgwtjw (ttd?<Na 胠'x7yzG5 G5wzf t߫p FAynl-llwywRXvS8g[2P_)-xUŢyxhehb$豯"xh*z{*z8zMz H-f؂ `\zg8_[{KdGNHMï"rLUغ/Ow[T3'X)b(@ O؂ b+)&;)/+BpIVpx{xOvkJt{\ȧR3*p8ZhK ~߅)P @5wzwa~p&p "LЙIB /b̈jSf> )r$ɒ&OLr%˖)JqfYn$̩S).fp2o9s7QsIEhTSJ)խWr%zX.a;Ϣ}Ͷa %ݺv;Vn† :#`ASRr#`@gPjPqVKj@JQ8ׇ/l(Yij$W/Οlq6bgPsJe^{x㕖wxۓ޼{CvLZVGS+"LF %f`pY1D1sF Bt&,l)! 1DeLㄶRN9&2WK2٤HĕI]_vUE}٧ޗ'f|dy9geNQ+w⤝wBK!4EDV-3NR [D@S0D&ۈŀkl`@nj! EI;xk]&VThaVYu,VV- m>U_y5XdulZp&xӎGx& 8 a `A*"š TLP0,ZiH;8"3 31$/Gb>窛W(k:%+wQgzG33;߬F tS1t;NJ\` [sݵ_c]g=&˱BR@"+(iC7>l@iV+ X܏ ċ!oEpJhs޹矃:$Xv7IMOYy4쯻.{޺ǎ9dlY , 3߼CS_˷ҧۂ ܊M8Pcb M :j3:S Rm\`ъ!0 \ LxY6X0`x2A C98! KH %qB5:!{1B"h#"Q@Э I+W bL]&F h쉃B&1r#HGY.5hH@w^ %r%B2$%MA0H]@`[A\DEhVX_3$%Ǡ- =SЎ泤=3D[4ִA$0o ɤ&Xk A%neLCQH0L3TQ.}Č@;o'C8Sx|-gY4ZE/*mEyCKjғʫp+@nq f 4XT$Z'] K3 Sdg(*UlLf*S3m5MZ*֤$%/ַ5r+]j׻5z+_Gˉ!T#3Ladpe (ЀiI@v͟K[TM #p]-k[Z"WZVJְ~cݭmy6Uk/; 7].sB7ҝ.uk;dU`1ha3 344w!Ȣf/}k"ԉlg6kiԣ(Gkc iZMb yYs~O;}e%9>5q"tL}?N/W> C^"痾gv.6k7 y_&Ңy__`.$^[^A^_PP }௉`  `> [p5Ua`JL zYz`bU M\`s JX>KhDZ*" =[H6HC8 ^]T>!  ːᳱ2!B"=8#BB1a] PF,: :B4_><e(G'@@ ԙ>CRbI+6>ġ>@.>I;c-@=dA=:8:V;Hc H:pC0 H%YbbbQ_u[Ɲ\j^]^  e,%G=C6#G8%$X4q0؃d@~?@k0l懦#>W'fDjci:i*7<LB&t }߬z_"6EMdFex=x@5@D? +€8led%c5 @ x_g+dia hL8?xHº0(4*<$榤R*oZꉎ3#C$PF)#s*z%Zrv^iH-+e1'<`ie<4)*'k\5<&>zkk6XY0^x#GXj+B@&+6rC'nj@ȃl>f`Ìcj 4.a22:dR-BΩF8 T@* B6_ڃac8%/emH&p / FAʃb%~3T&b.nl zsaԛ.I붬~H3dFn+-0-ex8z@̷>oKM-4FdbEhmK12(,x o~@$8#Ś=m$p +@{-rCd,XB%Vb$># PH;v2~2/1&q Ŏ6B3i),F^kA/Z2H?ld< A'i(0 *Ct@)|[]H{*f /l 0AoC%ekf ܲ6FV>82 64B'tpj819cfuu2R;]<_J_B@6"8$/CiG`2%< `Z4K (;1pDuVA)`ft]*$L *CU&'i@<=DB0LLoVtH0[3Ð[#1W$b%vvSۥoqJd꺅"#=#r;X3 *vu$0D/ Yk|=L2\uwD3$B3_E*wj\.Fr%0g*HlK“X6;wN3| 8۟ubl:3(:1X*<>&,.d?.0"W578jh>؃%K90\r2Mw{D;BfWD?@ʫ# 5pn3 \G5*d74G"g8D)ˠ04P78'"jYpQ5ꅲ<]㮜r=Q^1-<̡bY-8f zpv75A 9*JpZD%P.*FzI/`yVnn6vHJBKƦG< X.y,z{UT : Rm7O=H;zuJKۂ9Frsĺ@ f@,"HCipHc06Q%@z'j1G'mn 犮`ޠ>(t`?(C$6bo< @ck>Bk70iJr4؏>l `BIf$%xS>,D?<.`uH5cn$x}hbj47~Zvu<8?;S0jw JڎD)Ge)òKZzQVq)y 뾝2>cABWxұ'D"**XgCk*¸V?Gvu66@70y4p9hюI7bE1VLWUGA ?'QTeK/aƔ9fLoԹS?:hQGU̙-;FfgUpԺkW_[EjYn=%L,U+0܃uk.C{Èin,kudI!Gf<}"ּsgJ(;NVuddO(EXV ;Q\ ߌj)FVDo_NMW٤=-H*|RfkYiPDPela\Љ\˄oeMfBbk0e/ ̯{ $[T`×k}MN;Zɉrl;!o/;~ӆnVƻcLF$Hp߁^/O^;P aN 뱯˳Vpog^/|@BaӍmk~~yf́v. t!A N1;#S&=b3.B -t ]xPƒdz"P']H`3XÀ%, PD#E)NUE-n]F Q8be(F'zmt1Q!,(M+Yz|U-Y= ˜HE.#DaFI~q%1IMGBMQ*,JRfʔT%:-qKGⓓq]@9ꋛ8UM'^h:RWb62 D@X=SY } >9t"u($_Ywϟo( aop O8p=>-{a>TpY]a0epimq) q0 pQn20߶j p %I:hr 0 p iV, 1o|N& 0p.Ȳ.ڌ q NYq) &o5A1EQpjo2 3p]a ߈m $ cqy)O q܊FqilD15PO1 qɱ1p1Q1nqq " ё021!%r"q)#=R gqF i+& A>r&;q'urܒ!% >! JL!xA&1'b bM&i+iC&)-1$IQ$RB  /@. aR/3  "da 1+#1) \atj"A@4@ $N>( 24+2*R 7u3L@2 2+"SQ#33NB@/:~ N90190;`;œ(d! *!tZ:`EA=Z`0 )! :! S!MQB;BHZ=! F> `/ga@ `|S;s"|a  xa=<L=[sL@ N!2//]d?(3/J`rtF=L t" M)eGM!pAtΓ\AaM?i)a tP)@ l!*~F At"8! lr=RJV`S'BFhu(/̲X1=3VNT t pj*ŌjNaU)AYA)OA@ bZy\)*U,ZO,3| AQ "2[/bb[2?˳FIm |@lAY- 0Fcc5P7=ٓ<#"==u\^Za*5[[+"W8tT'Db4b @8/_p!'!ձB'" @ 7i \ 6UTk'b<١O hpS`o) F )a` @!L77JB$H# U|aQ[aWvd!xv̀-e "|`-1"( PS  r} 3{`O P5&/B% @L}Р ro-4u"A(D `Ax@((\M,3V 2y3um#a81R9^ $ /`D raC{ 8"ZO/!/+  |8`ZRxS7ud Vi Рp[ Z ezQ ~:i>|.:9?!v @`Tev ^`>- io3E ;; a>Fy|~y,+TJxA)—"<lx/;@9/:M*7#\:(!O":6Gp8m2{RH;'K;X'?Uv~|a/W @9}"V)h@:{"BZM2IӔ(;t3ur~XT'd%ev"&>[(\kSl!κ%i;swk/g*4a ^@@c(9//xOS&w3^K?b>/-XajS 2ᒾ29a[x L/=)+a(B|/yv*d E:F-O)\=p Ja "`VdA6ULͷiTsaJKc"8H=:Ѵ"A Hm̳r B 6{EIF:;/\>7;c`s@;)DžAe8ڑОM"x=x9!}`ȃB/`&=E2ͣ{վzOF;{\ӳ](]/];G @ox}3a @-"T2F- ?m63C;۩T$N" An'"mݜ">4=@ ^Mbw?:>AsvA)>֌S: 6UE 5K~Ov&|Agm鷽":TBpԏ/~Y RDZWe]e_'ḥ'p{/-å[2[οw'u` 7ws - hHpFQ!r"`E`p 1? ʈ\y[!WzH7>*F3 Tb "F&\t/ijJjq|8[NbZKٳhӖ=%L,U͛mTl"ᓄ /z4bI1%"X$m SP~v RRIO"!:bk/BU]w!I0X,Wy-lp/W S 11ǜ &goI7PU)k!` 6JܦH1 _FDlED)s~p5GlEP^]SZϮUIK'Hz@} 67w&[3d(,_ H)2E-K\JWEjSs.sK!J€nX|~n<B'?f$'h(k @xW-Uamwy}8|rH4ui#) P1? |_M@H\G?e8"u' ?fQr@ELT B"r @Cb. EB2X.*V1eӔz,K+z0'>S&Ё *]|(٠G=1yyQ|IALрdH~!G5ה,ȃ>=W bفRIZDsc@@oPPeC@$ _zR3+_<n 62Ce+ n͆pT|Y= FgB@ ēIДiLH,Ǐe ,-x0J|(*Q5ɱw£xtxiJOZu=wu\'Zh-, nACZaAEpT, f1J P&b LN!3)ڊozVDdxc2F1j\eU-@b Ѧ@ RX,Ff-l h] WyjS  Ge7^9` ͭn%j 2XAzĈ t+QI aư{]Vl޽Xv1spcMYa [XXajXYZB8`AI] 2 < a!/b8C,Xݿ u)֨OAa@j1J2A`<)5$L|#^$]14)ȲePd9SވHg/Z&*M*S%:JwRԥ9YL LhSd%lxZh#j/ndV7؂6lhӓaG 0D )x%-G/ a`jY'1ȬהG,j*#p feƱTvqϒk[$'t@%JͭSkvu-Zד<-K۔n~>`NK7]u(Ak-}3|%>s<{ʄފx*`mn.G#* ]]iwyw}(ywoz|le t=|@:uU )T,5pG"ER{#iC;^GHJRO%IN[xjwi7{9xNћ;ֻ=S. qޏ|вO{u=ox#LO[;\{>=?_RD=F0WwxhWyfwyyey x'G}w灁{w8Gw 4}؂.Sǀ84Xv6y58;<؃>o'BD(^Ch1L؄N(]w{7RXGuFO\؅!s|}a~7f|k^l؆e7&ukiȂn|؇Wh!8hf~AXBE S2(7v8Sx(x Hx"XȊ&؊(h~؀Xkx8،AG^JXyx$w8t|w踎W;Gqg؎wh!}؏70(XY| p7˘tؐ֋9H:hY&9]*ْ!x'94 G3;X>q8GD)i^KLٔA"荅{_NZȎh^)h[Yf]d_ɖgqVYSIR{rٗeC y~yNy9!R=) h'$3:)@B:DZFzHJLڤNPR:z@w\XZ\ڥ^`b!fzhjlڦnpr:tZvzxz|ڧ~man1Zzڨ:Zzک:Zj;MCE-1.608/images/03_Bank_Model_Chunk_ID.gif0000644000076400007640000017255512511671246017125 0ustar mariomarioGIF89a")  #< 0!;\ 1<## #/.(000282EH3KY3M~5C5D36,!>( AaI?IeiIoJOL?1LNOO[YP^(S~U,V"VrY,[o0\\a?d1"dN:dOeceeeh?1hnsikfisk lmf1mo]o`qy0t_KuOBuriw}zP(|chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2012XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"H*\ȰÇ#JHŋ3jȱǏ CIIYɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴS~իXjʵWoKveӪ]˶۷pʝKݻx˷߿ LaTǐ#KLaiǹ\xAFӨS^ͺװc˞M۸sͻ N Zںu{NU.uݵ{k\xyYf Ͽ(,GUIxagv F`>Bt&;̀$h(,x\AΌX#v6Έct7أ t;@-`|TViXf%0\uwa)^)fխfV'|h.b-`'**˖j衈&h]"(d4();ʉ=wH{1 *,ꪬ꫆6\ jX*c|ָ *Ȃ hlfڸM9 Vkf˛ΥW y;n#잲C.S7pFo9b˴\(c !y {g<++>'N8pYgS #$l0glp-yc<ƜyDŽDmpk޷޹- +0 u(۞gso"mhbkۼVkuYcK6k7vIz,ǝYǨm'Υ^Jf#tģ#^xnȊn2 Ig&=3 &Wcn3 >hh|;M7q[K>^..ct?{/Чgq;U-sbv8 ǂ֨GP`JӢ@N+d@xeg_ G6}ie1CXRX9 Co,tYu6q3 C wRiu+, _XX/1 AӡH*'Ms`!T{HΉ3լLf 4i0GEoG۝=w-&Q` Ӣ5`3F!SAA?;g!ծR|d dRNR$@Nzp]GM:jt},ԹZx-<]X蘆bkX%f2Sskb7Y%S Z,D- Yκ$igK;CBĹ|1k?aKgMS_w=ۘJҋ-r Q\n'Mnݞ9s?nvVOi1=nj _ƀnl BI`^AjZ[«YY0ӑ::Z6t}X@b7@ TP T`9ZTŎX,ܸŻ qLch21 q\ljpybF͙|q /^Pj jd,+S 8ѐFA ̟*89 "iT < 9M+]Fn0xXZq^q+|+kc&V2OʏN^Ż$My+q`X ɀ &z}oF @ oU {R{A Ku\Gp)-2gkGh lm}$"}gF8ȃ j ԇpp%5I+EDcb ^7]^QTyupun i@ P z(x.b> %":h| h+1jgj @qɐ "g}g"DHF}ח k"" 2xʣ(ʯPK:+h"&(7 ÊnJF@YCV$n#{Z p@)-U^ZkiQI#Ȭr_BI+!)򢴬,fG6ge$ڃEsO CLcLp(k ^;QJkTG)4hOw#ST )^pS*Bn:@EP{:tQ&HA@YW1a%niAX' j^mjs[H=OmYp#Vq$u{eZ0ږL^_zrK|fI*,,xF aN3w:ۚbߊC0 2Rm.Q G#4CJ$ jĄ( T(@Mxv%wF>SWI{^#*EzC[o{^|Wk~?셦~so 07=4L=K<D-Ztp#z7ӂ"=o8{2 ZP48Ck1&ciNqCYI 95gM.Sn9US BJ<)L2h.՚X*f^3cfhʹZs P,XuKPcC. <"М2hHGZKpR6RT;=8fΘfZ$WVy^BhkC~xvFv!(ȚCol,~N';CuZ͢>MYg~/nͅ}7/~T|_0@j8]'ӿzle?m>>1?<+h Cx{{5FD*\:tpC*KAY@NE_E`l4>9>π_pAHdXft7)CPKtPPDQ~ӂ4mԆVF[셯%*:Ne{$7LhQHHWpPDRP`[[XY%}JKNkGP`QL/kW(SHMTHKNJ}c5@葇bUkhFgSKV6y(KoMVC PtW9S1TcV/M:T}M,Jb[tG8 UF@^8mp[E[C)UR]y {j(rTK YHK%|DxqekVF=WYH~ rK1=nUL`HA=u3?\w_zHYNkcc9[Q qkGP4V--Q֣ݓuY=PV= ڡh1IY@5ܥ \H`Ce\5\ m5ut1)9F|*9+;T@JWPϵYϕSMQL5OXޕA}F YuWKZN^ǭhMWKۥtDjlL=Gdd ؍u=\ݓ ōu5 ve ;QJW`_~_ jZݓ^K`94> _4EUm`T`>b_L]_Fa`s@cM_;h_=19AaFȕ0a/n yc4搠uc.-űrP`'t]H_Edhch>WX?N(?6@`c FG`F3nW^6neHume@/f0`6DD܌q7LۯdH[n<~FAiaBnDPPHhb keb.QFabkcbLyDT]xT9 <;+!]i4Fji^Zz6uN5`n; NLp0> `%{v@@@hLkLA"ɀG639b-#gP`g~w `φLlhXl8D(=Z{Jk]q8]$ B[f#VC6\~炨6@uco.vkm^mU> Vo@ff]+"enCejpndk?XSQn^&f>/ ; JA︶΄D D5n"6rZm>+r"V/G@P7~(o_PEP.r7ppm@r5?\6Җs(:Dh3'.7] sOnq9PKPadu'%n(j. 4u5wv5a`uoQ@0œh)u6Ey1pk5d6hg<x;o/xEg{ty/@P6`zgqo iz5oPOnGez_rFrpGGPOwGguhU4`Lyo_> VzGz@k07/QXWئ3FnQ[dD]cf Ezo/hz| d}/gzoߐ/{~OzP}zy7x~5hxdA~4~?Qٿ 2TAcPlā+ZT`P Gq%69re˓,16&Μ:w07oHWQI%v̚8ZlpKm=ʴR[~ZΗa>ײm\'2MeÌtW "ɜ#_"VHX"7Vcps9+uyb⏙plW9T3MlGq2taږ H6N`v`Cxsui[1꓏[ JT>|ŋ | n]TS/#4s_|)`~} ~r1Ь!8wv\cEg}09yWXoQaGa2HR$ȩAci GĈ&=Bel"$$̕ɑRafA$SCQe۱"fu$`e&GkH Z`7&JYiE]~PE%X@eTTY##O8a!馪2ꪮL'_Y%͍D;{Rl8eeƯ EYrN\'S&vir'Kz+@L;ơeɊG!'enrji 7gnrVVQ,8]s&ۯ%`K 9G+l1/W,^HԖȏȾ7rىITFϫ\ 54@8ᬃuU`bN;]bOe4s/*=7O@a%}E҉:7]ޣ<*b xސkV_OJ .>E7Hd#QS[{GP7a,$r#%vG'X y(q@W~j'Bqzm[li< Аz"!JqhcBdcѓlH !BİW$TҧCTFe whc +]gԥRTKpj)e_Q1:Γ!3 d0<(*` "r&6FL(Gh$3-yN8fOO {9ik^$PO{77!PrrB:Nj"ZL$L)sKSTacģ@ENa 3ORctnRM^,vyF)dPFFS\)h~| DTH>i \bLpRUƜ2As,1FsUZWB<`2jchVj \=QvLU(ǽMCddO +egyBeҕXfM|ٞanWoL3ߤ [8H_Ae) NASP DI B$$O$]}6zNV ]F'PX.,cRd$PFe2Uf =C5lg^g@@&.T8jF.O|YT]J,CnE*&> f%2RNY>BgaLNuVfVn@|BU()~'xhyr\H*,{%t›8}^|p Z2P}C-aBq/$ ..lzD6/%DQޫD$Nwt.Njm /N͞į/W-,D(lT&/EpO@Tpx2K8kZz`i5T1h K$SqV`U"~ba<^.F$8o-j(&-I|#CB$X1ʖ4O<@owq,!IoG7_þo۱1D$*O/q T11#'q{ F/ q%;\%,n_o&iAѲ^%+-ݰ-&&dpq_htd"m3M$i7Vvq5;3{z2/57oH7#8r$&+::ώW#%B=;lB̃H.k¹무'18B+ZZ&|oB,{};,$/8=;} }-XD{ La|9xkcX~-7|_|1khB?=;4~1=GB5|{?}-$|Nw!H}CE-~o{~CӓB9 1C-zBc6wăkS6^,Ck˲7B[rD9 ְ 6tbD)Vl_}i24(#IeQ CӸ/خ]| @!Mh!M-_[VmfdrR&>-)Je_I#njΩ>SĚ(ۑ؞Z0\eYUKj2UpɪWf|*qC/yœf6}zm*-oh x[6m۰}8:v;^\亙n1hw/g>ByHuYVE7qy#Ӟ>2mISD% ;%Sp=C;K#v$Am;0M:9ċ;G DK2Q&ӈ OM*֤( ||rE w|5^6&uueW`=qlS#a l}c+ޔ@#'U#xc!Xpu]&`Of_F9#f}2RozkMSX;F:-Hrjwںk[WM8߼֚ێm!+;[C̬/JCעu6 4g5W\̘|d^j)U.NXcd9lVSc|) ];eOt?-qwUǯ/D2>3$1[9MW^WxoG[cX[g5>KL x%U0r E/1`3FMSKP]bWǻbڀX¹njB&©1{'APA*/1 " P/&l ,`#XʁdFѿy-x\t [E,@%bsZMd0Eh\\!Ƶ1}$('2N ӄ6oQs]': _/)Q;dbZ-XB*f}3(gɵ˜$W>#ܠ(&:ՙsIc1_tYb\tyI#w j  UBP^8D)ZQ^(xP~THIzM)]h+jQRPLtLU*=Jyч4@%(vZTIi-&TbR j-aՅR)*OITInԡ+K{6qZ'OyͫQAа.1[XUbX>d)[Y^f9Y~+WQwj V_U#Jr2qx{c4@.:\>ѕt[]^v]~x[^U/za6Yҹ`{'v|ƶ+@Cz^˂])\a _9aO=n| @k,^ `۾0[zTҀq^_) Ƈ\d#IV\6$6WW@ .qq?Ef7qRVkg\mf5ֆWE9x!a:_ә9gxpиնh{hC8Ѱph?΃;k_u?emwSl6MjyE#r]]}b[j>vC:[c0эqWŤ+]n W8vtgA >]*q7b}ێ#z՝,l19tۈ^Yrw2QmR61^~9N\>yr?QpEsI:v`v^ϵ2?^w0+wow.Kx>rn.$SV x K%*8żd#x//yTˋA2C64 t4ȆCxWF-whCH<*z K/W@p 6`dʫX>K .2 $RO0Mj <к`!oX敲l&o倯 ( ` p`A,@O.0  ',P2 *!Dp K0P0 A6Z  Q ѹqXn!k1p =oʯrRM-hq`a.<`@N qAq ?nqƐ` }/ǰN`FPq`݁k1Q݁ 1!۱ hQ q Q K2HL&*n%-%colpQ~qD@~zrj(e rxrH2|Ajxd`RQPRF P'al,a@ Rp`*"A/RH@4hp -2 lRd`7ePX-G 4a (@`D+52.03/7x f/6P҈o)s? @I@:n;jN''}T  C1AN@lXCR>\+]@z` laAQI >4(''`\TR4naG@4PpQ&@B1tN:ST>@T`LUt@va49 :TF+`*B1TQ@\D a@Cq`K]R`5kpBT@tBpCAU HD L1u5?SH%gR&]\]u BOA1B`S `ܠN>;Q8m_@\4`uGFRGyDGT,1 !I  J@jArN HSf5MNpNl L#ud6 X!WI!I` W2`СNlai@`M k@f]QaesmANUrio+ЁYB5cJjm(.Q^m=CZlfg<*gkad>w%nxWA$a.[N^'O@oQ:aLd 84IEmwGG +GS  I |+e`tKkU@Gs `1hi`q *) j 4fo=X.U@6h`$AIw g5Q $!S"8 k3X&ۡy52]jPNgghy2?X[O@Zo h&@jnK YŕwL |7wHy7BO0{ 8@֠ONo`nE/`͒ocG!0)W0 4PeAG;͑3hY~4 `@I78;.ii`h3pVKxV1RsD`@H PN8>ˠV4 )^y`ra?-+'yW 2+`|t"x!P!j`_n\(5̯ϨtI5rA0rP`\ o`YWc qzA, f66g @6BB5X2؃NAmZ@0Y)E5n [8 99TX1/%`@58!գRGG*OϞ?/'@?ZW}Zǻʛ ǩo5zz(@bkkraLy>rڬbs@guHx~ @6!I``j!Pvo;O9O5v!;s6S{UW;4}!g{ڊi $BAso0'q ǃ:@1cg`k|Y练 БL4^[ 8}O.I l!o@\} <~/<pd }{y A0uT/]VqpY\qt<` .11KQ^@)qȇpDz !UI9[Xa 2S9{mAHxx #5 ˺G7G`([)V 3 \G1ew7uN[>Ւw^hpy%}ұWmP^U7ġ`Wa!8w6wHv}eKKK@:X. X<YA+;1{V~*KiX`m}N3SmXkۊoۙV#Wj!S[Y" X[xOIG^Jwյ]ٕ~_jrsrLB91y6Ȕlp!8f[E;<X7.3#8JY`(/t00Wb1~`  0BECRՃ ՏJ'{v4D$2 ` ,?=Ie 9DGqG{@ Ѯ H 8`%ᇊ]ٯ,8zAZ~JrdXG@@UWUŠ^Yլ[~ ;v%;ݼ{;7Z(ц_@U Mzt\ @O6Ol`#8Z?`#}rQU=!A+`ԏ>$ NU K|B$#6*40'*$UT ?l m0JHy FZϐ\T hZ_^[_mm GqC[Knۮon92v+}Lp* >#:䒋4²R7gsq7 GpJL <ǨǒLP`2 C2ljˈJLb)׳@p) ./M/U-P\_003!CLXA\+֚RlG; SGN8m{ĸ.G;^GSy;^pᮿqYˎ(3$͵{s b1ng>4xM{=O߯zC.x.} <`@0d ±]o?vF* p,OJ>@k om~S2'8#+2b("K=WAjq- 60F\,=8`gglctx0D\D'wDbXrPъqD\݈DVKgd" ٬bdXH|=@N~RlMN&̤!cXr%lK`ҕ:qN肹b,RKolz5lj36Kq ^8UNrFN:g׉˛ض|sl#:vQ zx $<BͣP2:Q{ doG? ҐF(|| Lwk*mi:ΗS_4{ZatT Q϶p(nvo w-q*wms Jwԅ+![8{njZ՞ wm{ WFu\KָW*ˁw.+xQԧ65NE&S// ? x-Kezbx3*N1iJH$nx<Ŷ xDƱ(~mEn) sd2N|E%3N Np>$1<&sB_7b!4XqhЃrGB?1n,Ucьn UB 1&hHkzӌ4%꼺T .U1T\H_iQPbW5a RBnuq]jOWz5K] JĢעű- N&vilֆkl\KJ%~RSK(R҄@7#ManVwI BӫeI{4Eq! G0ǧL8 W6?}X|KNsb׫#hJ_:KH8%|NmW\ϯu"6G*bQFBki.iio4%I?}٪".I܀'FWyKG7Z_K|揿Z7.TzU=⥯X~ sq :H_xUD_/<'w3~:Oװ;a} շ{kz8~u~3 w}& /Xw {WxgqZq%qPTU"'b}x}2 x/z8Xz/pÁi x"bmSxmTmmU(O1b0XCgwIt>/@xB/D{-htK-MzPbwhh}g؇~懃6b^(`}Sgƒ†W n-pxG8tt-x(xaRWSY+Ym؈u8x=u?H("4Ga!vqw+q&(aVbӋJk8S.ьO fbV'vJUQWxuzĐu6yU|'^#I/j@Jv2 IrV L6 5 l  ij=)Ppl-i QI NkHEh>ْ6l QyA `y> H|yGg$/&(ij8v a m)9hГJA JkBDi2R@ kL-NiA Ap2Wɉ )7`yҎ ^ gyiUiijo Zg(8E lЙ钌iP)ihP˹hhـI VI Fb j訕Й@2@sS*֠ A@H8aqK&q名.f7pBP6"V6@ihJ n` <Ж^ِMn{Pv 5i WniQj!`. @F

j!f:lCI@27Y Sƙ2V 7ٚ lfښ k8id r@_v pZ/{ (8T/ aP" i C9izlpcC+~Fyl~0 N+f٩ )Y~P֤luc誐֠Y :zo H*Z脯bkF`*e)X 0{ T9@T( + a-St5 곏`HiJ˨ c8뉘ީi9 ƔiYƉ1֠BA*0p鮚6鱥VۜrUF _i ժ Ϡ(P{[_ 铬 Zp&ԇf  hyf h: iȰǖ!@J[ߛh`蛸 7𼜦;uI.{`(@ՊM ] pTɚIIƎ6YȰl¦6rzIiɛvl13hL ɠ~ kȐ h|įv kt H ֑ " TD@YL3ʹy\akEiy3ƍƯ6v|ǼѵF nɎjfȍ¾ č;øA.l_١lkCɯə7PŌ`_ހLO 0 j<ǜ~<̠ ƵY=i>0Q͏FN llfh\q΍<`ZbΔpУ)D k1}}`hըLb Y$J/[vR@lEh,ǯ I{ЬҜǯ6 G̟\Ӹ,Ӎ6 L,9G>2J(̵KhlպJVx4.{ŗ` 9@&˼ }R`vo !-}oxL>lxo¼.ӏ'ܛދvϠ}l]ڹqڨ̬Ϫg๗ *u$kS]uĝ} o o4)ݏ 4^&[M@ -/p 9 M Y%B@`tkrv} \wMq8ə]td'qS @pś +Lhp0'0r9.lv9H n1&\vmէx ^_е@\ @g~6>#@A | x~下O)nʈ^E(pnr!_:tp. X Fm럈從`nTO|x@ڛ `p ^ppMTNs>pAKq+1|.$ ߄PUWMv!z'.p( k^귫X&r&|捾{qyWz7nҏ47X &)u[aڐ @g @P@*Zdud WiIP zU l)Frr߆vz'9p{' x7|~jJu'j_`/N=|^0@`"^` 0n2P/' `(p"?oV*oJ?2gPB\=QDcZ/YERdHJ1/Q#]q$'rj*U*PZeL'j->%V2cʥ9RVVVRhD>|cmm۷q纅O.[l(8g ?KX[2vV0(d<{ ҤQ`Mf}*U…_z {*QՐʬZw P"KH3l2DGS,!)≔R\6k3=6^9/x/@h@0$b{G r|e39 P4>;j;y.[ʜn|J|Md( U(FQiXIU _ɜIltIx4()RHbb!̂[$FNmF?W-% !DG0^B /+,١LQ-BXΏx<Bܜ7 #0U_IH:: ;¦, N&xiVM^ !N] .D)Eѓ=T _$䅫Pҽn/Bhիڰa3%O]0dNk#҂"J@EQ;<3&<=UH53 BĢ OŢ ٠SgaRio*O^ܡ~HYYcV G/z _hF L< ި¼%AtqX7_K#Ĵ7Y/vv ] x֛'9\--1;ڨwjmk{fQ$R5]!`CLj6FVyI8P]ӼG6RQ_%fo#/D+/9F,0tϮW6]JqS]}ȞࢻRz¢B VG~f:<$*  @0CgYA*pQ4.h&7Jh(l=V.mң(D51+YdsrFRʡ*:ک;tx`}N}ʗPf$3 )B#Cʱ/(PQ=P(iiU!i6Ni~7}}#X~ 18R-yj­;;m/%0}1U) \P^|Ax,?]sCRBRv?Lr{!V2mX_׈*<ׄwB6cnSH!<@[h$d .p 8s'ܬt2t(,Gr3tVzj`Zep]ӓ׃< )*hd_&=F#@+p@# h`0A#HP``M 4>Y@T ޞ?z<,_˛ޜʣ7j+M Ih?b$ԧڏ88(3p@lx3Ⱦ'-TJ->/Ⱦ<ě 3{t +8b?ͳ7/3#/2#غJ `3r*+ mP|Z@+ K3Y  LA  rAqA=hz? 꼧2"\/#Ԍۿ˚Ó&\lDR@2x3p/bb`(6DW>'q컀5CȻ1ӹ?@H3y[ jʩ<$_:!cH;4K`;˥HKHȯƌˉ˺8 QabX /d8= =Hu HJ<9qʀCC^LL'4< K,+"\͟Ov{IMHɒDO5#Io?@LN(`kXXN P ଥN;H@Pʚ0"O7ԬhԈijkt0@o=p$$qĝ)NxZ0N9Z:MXNr NG?R\MCcѪpQ'5]FxHx< }Ԩ;W(W_, M,{W5WWdtBL n=  PX,bLZ(Me XǏEC خ/,vUKU}חmXY x%9%:YD\h -HTMAOuP| F w\  $K{HY1%KLɹqb0Z-?@ \>ɥ\oPT !LNSU uMO#CݟP]ۚYY\O]O͈ܵSol(^ UVȅ0؁'C6tT;D&^P>]HW%u, yMܷlDU  :l[43=M_ૅ'؀0 P`]`JdY< _>ue]X CtȆH'@H!>EpU ~ &N@& }ddTѷYYYb-W^X?C3)ucJc_lXGHΊI.J~d1d')Bi= ,8.N^0P$kNpW̑%Y'̔M  vOb`Ka^U -~,8e]f^=gSQfсS=8S^?aCFfmC , LL%=0d1g5yUb6_c dguߞe>h)ѠEa7f!UFH.gG;~LƉ^ =Uv ^u&,aՄ]i_ᏩfȭxNPe'HZ(5ZrX cXL ,cv&UuEPk`kt'ft].kL.N뜼Ƕa lhl:N~Ej YjP-mk\VX8m*~ +~֦H4ο}逾1mxXFA7>? puހVG > Em["_H'VW(5U X:$/ur2 J:Qµ~\ĘF\H N j0c p?0@VU$2WZ ˭l1Z5۸5g[{,i(Ss/c8(c2nۗ,? :'hkE?x0}N X7dž>Fu2 FÌP%tަ?A72oٟwDP)|SZ/mqpQ j[> JtDN.G7n I'0bvSdjC $WtkT Uw\|g:THv謜 x+ QxvGl&ashdB"%3)ӏX6JR-Z$\9|\k9bժ+7`͍[ F`"Hq&.@*Z*3P3hFK\ӖR\xjs};lnwغw8\X!MY @QZXL17~\5bϣO}mPbU{rsmJDJ۠DZJ5H}ոvRx/a} ԚxCvU[<mspF?}PL-"@aUvY@v48 '(`[r%!BJD(xX2SIM*Y'BfHd&v %1g*j'%$aLW&j&a6 !MЩ隓d";X]}VXe^0D\-l@rHw>@DKZ 44)*J"('R|)B*(1RM !ߪ[*x S2~[o}PmHD "K(oU% ^)LgF*R^\/$z2m)K&0E^lAA;Vw-I3E) ZJ' #}l(K3^)O'mUM0qLR(٥A()|6zҒT)Jby=%B]{SHs+\ #/J7UF@@2h2u7T#eFUQzT%!m$I[HZHv"js0 $OM)H2bco ,`Ap P,tPXA :/d%J:̲cJ öĤC\UbW$ _WV: +OXU3Vgtҍ)P =DlC4ѢF:վ'qm}-Jl MiCm6}R\z Iqvܓ$w|d.;;e)5|t\7'b^fA/C; `@j7cWa*\k0p*5]`l6Iq:J\T/;щ #*^@b W6cT&pߍB6giU)H~ rզ\*NNq%-cD +643@[ ,F ՆD|Od$)mCp׈ a=sI.pN&H o7īx/xA>%[GyL_"|Ayu`;s{?yȾTqvߋ<}dm_/B?%OkgUI^A}[Iitڝtś@^@V d^q. NU r)A `+8 j5MNU F^#ݠT#!ƠRX5e˽'qyŀ@FbV@- N X+D&CEL,/[^! !:5q6#ݟ#&V^ .8".`Oe!uٺEy-CaC W]M؞!܏!!VMĽN ŅS&2 Ʌa=hO6OnaJ0#RN Kd_4"]9lraQc@R+jdC4ǚGlO͞M$cӱԽM"XN2ADBFB2d;CfEQ N ^zEd1%q+G^hcX KZdY2"R@e$"DLRX\OV[PHa_cjz^R !pv@ hVWddƖqhhjLZZ [B[\"\Υ(J]jkZ_k'"gy _VrR r!J҄KN#@H'uNviZibjRBqlaT..`T|JI}Kte'XMg"֤NPh!Khp\:Q\吮_2D!g?Έn[{(}Ȋ&jsċ>Yc2jtrxf%h(h(ChD5ќ:+@*5?N {qDݕ=r)x)H)ĨI`)>ڍsvNhi577~jmc<B tT) ^.o颕d:^:Ȥ2ҌDv*B/ԃq3w{{{P?QQg!_\Fuo9Һ:.'P˃,B>7nwKHt4#5TEAA%Ǵ-vT>OӃ3x3zx>>8@چ@@CCC/D{'D D;‚xzx>+xkƶkx x8O@VȀOC/tC7889syׄ|+}z}Ø7D, HCQOL'yb5I{55hsz{zw:0`##́&zǁ-Xzzz{ {;XK7tuz1/$@/%uew]D6ށ"SC({{A:#A;A{| |#|+3|;<;90Nj+vm'kO@%6eG/۪@Ę+3k8cXA˻|͗3x}ߟ~~۾3[2}z;l@:F!>$B}!}~ks{[A#ybDwYtVWU69@p{`??@X8`A&TaC!F8bE1f?A9dIH$6|iRK2iڌ9_͗;sVLQE`L=ucǠĵ4 &0xab)gMä-.s)m]wջo_pa(UWƎA'qdʏ+dzu(e|rrl YĂXpm pѽwo߿uEǑ'Wys7v=}:I/voϞq.zbΐ7c_VCm؟#춄XRVIPlRQ ( 1P  óSƶ+ǛF,ID]\Eу!feFMEG ]Bj`)+ôBC+R+W S1,Ll'ܔ tN9L&M8Kt|NA*_Ti`A%A͑4(:VRM\ UQI-ԁМ4M6㤓PW[SOX#Y5rM$b@cAՐ*% *96.A=kVۻR]C6[pyQ\s^Fd$Gy&!C⤵cZ*o?jV ,(Eڃ7mnRN/<ۣNN$sw&X`+pᛵe 矁PW4ҒFr!=fj8D2$%k)!fѶr;x᎛-.ͨx馼jo D~Jo *E( l"$ AN>ږ[I/ >bŖZ2\W^i_o]kԄ^Hl;MoE=u6c/YEM>fW/~P: _* s6O^yС_C~zq ]"x.^.A,50fB0>  YJx )v`cza fd 5ZxC#o!IVצZPzi5+,nWW`b >`\0^R?̃)X9:XC?BX@"!1@tx 2L= `$ AFz :Ld(ZSS\yWV1,@]RX, o:@ v({\(?6d\N!in 02Yu$Fމ=@MDzQWOԀ Q,epY9,uH`0-/ 'A͂T)ADz!2%M % $V'MtAXJsSI*BԨ^3TT!B$ I'2UuoC%?Qiod W׹,g_K@,p5+J@ t" ]w) Eg Y,)z;RJ] 淅p΃Ā"bZr5"$oΊw K8. @NF]윖Îƺq9 q )&%+ΰڄnN ά$`4 oj,GN Kn Rudq*?/3ψ:̛\mϪ4׬.57R78%J5Kp7K7/PB8N87 L-'I *I:S)=np*Y* Iޮ`PJa5);^  I LԶ,!5̀M0ĊʐMTΪS].M= J5I)6KԶl,U3(5\w "'ФP6MeWdxo)n_l_ʢWu$^_'u' dG2ii}7eRTO[B oj Jhip|/BhV8 i ,@66K,LҖKp Ȩ o ~H ק0ow@$&!/ -=4 mAHwrrA Ikqnnqm=Tnpr Bp7@6=oQ*r/s85m޸wqbjjGRxitW~8fwc7jy}SD@~&0!9hYY*/؂gj 8*#a @K8b'IIabJAHW9Ux[Hx&93(3(}XQtJyXS|8MɈ8x9w䊵txY\gxto_Иxy}CD*YP[Y=x5jJؘ19&Uk@ij5f*SY]'7|ɷ{n|hFWJtayLdYM~K j>Da~~҇9`ދ(aԱ} B %`%НzУuѠG~ ~+`i }^ ]d D~] =G^ C WyGF ^ m95eڙ8imyWb ^i=$6" _@G~  <ͅ~3?灼@뿛{iX=HG= t?yG֭ !F*$H+i-HQa;V̩ȱ3VHra4I, fjIvlIßVTGѣH*]T'Rц_pС|+?xC>\xף[N]u۽{]{9_-_ܯqF8A#'H2lYo6A}i AmVn2=\NP셨WsQ tX*'"*θ-h8Nt%J|Ηbكe~Zم0_ؠkF8~Z Ʒp[>DYy]x8x Jhi(~ʨv.rVj$PHd}M>I`We)#(U`|hjy&rIۅU^X [l-K:KzyOڷ)9^6*gV96p>*a B?Nkl, G#|p9b;p6X-Aq NTZonrG ćqo сsA 7(Ee 3L{+GtAr)0&Zو}iڨۋ=h_Gniǜ)GYMPR$qC<}.c9! 5$ 8j߼9"C%3Z$18oWQ%A$t lrcט4x:o<ޏ rg|k 2>K8,L6lx׸WIWNL,ߏb䠼?Bb<(7.jBAٮtG=wK3t'TQ cЅ*y S׋Ϸ> B$Ϲ:%S sm"#3ɽ}Epj pAb" P($88Z2ȳŐq [ )C62\]ȽT[:CB p!lG3V~ QD yb}bZ*/&.":T"ādJ/u(˟hEzOU^>@U``AhjR7QnʶtԚvBI|<W{u!4*ą0 N5.K.# =U%j#FI24Zw+Q 1 lZJY;ƵTr>ڋ|*JR I,6*"-Hd?&HfH?-pDIhsUօ5!3A*Rղm^ ߆worS,qk?PbY؎D eu;'^Vĝqώ4Uy `] !M{KZ'Ums1]g,cu%o 6h5i2&X F*;# 9u g+%7vȈxCXl"/eiL߬R8zA:u_c>5H 2@bN4EbSOLR ڕ !ӊh 8!%#N愲j4!)?Io3ĵsmtz1OIdITh12˂hlB׭rv)"j;B Rwc:|#3)2 )';«a^Uh!d}?ߎTɺ};;i`YAuA 95I@NXܵk<3T~IaoBrFئr||9ƍm-s:ԧ㙦9m.\Kv4}cx|//3mJڽx]m\Iinvz~;Wg =C2t'}KK#Y:exቭC}=o׵/7l C|lo}K.xo='.ʓ6f#S~=3!{5޻~m7|ܷ;\ .{WtlgjJ{L7wfU'gTgxg3g 0\7u҇x! R$G/a{s\PQ}j1-(8QKg$"} | k/CMhN+r)s)t5 b@3|4 2P65dqBb u?уa@h@?1z]񅚗cFp&p6pTd$IƑj|P%\Q kQBhhPaH+a%nh/hsЋ ,A 1{+VpXt:rQ؍N(s|e0x&! P6Ȋ! p$!@qX!Xv|-p~@x@pV } ]PHtthg*Yg,Igquft 'iep5Q8i !! ?Б?aLGz~t*y )vwv+p<ux,x1%}i\\nnV!c(+]f[:pAŜ<v<  (*=xt. ,}'yY q eM+p'i҅:}+ pp&:Vc]ό\t S-ط]a`3h;-~lKC-,/iƝ-Սn AupFM iFHKn?@9޹ߴ{8t`Ei.M\~q n["}%̪kLD5PTJPBU$R~Ѭ-mȰ4% 2`ci4`Nb܋aKe^>5\ ƽ}omp.'=Qq qF YDR`^}Ցf? dhZ Tېw\nKnK$N7~twmTAN̾NmFM ƮU#]+5 Rp5FV V=2~@Fb0_}<@%_|=;Q!Ʈ^Ҕ>K%i:&\O ee L>?ۦ>73w`NK^ <<q}7>3^ȝˍƌ_|~ m-qQdQm9;_9@6oxz/|O GO Y %eGyjnb%ON S>@D$L X{NP `f$  ,kbY?Z{༞,_.0$,rBbŊU5n丑+ft$Y2";2ida6đd;V1ٳv$TX c%?I=K=KP\VdUYYU0 M5?{0P GCMDdtHGkRR%TIL!H%?mt>H5OTj 2̱rKWuU\$̲oKW|EVGa !5eCYUrK t+Pu>W?qLrsCDdUf9KveWeŖ]4|k_}XMa4x9%4(K}iQ[f[ W0D;˳c[m>0߮[Î9Ɨ\æh&:F؀Uǂ->baΔM;P&+c.m˶[uF}.GoC۳sEBXVGE Ujٜ]5fr\D7x&A}m9ч_s޹62S\NЊ?<@#9s1[sL@Vd d <?<69gA,ÈC@\Amý;“8sCA!HJADLD d9N9O$ E!9:CFPXoHheD@yHĶod4HFP`<`DI!,DtÛ$C34LI;FV=IhE\Jķ0C/|JJ':)C JT4#4:G8IYIA@K|>gA$ɜ JT'3pV _B2L?Hpb\8({OO(!(䤾u|GSG>O DΠF\?:CzP HO H]C 6 LmsWs:4( hڞ=9$>۶ЁbX߳\I棆FQ%OiیQmP\js =wp (~=!0<[?Qbhw[ 8a݄@^q@݅ne@LTx= kx kp(pD@Z-.J'7(.,83s^hPp>]4Z8ڣ LCx pWt_r8؂Z) Ĕɝp`ƌ$! a؃E(෕XM?ЃUj =@2.ޒ;ca3.8}TFf Q7 EuM  @YK=XpP._yp6eS `1U 9\&<dPoHhڄ F M8PFӳXO wxF TOh@dZLM,mI05.^p&,,sȓF`؀h_(M~ o@\U=M؀^l…@L9,m]NwhNT(` UkvGtR)FnjY^I8:nf#9 4@4<,G?(X`=̅@v&oXFށMW r'^P__-&`D y]`q̵\؂J?sNHfJNOaXh=''x9dXdph:\T)$׌s@ūv9؃^JG;[:1HFI?^S0m @Q%= 0(؀! v`i7U i7`yh6bFw;ЁnwzwO&\-6m\bx"@w̳0xݳ@ ̞6d]\fma` v(9Z^0;$+n;n=eb(lu9 $p$?Qx$8 ~ TH Gt}x-Q``x P{)O>=NA=P))8DރT_0beRL|TZ Ms^W08xj 9tĤʾ+#vk8>w'XdT^gK]^,?,b Y|lFCjD;;;snᛅ p8LWNE⁻11qJ,h „14W-YBI:|٣FM4\b$+(W$م i⨉>/:$)T(W!֐¤J2m)ԨRRJ@ ĪrP +Z8DHrrΥ[n@Ͱxl,Bl/z9ꩫ.`p=vbfqUw,|VG`߼+髿> %? %???(< 2|C R 3 r0  C(&!(!F<"%2N|"()RV"-r^";MCE-1.608/images/01_Bank_Queuing_Model.gif0000644000076400007640000017016212511671246017104 0ustar mariomarioGIF89a"  #E   'C.-_  ).%Bn%8 3D+4K..CH/(11?1HX2Jt3^46/)6L$7<;7R>)EBGE*GelHLPHnIiR T`(TrU- UW#WB)WROW]ZWxY,Z[?\]H5^_p/`Nb1#ecfeeg>*hlsiqjjhjykC4oq\psc sogt_KuZ=ux/vOAv}vyvszM%zyz{|Y|}th4Äy+dYN_.gHwpf! 7BV͔xmJtKQ=pM~We3FRh՚j~s:iE+tjlԧ㩷crDpU\[knSI7´TH*D帜ԹK̺DüF—kò¾˰̿ʣʼvMCҷƭԴţ։l׆aףqؽʤͳʩI{ԳΣؼӬص]œ֮ɨڲܺ۬S߶°qԝڱ{Mծְ!!ICCRGBG1012applmntrRGB XYZ  acspAPPL-appldescPbdscmBcprtwtptrXYZgXYZbXYZrTRC aarg $ vcgt DndinX>chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2012XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"/!H*\ȰÇ#JHŋ3jȱǏ CIIYɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴSicիXjʵ6gŠ+6YhӪ]˶۷pʝKݻx˷߿ Lp6Jƭ1cǏE6ʐ-O|Yf捛GZyqװc˞M۸sͻ Nȓ+_μ%%Vi;wɻ~uwşo|k.zz{(h&LGuxU^R(ff~3<,0(4>נbɣv;^:DdwGY=$'8$- $Ԩ\v`Y=ij&|g gwrg?< ( (j)#梌6裐F"r$;*y鐛")B =Hy 5:e.*무jkBثz8\8φ6N< 3ܪvj]|{w2 <ɦ k@:CZ 0ق+l0I᜼ڹ yM̔2u iv<4ˌ5,_j2G<1Y5t<;L#|.'L7z+_XNr< l۲B=,v8,^q$i 7xvj$EJk Zcix׏n+35׹n~=cM֠NqZO>85ՠ7Z5*jSO`騠 Uo|#fWZLږ]`>I~ ⺆2clr6V'hZK'C~2ِz7qОDԺMoCqiQMr.-E t[0 X2<e&ϐ5&H ŲE#`* 9NrNR=d8 D- d/ڃk-8[AZhZ g {?(m0AgC?ÃSo҆m˃(ܢȡO%gc-r yOh9;@*Ny(sN&vf4Cj./tiuK\ȁՅfuὰo>PxG]= ˙ jE]`eZF]wMl=f.">}M7pG:3ސ_5{յIt&Oyڀ*| ލe̠1|> {p y%v"7OgD{p7D~5?v7anQuZvNq &z{Qu.`cG 0g n8'g;h r6[gD~b"6jOb L':ixj*&w2{IZP xl ~c/|(hoAxٰ qd{}PH~8HZ 7gͰwMr ِ 5hЇCGA ucyq؉\8MJtP)ƆrIY ʰ  q3sViw HZx`sFkW@q 9g^ys晹 Hk rI6Gf놐twdXyjjpqqș7fK"HmB;O`xdT?+-(a,XNe{Iihb9|? Ё. W8IjrFZZ D bk*:dI ̖;0A {w@皲{Ѣg9xɟxi; k1)o'z wZ/wN"q=qr[n *A깞`tGK{gA ykbPt1PkЇ6Pw ? F `WLA K7ezXzqh*;:ZgwLG̀ I zFtr٠k; wЁ e:~e]^m'{u%Q9'Ь{r&J tItqwo5GZ*˰+:g|y˰~gث.}xۚJ:k!(k knF {wgLWxQ+D)g{@7y֌K0+Zk& E;Ԏ&4|2_+P1'yK{q .G(06g HdA Wp npԠk{8wF{ ΚA Y8D۹qqp[:ʮ [x{L@MA|ZkW yH{g˰۶^^Y# #rYxS(';/׬OwyWx&A[QD GO*۔{pq،1s*g.w   uG+{9ֺܼ4ֽ-y5tp4,£Kfj֡Y &m*Nm9EU,LxWqGib܀l…##[Jȍ6:1Jľ*.<@F۫vx뺋jU[Ъ8T춋ti*'S^CYsLhU<&ZkԠ)<áL2Yx LM:g@)qb0\bl͐f앣‹uֺS\J9L\Χ @WpKly3Ἲ1Pq\lȥg[&3EJP5ƒf,WlX90sL=in<wHYRx='W:RyG XZMՔl֖F=C!F*HmUoQw?pu ݔ{xښ ְ̜qѴKl;@ 5+ Fwٻ-.֢ϝ|qθQ@hB ׍ݩU; G(Gzq\,rcmKTV.rZ_{ϹlAM1'}KGImֻ[q;~mL)qJ}y+Zp-g5g:*;H [W绷v](i{#{zv*}˷U8ʨIvׁ,qe>/JE kʷw k4 2PshZמt>ȡ9Pi tF}  sg־n;9-עܻ1Ѻw?p P tN\vq5?pA2η.on<I nI0.Ȏj~>,\jp|@t8K7 lMސ[on~qVg? I~ {})FݜdΎqp X~料Uz|@Fgvj_h/B`ih j9(\ҷ|j곻Vx)Fj F)nYZ Cٝ^ ^h3K aVhm3Y_ktgLYe|j NpQ]{ r5-wPW",S#Fj"Ѥɐ{f,tvr1mTXfO0-ʤi΍:)l^ebSͲ =RʂL 5SpF5y֢W-ǖ-P#'XPEEDdb?e p=K1co1PNe;%ڪZN5ږ̇UfkرeϮ "DmSכo7Nݼ{O޼rС?.rfѦYwz թ]ulP5gW0A?tϤ`j͚90Ao+?lVlA$DM0C02f`JDl-$Cо]0aBо3@)t SpK.KlMqgq1s2繇854M54,:S9s?5sff;0Y{QH=aT%-546J+mJSOG%5UN;nWt5v \U-C%P-eS5Udv)DV~rXGtUWAX mFw\r3ݬcNq,n]ߦS7xS^MN-_x") v\bc3xc;~\x-;Gw9'KYE~9u[Y9Yeufxh׻=e i8& :iznCwwVdge{#yF;V76yn9Q`op*""BlRV12S/#|-A9:m8t ?G.MGQ']׭f|vu2*]eJ h6yx乶tayOA^{{=lF*Y!ϗ~q-o;ny[7uoc`fG-xA.g.;8!mf6Fhlf<A0|#ڃ  }Ch^az ^JLbDy=q_bȵ'5>Z4QEYP<1э+&z㌵Cq*ju:n}C0-\v%èj}kVw}lm%V;|*@FX(E M KXb0- o"Є6t)H E'"rx!JHZЕ.4 M"{F.Aҧf4/:/z5KajTa~)mY/Ƨy=jR;ի&XJѾ^i:-o _&5}-ZۺRk0܈,}%gk|ְ%  \WE):шJir%щR \/%*D"8 ԉ/L;!y)0YN_'MA~Xv1й|n XEq"됢A\v9OZvmqU(}⥰8uJ[g:PC}G/T$8^t.~|) Ó pg| o -r=Ue&P Ǘ5hKoRGOzEn7 z9Q 3\~ ~qcNz$w| M>Q+=??ЩSsFssj0M`qR"Irj@#4?`NAS@s9P0@ !d ۄT0BA&;M6!;"lR @G;r b7t[r,p@+ܪs4j1k*}J)8K-yK&.9KD; CBHː Ud9@3Ւ8;pc}[D^ETD\S)-9XXLkEmn@rF.BPQdF|DoD.IGr]/q2#Rr.B>e<ubxMCǠK' zqGKDHr,Jɧx$F(Eȅ{$RH~MIڀ~0 q`U8=vs2HdVP AʀTpDNG;!F ɀkII|AɓHɕIƞN0KTIWE>]0R^<mm^mي(U^ޝM_^ߠ=^u_ܦ\XBgYp`[L]jXL2UC?ha@8\=:ha?x NMhna:[kb!9m "7Hޛ9x~ZloEۥa<^]08P8 >a NXc Z&R<c0V.v2R8ccN*(6bۄ농 =c?N_ V<:+p̼i-O䛡֙X]v#~Ieܦ靻&va"4^*Z܄,PpZX&W%vը7`cKfYXVfU__3*ijv-:M@XoVgcgwZjWtPvZgHehC$ڡjN7历Y8WU%efdm׍U:re @nffNj曫KYhpV`MH|g~bngy݈gPi0Fj?Մ}[f9Xu\2R: Rzz7R܋-=ݚac`cynvZa6>0dɮRePQ^vil,gCl:jVEiX0,XFdK1m1m`a8R[cdg?퐳lVGFHߞNmEhm`7vTjTh74R \Ϧ>VĬ&TP7D k|.TPnU8XP_s[ wb ;KF޻qK,q#<~bBq厅 > H&r_#pFqnmh&H֝9ZJ|K>ZB#Q:eN)KCI;W@Ҧ@|N@$7<%c|Mg\aH)@K⍨R0V_H@dGBR<=igu܌*t!tEW~HGNHdWUw9NryUh0jlE9j"]ߝ;VRW .9ˎ?LH`D^%sWZ8- އ8RxjMo#N)4BG܊/G\aT-4"XO_Ff(+N@ܒ'v@*H8;Du'\-T_u<6 zOz0k'[CO)k'4udG%o`PeԵ&l^mzf?;pN(儓|_RtE\0An>W7(4|㮄PWHhkfFpN}3;"PYXCEOEʷku/çħcTLɏs:t=)R,h '^2I;٢*,4BHhTn $;BrFF.V fRHJ4,N<,Eу (G4-_bX 8u%ͬXXҳrҭkP]mif9u >7|瘁CuցK s޺ą<iuQVM::fm]^d˪Ӊ &I3Iޥ:ո-VNf{8\RlTyoBgXD }h8'!B2hzC=Atw_zXKU]) x 5\$A|E/ymzFc"a&v`$c1F 3*X gi*ژ?"C`4"MD(uv¥H6C"ezmUtP ]IBQAeI&\FF)ogES~r&NɷRI4up`@(8YâE@& 9y*"U`, .eRF#0,tOA, #5:!rvUe]%NDJ*aT]m1|oCɛ+Zg{sTèoIGzA``T 'y9=oz\$O>3\8Xd:Uɇqc (r>*s8\GrlTQ׵eXW͘ՓVP7svv"6J[)s ~4R%h-,eRÚFЄefJ}y^{Jĺ\3ͳ 0f@㱦s찝,0c~nU7LXKQF ؒZx[x/OIi|B?4sroԝg='WUĠ~TVuUzS^?(:xW_)T"R\XU$(ɰ"c+GQ0lљ We1cI\E\#Q& BO9! 3)mؓוx*-Jǐ%)K,tQER1T)P]@T%.'\Yst dk\FG:A}thxHEF6&|cRإK.d/6A!u+%8zL)֤1 _JOzb'urT/  *LbW6Q&[5jSiCO&ƱUbX3Xd/zLdQ+>'#:AkgYdvո)5b, .g,[|Wl[C9r2)J]MӰ'7X*3 ⿖dL c^q~oBQJnE\!ӣ\l 1[WJ.~,$a\ t*2;ij`=OZ@mTT14AGc p@:q![)Q9L:_H#Z259lD2")oc H' )ĕ74Cڑ"QRrc/Wހk:Qy| I 㐍-_)rMn9>wMd+J^%x[['Ie$z >/⋡͓5EMd%4F =Y? X/u)C05\gOWH"LR #*j"#e2j(B %֘7?`{KD.x'cw9ɵTP%V.-2V*SMYkFhxݗg'O["Χ\,کu "B^@yIk.R3L(z`"[dyE qm&7ZSspMh,5\՚1D[ZJ=xGXekiOu0 l@y\p hG2L7NۈF-#xF&R,y.ȑ)a/A^D #%⊶% ߳ihx]`g>}q{GT^u@&)ȜPi>C:p0 ,-v.48C-hmpB&8/ X38C&Dm-D-m]/DC&4zE#dB4-DC-߂:~^ rÞCe kq6,88<* /1I?*%H0?8-?C-dObp9؂@3C%FC B32İ +Hq!G:(BӰB&+C9$/ܡ.[+82p-0q<1/Xm/k^/ ,`!]?\n 0<8W1bp p 2I7l kp B1#//]T!;rt0?m/L/#|*74q#) ;C84;`r,opT./Ь]r/2q'@!r`l9$38 i0 C.r #o&?!G9K#8a77p9×Wysyaӷy9y9;[8`9*%c7H:p3ɋgy?ypϜ;j60>k{zǹw3ɖ:@㆙׺ÝH::9';;g1m95Luys1;~.yOfK;:s9{H;7nL:t{Ȱe ȈeGO#o`}êUC5=k~^b 7p2t^l4xS6tbD RcFO9vjWFwxQ#YBc&GJX)f·|N _q:y:Q9Qzj?[vlXZu̕yخmVyrgٸs6۹q_3u{ qoUf+X͛9w4ѣI6}ujիYvvlٳi׶}wnݻyw.eZlڵk-ǝw7o`r`†1c,a Km2~8W <\|% - 50tمD 9:9g.cy\T1sV;qj="$L0,ž \,ܒ.0sr42D뮛-8ǁ7N[k% I&?<f&352-L5ݔN=$ĴܜN9ftb=d]~A3XJ=1E[vk_Vj<¸ Hr,QFSWW'/6Qf|ws&^iT:禫9 .WsW|Xtc4&'{ހ9J~EM>Aظiz1^Ƴq -wg =T%'Y d^馝T壉v|y:3몿'yp5tn oQFMcӵ Sޛ;kV{3cfSeOf 9$! ED:}BQOp! Y" =b7T(H5% `:z86'<vhA2cn H:H~89'd(A?}@*?q``G0 -T@`il0F7J{ۣHDQshS"Cb A' A?!`H% 䂈P@  <%?' -Yү@e0# QQd1iȺa[Xђu']X&Du;Y:F4\h6w^י{H6ӷ:-P1 P#;8~p`@ @pE%:N H@2L$h˅I8IԖBH ْGf|5uMl\x񚷼dw{-6 ݥJ(@ N8؇# 0b=+5@tD: @kс\C_ꩀ'pr((7["@0pz6~!(rBo~=]ed69r];_Eַ'#0pi%:U T1?M R; 6``+#aܴ IoM6@rT) }D\fvd!g aPKJEJ;#}5iZ2I_xC(UCU@U)Jx161Z C,@ 4̅ !ʆd-` #h_ [p_-԰QZiV.TSK{Ճ—L7δf\%3lei^?fv psh;T@* PxD`)@vIHEzaAh`K-\WB;2;nH~\~ @|mb f*'hRvEkԢ mv.X*J VV%j&t oL {Ʈ9g{pFw}pXGH ڇ0.`\,u_? 0`ȿ`r_ybe֞8j7Z@bYj .ꤊP&ڀn$ឩ+0*Rp_0tn^b!<͔< hL `܏I No`œ(AJ8doaM\ H ːPlͥ4P K(F b )Q@j. IΈЗPJZДj~ά Qr Umʢ^Q`g SQd6NN~lH 2I@`inN1@+ `Gvb 2 q*Qꦄ+` Jo^EMG  †rlr !&`j4 !H@a< ȸ^@VlH`KV%YL뀮`@́ < h$~i@4`A$x` !NL"Efel,,,R_?-ȉa./ .p.~aR.dܒa̡0+-+Y&,pP4C4p.."7^2^_@\6_37aӢj7is7Y_:c)0S9/P)Us:3`1rtS<;S=l ;mo<;H>;̳s=?= 99s At0s5B#=À]4MSCemCŐCWRe$DOdBB0_ϒВFJ-AQTGwY[T1! ݋ t!I!!GJ?%vNHW-FRFFtFmWv15JMYaK?s4;4<OTO5vMP-NaE+BPA!@5A!Q):-nKAB'm:S?TCUTGTKTOUSUUWU[U_VcUVgVkVoWsuV́lR>>XYqq/Yy<5>"?_`V`` `;K#pa3R%P*u:b+b/c3Vc784?4OpO cOeSVeWe[VZFLgvL2-ϴedzghVhexVhVi}V!^AFai6hGAB$Zji6ZVjjkVlVUM!jvjGVRtm6iw\oopWpIYFpqr#7rBA4M!Bq%s?q+wvDt`@Wu'Z?֚ۺ8޺>zz4pRtߋppQ6p >ۜ[Y[GY9x;;|ivLqQa[[D]z,hzې8v{)۞[^`Ya{֨bW{[Zېe8[mq{D{1y-8ٛ88yQAz;z5|~/:>8H{%}a\XC}b[Dl)cI~)}^W=:0֦:!8@@U:s ]G,J~} ݱaޅE t^+|~3}}@} 3=x pX?ߗҏ ]9`۹bh*;fzck7= 7}=}waW;ח^r~#co7?gU4ڴS… Rĉ+Z'ɡFx\9&̢"K$I$ GQK3ΝTS\`jSbNeٵjz} 7g[4z&T&#.e,D]tR;7 SM[SVLNJ @rF< <:, ƒG!]@vlڔL&}m#tɗWw~^כW'M@]sM@=۳W 0muG`wsW_&aQcAe3A0Xg!GS3{HDc~&kR9v[nuJp]rc8fd$t@AK0a}tmH".֥]^F]x詓ϘWxgil94BF .pEF)f٦: 3|(\e c |]`Vua(EC%gG3Rl((nq _E v*I1הMg1(a]&N?u Ppt9`OoϠdOR-!% JoB[70!3Ʌ.`&mta ӌ'KGI7XOW@d"TPR$cK7Me/r?Gp30X= hBo)مзTQJ&TGc"pSndC \B[(Μp$grI3sHMO$thH C8,X!VU0^6nS`aX^`TIGU#l`ːj䚈&k؍-F;fY3ܫ@-fhՃ܁ZqZ@I< H9n-[\ >u6O{NGȰFX]uU{ &nD(k p0+p'28”=xnY()Z 0`At R[H`U,O$ɝ[*. ٮZ xMCkF`L( Z8QSK] `',׳pL*' wJx'{j-ͬᅈxnhGUnp]j 8{^mt>_ӧ7N.`[zitww989%D0OYs1M8ѓ_YʲUK v P0lWn%Wl1^|ukhi ` &W طXH` wxeA}&zw Yn;! jnPs~,y/hYfNzUP7&VG` E$ Րz5[5mvgml[ @w 4xnF$)j0 &+G y1 '] P p Q B 0( ps P 䂁HZ%uu P0 G pep "vRu wWp0ЉD p(㈊ō0Ȏ% ؎H Ӎȏ8 %ٰȏxmY%G Y X َۘ見'^'pwb Ux*0# ڠ hpGp'E xvPpv[{ta EiGIpn J QyL- W ~hO]YT(V^ٔn 0X "i)P֔ nykIkksv=\sP1 9 [0V[`k}7fggW7eɩɜ霦YbpY. [) i7qpjEqcyv?0,)Ii )^O7u`GK)#0yĨ à[a_ hʡꡢa$\(& V zM7(l*CJEjBqgXI1Y[Ф^ 0jـ;ڞƧ[&pikʦk儶+9iX z@KD[|g?@!iOЦʨ(J )i5pv00 8\pj| mꪯ :b*JpB`G19 ' ^@q>V6h? &׊٪l0 ٫&Y076>) l0IP ʯݚts:ګx% 5P  J  q #k')Kzٲe’ū*Prr|=yv ?V7Uh(`* Q+:H_)JH 0 rv^P"P &8hQ` i5Z{8x5+y]r#]I0+_0 |%\ alKOK [k GKm @c pg嶫o˴0[٫7Dm`+zp +|n|u~ ѽkt 蛾<$`z+0%pvr@[uL%l1K/k>p+7P_ / 7 I@[̹@kJf'LU) [Ð&9 vó_ ?k, {+m{"l{ǰZkVhȅYvJ=\ |wf}ɛX. ! K0vn,~v3=>P 6v%lHmɽ˰u'P70 B[B% OqK߄*)YDz`ȭ! ܐ2&$1q a̤D, 'k`7IW$+#jJx%3YwS= &`#.7(F~Qr&R?Ii.X.%L-, D w⎼0@,$$I`aѠyZtLa gXC@Xy tV4YG "$*E3Dz ,!ax/t$)K'9?jt#mFxԀ)H"d^vXUhI`@aj CFXH<*pZpJ@lVJ:-LV VځyeB%dP['(d0B6D8 !.-m0Z7 Pp= ,:\aHWwn;1Uy^^J`vTl M_dpZP\^(AAT^#642Hh`7Eܠ ֆ .e^b* |b Pq#Uvǐqu+ʈ=3 Q%P|EpN: UP8nxrl.!t,>~W1+(yZ v:\yIpmoѹNJμguV9q.UԭxHgQ3OH MK-js_ !"}' 8:m8K(z6t)s-ˆye&BPñk1I[x 6$)yBO (47 BQ0s njT7 2 @k[ !y!G8PSGXlle8 O2vt8 x9Dm9Ԁ@ жͶ&ےY6oTt9]i w%uƓ:9a"gukqa\>9 @ J=X dQ0;`ȣ(S4[ DodEtԨ9='&y:3p6QKITǝz(Db:qtMWͷ> , _f^dTJ7NҶ)i,.;6a Q=˔yBvDž`Z#\: zn[^$Ƹ$dx  gI캏2"؆O[;P+i<L!Ri0I"(` f,("$tК81|VP~s$8cCɣZ$q Q":[t_2OBc%~˥A0y8-[Sn0JY&mAdI&Ӿˑ{)wpH99X98?њ3++w1Xlr3܊9[C`9; y 4CCB[CE@4B;뒇 (2l5 Xh$>B0Z-DVIЀ18#+ > :0(T4| !91r4"` hz0$TA!$C7Pi4?ў TMrcEY>hAdAǕt6k&샲W2ZIa.۽&w)Ӣ1x0rE{3h" "0ȸnX. L: ZxH#+OFPjAd:E+$XģG-8cHTX.#<{)!X,@Ԁ$p!AG9 hਂ8ڄ&z.)p1"C9X{ x8qT iT K"P@!b23h{a"ap%, B0C 0"0?az xJ 4 Z+ X H0*01O'Q)A0|cDi)UdxzxuXL&؆Z0h̵5F*Mw$][#]CoЅ{W$J؃:]Hl T  e :%+PЅp[#1Sx]0Յ8TFeAPMfX>+ eӄ8ՈDS׻[( ȠFmZ}k]S8fhףUԄTWk859*=t ʇZqdpLhll! (|؍H#c"ؒ5\Rx1-]Ycmؠ i򨁇LmlX(88٢5)$$ãڨJLìZR%/4 ȡll ZhP۹ sۼ[uW?#FAF`^ FńY 'bPmR\V&b6f9euE@ffݪ[>/@c4a8R>icpKfgfVs^k f"CFdE6}~!W\}eY.\h: lfhg @5 dĬ૽X/z5p:=m\.隶ibacdybigh)&,*+*./ H.Y\XjhjIvkpcccw/em0]xVGh rlI~eJV0]&Y>^SjA_\&_XnrNEgnwDÅ2.gFnV^Fã!g&m=mN_<D/d\mjwVaJl 4EÕNd6 5lN~Gk-랦wA3&jiHFX!Rb>r-j%/X pO0p@\.%1's\f:nqu`RhZXȞq-G2?׳Ʀp~l oHs,~Lpx̭n;@NavRm_D'PZHOj>lO]n6↺6dVSZfX7 .8lvs~f``bGulj6hjhgWrvm~7oԞdF7ZPu`X_=xԍBxvX9ZuxXf`YLm3kVs5?k;@_%!j/Y//?v#_frj$j1d7( rk¦\lݮNlyu2Mc6{/XwNlB@n;'g_6mXW?o%uu 1)s?sxW`6}kw}hX\_~X^(Inupwg\&_kWBg`[~v[(-oow~Ϯno "Lp!Æ s 2/b̨q#ǎ? )r$ɒ&OL+a\2:uj7Mu9wĩfPCyIt)ӥP@e0s41 |p,PAL0f!GQ-ݺvͫw/߾~|s:7 ,1Ņ~8_LB 'Ĝ1c5P7,XPMU֭>fqV{7޾20L5Lr䒗''2u/ 2+a\zͬ0Ԭ7@!\佶'ϯ?.O>38`&H ( J8#U&Af?4 buM5 4 TW%U a$0ռm;أ?bD`EGs5]b̀vǁ yrXfP$B2~S{pɗf-׈٦o'^BgbdwBdL6Y ]YhT(6(rک3VhI&fR˪Z⚫ګ[F'P=UœP>KG(?d3R5UY1:PP xIԠ2|q 3˯&Dpˇ# 3ܰCS\c'L 54t%HN)a-*\d50s@&dւ@Tq e K3ݴO/m!ՈL$`!h _c]gkݶo_17 ,GW%/WvTܜA!FT=7xhR{Vg hh^駣ꫳ޺/Mݱ)+mmLa^շZMfH9Y@5J;͹":s߽߃-;ނՉz)Y3 B'ڝhj><>Af1=s@B0 +X.oƁΑ)q0IYfn8)B)@JB P< K@ 1B"4 NzP%RȉQ$P-,@Ї*DhDT@89 !HPbxh;1C_e$I( 2,<x`ds3o,66kk@';O2\0G=2\%+/8$jO-#qTP@>)j m" PCF \@3Ҝ&5 Iᇭ&7!/ǻr.(/ÌA-s[ ,-x\ix*aЃ"4 ]Aq1}3(EYN e(і9K Ù3@₡:ݩBqmV4B*Qvѽd4E>z_,ERڲ: ]%K)iw1xjV:E]+[ѣe*; Ϡ0C)ф]-4Wg*n8+cw: <5?ϗSfO/G`*2k2em,m AБ-oh9r=p#B Vh@gAf5(.4ނ7UoD/NCMz ZAb/#-uWXӂ@@ln(wC8m嵋ka^X:Br3HJ*s`CIv+1k gykPUݬP;j4cx)TV̼H30a7ZxqL5~EgzκuF rAKJZ[2^F?@K ۠˶ |}Vt@+|σwDl[ˠt^VKRTz'Qқ (ApZ%=!:gU3O:N `4زp Zl@ . (@Dm]6@fˀ:4}s&`6iTnl/ +528gB &k,,M$^izo54 > sO|Җ`x]^$W,=PӼf~y i7/镖S>iӢcvǥ1u*9 Ax2.N} B"Kv3GP lˆ-ImO@Ǖv W4 P@E@di\A\9@a)-[_B̟)A=^)h> ПDՀҸh8 `% rr}%a[ ,Ax@X8 P2+p!ᇁ$@yHAd.B6M1 \A+% AY]_]eA0\1>'bP\bӘ\ @T!,j ("T`Z `'bxt4[6&?2Z>n*:A@d"[f.$0\/[Fi{}~$z{Qh@ !!"T/T5DPU%%2.]< Ҹ\'Rna6Mؖآnc++5DI9b6.AJܞjNj`P[j4ReB^Z*60e@Zk #i @ +MQì\b:-njjp[=]@oi<. >-6?Vnzerk瞭j೅)*oIkkͱY=11C1#a.Zj"9  'n,z)Yps:YppsO%.`=xi aj5f( a.rB!(rҦ_2e-'uDr:u\E".Ϩڑ_@2M\^hVk'rtoVhj0wD)?s@Y0d*֩A]AVʢi2ڕi?v"(Fl'^ fIS#"TdR#*XdM_!.pTi|k㉞@b2<+ @^ZBMgv$?EBlOXRuqsv$T)Cts1j%mF2d>ungwvy' v#9Ak].1@b"DADNTrT@ߣYUgyvp'^&i g,hv&'~ TZ6M@vkhr6H5sS2c> \d+)`|M396,@ywy6w6]Ɋam 63Mkz(ˆwz7i$RLL$MWKr3' A-X_rY*=Hvb3R/x)BY, Dt#̥m+CM?@ ٕ^:)t!RByy9H9H1q ]c9΄o篡mUm9S:8 ekz-{9딤W:Gʹ:w:CzsϪ{D`t4gy 4A/+TzK;zGDb"67yP9.Msĵg{o{Lڻ~1{ɖ+m%9 o {c3b7x,1dsF {?{#O`O.Zu|]:pa{¿9[E"4$̖CC||FǫE|k_9O~8S'?<;C|c~7{kgxG{ӠAL;C2E[?́7M)]?y xkgנOӠ[{0?[+_?@)3`2fQaCAc>1fT Eh4t(cABTeK/auK+Mәs'Ou>Shϡ@=z4iСjU,*y[Ǩ;lٲ` (bƕ.Isx4+ܹ;ڊ Q|&zc1ŋȰ5s$*vs\bi#S3(cCLs5|B=q8 azٺf튻K)z|TMC"EBDX^,(ǺeC Zq,b Y BqQɰ4 3E $)e(/39G9'(7s7uM<ܳN=P>sN7׉@4uiA̘A0"FBJ9ΐb,(M+ TT(Cv>DK/- f=LEƂ*-%h QdG2MBmUR3='()p%7t{ wvE]yuk[nMgk$F"Tj`1IIRCąׁV˕R&kgE+K6 II(Q+qx%)CQ~2,Pa[7m΃h%1IE0" @0+_K R0@+)L Ry$<\BPj̚LDYd]FQE6qiLBN[]YY7ֳ®MRWcS~1> Pk$T-*ZVX6+JŐV?ZNM%SF5NjRZf;5|X c#O銙Ƙk4&^òZ+`j"bv1E5,sl?FSes@\Q*Н b16ެ_Ļ$bF孖Fr5B Y;f.>YXDRK0ZEyOư4<Tp! y-`SxAΥQpXv!?4bPEEOw>4aaE4wF0c B[X ah2\ ,c!yUd00޸MXs/t ev .$\b8.8lE=ҁzn݂,AEy8@M,MA 0,mlK1<Ҍ&p&Rpd1.5l΂$sp0./^A2#,t,,PZLR,ȑ,BMޑ;,,. 0Z O\,V g/.r"b`|`#,6Hx (3 ,<D-"۶-0rIxR.oz8ЃM.::ljOJV*2bخQ׀ä1,rm-BNWP}@hb>q.+?Oˊ9 3 /#t.&+!!S,8/<,ds12/DBx(PK.bz4 G &O|3+`R+e8CI(G I/XlLYYsBk,TrqRt.ʔ,ђSS<>N+eez GO1UNsD@u1 # Q.U._or!2r1.u,O1D9Si2VKNFu `TG@֏lքxU 0IU`˳5YUz|^FXI ,[[O=Qu\ Ofő/ԕ@ ]ߵRՑ m1A͢PKPZc"`Q! $UaC5E0#'V%,^,R$!l{dM,fn/E,*u4JJaueL-;r;s;hЊAl.iL\xW+ UOxu,6m0  r1% ł6a..VpMjSE/$M uwz!,pS,zApm +qM5[łf3usMCT8X\x:@v,5d,\r[jBjjaMevlxXk>W,NxNȍla䐶, 5 -=}~ ʕ*u.H3j1#.8W$Q0"lǤ0O LfeX5fK+)r)E9Iy)ZKR 6̇BMj.$ & +1!`|#ϊ va!|4 ‚@KOm֘v/NWl<3[ ! - ,,8w,D !]Vtb@۔)Y@lIKOGڤQQTYPgW.A{ZƎInCdW!6Bþp8bNq˩ iOKᐩϧ}Zp'H#wУʓK9zM9wHxF9%.d{J+njM7ꀭyedpvuSEI[LMŰ)D9y$uH9ĬDIQufu4vQ[T9XS\L`[Id;hFl{Jp[t[(;duVr6hug lYkQ+c)'m&k_(ķ鼹;oj6{gۛg-YVK;DAƿ QDyDKbwq\u\vٰC2٭Dӈ:]a^ϼ" !۷D6 p}J '5烆d5\93@xL%'wac(ϦN>z<^SKc b~d 6e8)=ax[8Ii"SD$/krz$CA~ amdsgxG҂¾Fp>_R d{c5@E$$fX;}u;y[۹k-[ri/daBPA+=J`FPagiO ! +ze!( %A1f`AY `Ss$:FÇ#F<LV< *C C6mTԨVΫT+׫[zk5fւ]˖'5P5 3Ȑݻx;3qˬSwwYxnaĐxȘAM!̠ 8>C9.93a Ա*D4܈Y.>o隧q3yͶس %j)UX~:^|x_ǒ?UYg;Ͽ(h&]'߃j UQT>O`^"~Hb#*z".nh: 8D<@)Di99I؝SNa%(NTEyUZZeXrS< 372Hdƛp)t_xi}zI~\} h}h.硎hNJ)&NRԕW}U{S:ꩧ**f֦9٦A"ɮDtk챾4hF!묱$WgV h,{xj' ǰ)6meݲ,"uƇΚnFiOXLE<1H0S<W"w4^Sͬ ?hks)L4Ss-t Ts‡e!4$4eKt2!3 A5 P1\t .ܡ3GR-1 ] ,mCôb}C7=v(rd-vq!rv *嚿w+˓kfcHk* r !ݒAF7N;5ńtAkپo);}Cd @~&<:1; K=~䥳URS?[_1L8S7ܪhK_^'Bllӭ; F Bے]ҽn@۠4 R#`S$fc wi8< @ eчI0tlR;Q".D_\QcԢ-QFꠑ0E@BRc41C!A'm1 gܝ$aMŽ+bG&!WP`_JƟ,L= zd=%.㞳A9]!~PYcXlҘ#BV e(Ph͂1F;"DA*1Nlw(Cx*3BX{n ]2B{ EDBC  `Thbؐ8jBm'FAm0JQCJY TlUӡ hxƩьb<-ΨF O :$´25L.Sl(II> & ajCioj2=-`2U$RjǦS@ߗ7 ,7@*:r`̺LAvd`jG髫cWJ~,nGXv-bu;5eT~\X:톎]eeݞUHE1:ٮ6HvD26O|N OhoW|A ^ F.w7\< ٍ@Wp~y!a >%N/0"v.zBK;O(Ï2 Ųuy?LJ$`U<"K! HPxtG o&ca  H@ P' GbrP} {p 7 v]i A qC@ ؐxc0HfxS' G mЄcpyyyl6\Pn Ԁ w {Fz&P7PWЄM{ q!Aw3w < .J( 5( \ H чj ?&Hip B4}CjUekV(~=~G ; Q ``"A{FlP y׀b@G x(!eM'!8P+8 1!hX7\yQHHz7q{vX3pxunHh͈R@ % <ȍ؎CUВQsg]Vh>YZ@VbZk#7f ` 0H{nm԰ O@& Om d'mB0{ 

}RE[{ղf]HٽuIv;zgghq uAvQ_omm jp gm ˩ ѯxQ(z-+  0xh=mwɘL-uMPI 1-UҚ97Bpk|QP!Ḋ{W7zYLܭ%M]ؽdbW|+r N pʢ,, apkc{pznuN$\@ޑ$ ߶ qP~xL63 8) W lm3pt:d ~"+Jwj@6z{os6 <ɝdK^S&::߽Z`^a[w]imccmG>X$BBK\@>HZ^_Z#fo~pr&PSS65TtճL~NQVSbUbݼB^=^_>[.A.A`^=N\>>cN3UsՓGz\?GT|_,t]h.A;=N<`<<>>MR+o Ab04I jGhzvM/iv@SASDC6+U;Ro^}V(A e^Ax@js PXeSEIq=$Sj7`wM FQf >.A6tAZŭO뒯__~% b6E`T>4_6_RCRt% %O.uJvMo/EAacVDMT Rf@ >MYN/+} h84h EN6$4rMX:\ }`BMH?5;;Ũ $XAe)Ӱ X %NLxLw رc8ktIPqؒ1eΤYM5e]Jq7O踠CMJh>B2jSQ>:u̮UY4j[q6\SJ#!Q&$v>5oC<ȲPO2tp\=c%DEKܖO{h(t|M;Sw`)c i}s<}6zivٿs~ݿc,tΥ_~C4Z>o[3B~;8p I,`P-3z1.+$۰5l-A|Jfl < D+4bB KC=YDHGTTVS8YmLe([EUԐS'DLt8bpZ\t.%EZK㱶R%Yn eGBe!jNzeryαnKuN_5w_͗``eW߰zsc= CydK#^Od[pߺcek f-y>p՝ng Ci} vhjk裁Zl{uk^'R/Nn;n񪹆ڶScwq#|r+r3|s;mŧ|\}3LM6ߗM7/N\{ܡO$` x@bN}tt\=4#ȥ s`>{ $a MxB0w MF*[W~:aR2ĵ,#TaxD$&,"MgAjb8)G;a@%Qc$cc4QkTc )y'_c͉{r xHD&R{JH4DR$pIL$!IPR$')GBFJԡ z|`9=Y,x"}K` kC13Mnrs>YB@Ӥ(@! QB.| ]4F9yNtS\g7vBu[ YJ&Wk% A։fG'[[f7VQX q{\> dLBÝڥo}C׼淏k?9Uo ZhQk_G8'K%U+|ab{%Vcz^xb+]HfqJT1Ki. qiFҽ=kb$',m}By{y}|EY(PrVc&M(a4o8ʹ\Sa+cYH<,=[P(Uȉ: z` @$8 x-?XSCw -X*h=E.=:3s3s*l*8۱ 9{4s25d0`X h `o0` z( (@ ";!( 8*fP #8(Cch t:p(.\eP{3\N:($(N X+4EAkT ɩ68;Cx+9Z8!8Hp ,$clЀ> /b`w 7H iXC8(>wIH ЀE [Ś /P[o.mۢ(w<_Th,JO$Ir dF_Zd3r6j "@x@,|Q`4S8I#:xt耹\ǽI0+w# 87\(s< w`t (MIDkL(<7,  XpDjx/x55556+jrjeQBd$eK2;]P:K t8j _P*Ƥ 9XuLO&x ͚μ./LzxGO`Ф G0tЂec nb(ԃ(ǘ\` @N,l)6+77#ʳAL# 5"KcOw_p06L<sdK4OzSɜ{uLt s8Ҵ "!LXjxx(d Hwh} (j`XSEͱK],8_ F^FbK]&OHOʯI-¸0;+^=VwHkpDH>E HNfMdb*!*M^0ϫ2]FZ#ӤRjShieE..HNA W{ [ʍ B۳$0<"x.ɘhg0 gf.g+;VOmW؃+@1xg[x=p:$aVBsV&Vd^4^t SB bZ#:,IJ$]&Ⱥix51*%26Rlj,.-i/Nf8^TJA\ڬ5I-ˤgbb^q2'1j\Q=>C^&` P?25,2l^;l.k$ob[ϢJe#7R4E,#A抺(("n稃aݳG I0E 1cj*4~./׮~(CiORZ,&vQ&nɄS&=kքVVF&3m+tjnokleUesp/p]ݳ=:p p pJp w ^ pq0TU6^Z9]qq/p~VANiuqqmҨrJ?r$OZ#5'Ď'׊Vr+r,g1 r쮩r1s2h3'4/s7s8A:?qg8s>s*}t 1sDOtE״tpH?;_tJ8s;ozeKtPhe5?4A uU_r.uNnVu[p&r(&[vbwmW'rdu.bovgoQ7uR_oFD{vltH|Si$S*6%,Hb,_,o4.1589EvGD7J_oKQQL.MNmNTVV#Z,e7)ggkoG2riZrmos8tL?tv||ZOޅ-_2i\ȍeLJDqʥyoͶלMܜ̓Şşfwh2٤^yNxZgỌ̇̄n˃r0ط̴»~Ƚ޾գf߳£´ȩy,.-X1?FL\G[ĪʸvʒƤkǹǻO3{SȆ=ȏȚpɺ>.c[ʋN̬̳ͩͱκϦЗ]ЮёӵſܷӲӴӻԴۈm7ֺ{U/ؓٶڻڼ™ݼǼɝʫ̱ᾙ`Ûæͬ㩆Ȟ܄}ҳM̡h͎ѶÚјҧ۰!!ICCRGBG1012applmntrRGB XYZ  acspAPPL-appldescPbdscmBcprtwtptrXYZgXYZbXYZrTRC aarg $ vcgt DndinX>chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2012XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"8H*\ȰÇ#JHŋ3jȱǏ CII\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴiρC|8꣪իVihʵׯ`ÊKٳhӪ]˶۷pʝKݻxuV 0++^̸ş=l0/ϠC.GΦS[װc˞M۲nwowȓ+_nܢG=N}nim M,I>}˿Ͽ(h& 6|9+ 3T 3¡5"z%nh!n8$2085װR.Ȍ6@!+f䑨v8Jk۔TVi%loSs`i>J"bx酘if0N +䫡5)Wj뭶ږnb\2z&ty1nw1i%w'>G%+h_dh> b)˩K/*d/j@ ;k k1rT[gj&K޳z{vfqH{],4lJ8a={X? H)<4 j L0u\0d$p`dFľsklB˖\{,o?޶||n ܨX>(yʡWc]&'kF:ejhIl[#s7[7.g-u|Swz 7͊AaR:鼚>*꥕gR +HFَ8r:2:ïZ!ֱ:֧LdvS4=&H Z0Pѣ:" D1T,C#E#QzAA0~E-.#C/_7+) j"V_m;Ek[;^3 z` #~ uЌRH#4+j@"+3d(#S|/:!KF" ۈ#ʴ49eҢ(R##AD)̵2|G$uq`HaQ20)X~,$"F:3WqE&HE:5ZoT W9(C"dQ<{d9f0F7B-XT0)(4 Q#9Ȱ)Q3':h8Tr(Ykj'n&yMObZe9Җ4qBJhF&=OPV(hP鰡UfiIѪ V%T]6E:Me+"Rїp+ΤRn]d9~)EU}G}d% {h"iUV3PRb[Gl=w'hֺ*F[UCN+Z5GFҜ&OX a~mC!QVxֿnȩіu(sYw/*jׂPHhOBR/^%̎Z=2tOn%kE^wn&iUE,Kw&;'UA!/+g\4RyS5h?4LByS7]8" e5&­]o8|8^%uU;[2Q[TLWR`yKHM=kTOY2GE0jÄ!R),M˺ᲇ b0G$&sb-:b^8ueJ{2gl+Y3b`Խ|~ψ0 aT|eKjK2$=jM7MF'~O|Y;vQs렙1H?s:xwi860mC4;8"mTyնfZm2#uۼh&Yv{r~W>(ިJjT`{讇`D W8C|>iX|P[7~c[pi!nҦ؊^/= kezTPɨG51o`my NGf2x_ZI_LC]Psty\?Ndz1䓊ջ1Ֆ$gO{13͌eT@\./~Hg4Bx:IUNH?=ovm:Cn:k^z׺-~S7mm64tE\õ4e[ lL  pч$'ay6اL[i\,&̒ Ӱ %'ov'ZgrWG}a\${{.82rB*$dv`_weO|PtPG'}IwLA}U}8zA 6V @)( !'-WTxxe@yTHIB}Isr}6s&$s4| s"$Dw7 pP5$Cf (CQ8S(x:ȉfU(ubڱ 0 bhVTrk7ZsH8k(SVPRo*"`o4>3w ɇl7|  0w$y٘[ȍY`rSf 7k֊$-8.IgAB "o3 #h!FC9J#p$ YCА$XyYw+dUّ*,%gFsbb(jl/ԋN3#İs6Oĸ{v_pl  X P (iQy?H\szQc5%ÂI/X+y-ZlYwIeWIrbSx$kxƞŚ7F"w>—x)J]JR*u7z)%zlؙCfZ^uitgԢS}_+Or$tD)EJCZڨJQ*W ;De ]x72zVj:tek0~k>Cl3>:x|z JpkF{S ʜ;:kJ]1"Kb<&h([_vkBkJ|6Đ=@ D:G;ɚ ۜڍڸ6R\[rifnc98 s*so )뚞_7s:lG گ} 6ɚt{ѕɴUkaZjߊ'Eak3!/l[w!0z~xS3|舸٫0[JmS԰ pT˻x*&u3~Y﷽<8j:̃B9Ƿ̘;{x'z4,*+ 5Ǔ8$FtÄl=tŘ+C\ElIL`Q;z/jV<-XmŤ.&k#LW<(6r{W/+O 1||AL=lF]|贋PJLKaBvR\&2Tb<ʥ|Έ3cuW* B`K۫ Y }y+Ƽ?֗ͬ ͆4͓SU }" "mTEaj\~,15ںKi{hCu j0ߠ m~[T{}̘؇g}ҌĬU]K "=zcRؒ'(vM(^3m/̰?{o>T8[ x A P-=UW0P̜]}cM 3 | jdJύW ݃; =-i$*>]P,CZռګ}K,zM!~R)D>Rબ3bKyxSklj>*C `.xΛ3 -cNUmMz=ʤms <4Ue֫~Z;~N^1 'hxAs™ffOowGI>өQFt΀Fm!/HmG*sU@w[8ڭ~Wָ< /&h~%~$gB~8({ƽ!>lw}ݩ/ⴟszo  A.dҦG=ZTŋ4>Tȑ!B!E"WI)UdK1eΤYM9uٓ!N iQVGJOv)ʌѫæ;3>UkYe2`}n#i_:Ć V1bȑ%Cv.r9̙5o<3bDnWh}4vwkخͦ/mܹ&woY!(q=zT8HѥO^uٵ*]Uw1Eʴ5VFNԪ{V~>lՒU~c¯.qFh/s*S1 ̳ /lr.,2;M4Hk-j A|iƊ(Bn6ꈹM4H$TrIvafbڪ=Jj1,1*,4$sLb f` |ŗ d s0 0caV!\$p C VqNdi{q R7x@rܑnj|\ F( ZfH蘴~hWbk"2)Xac:N~8DN)-f[rS?15K0ku A_jb 6`mLULCH;\R76P9T6:$ GLh׃Ve("_",YĆC~aWOhN@i ^%j~!Eb-*ܥyw)pKkX1F,gLg PB+Hf wgχqBP&R/vF6m~X<vJ.xPS :dP\Չ[DAc'čq)NjeRk=JV)rDtݲL1-|W4o8x\XAzuq"gəc(imRW0hCd=ʡ|d)S;nibvq jHsC,1I 1@Ey耏+U,sP?Eu0Q ֑60R>M`Yď\BĈ3F8K a " ^sAd~! M@A\$kђTыq`+U/0EμW:qF/ f, 1q#%Rsܦf2q/iexu&1{`%3)t3"B=&-Ah6fBNjKL zZp^M)B6/|Nr2`W[tcp2l\ dFC(Eۡv0 :ChY`l~I #uG_[F0ƉFYkwKnaTz(nN˴ 6-<-; I!OA͟8va^odBoß3u֢<'m,CE l_Ceְ n0]Tjfs{SiLܡDkCMm:Kzx0lL#-l-S>BWH"DKҖf}LQ$8XDь4C%xBԂdQ#}j uB3.LcdTF2Qvs^DŽ.-!Vj.,z! bV/`%ᰘyV HBOCbISoHlutN4vsφ1W=h* H L7%Et"$ l0fpݠi@UCk ) 4M< MO"*!FQM wIdm]i悧JTI~$ZmބΧ.x^%K)h*)&x} @&jm&6 &]pD7hpfV\1ՇwF+?_D"PaL**8IȅK4,mo8uRX:) qr m ཪI[X+$bL\[&ȝY 8k) Ǜw@ 2m3Y 2xĉix&P8)-ං-,0 348\鑲h2`@C&h206 !Y`xk RQ E Y yX3u FxO? 񿽨cpcG tHAL5t ƆB[HePVro ,ZPăqA$qPq pE$I:pD;cP;# H./L[چsl[8L,M 1DeƜ&CA"dC DɘĹ;,AlDÕHt &MPC(,pH9>Dj8CD&>#ӳx3?KqaywAC;4qFXE(4 &:aBt;jYӆc҄˾K Za>'cLL҆@ 2 {A[ `H7 @`qqᘭ#iI@MM̭[p. R;Z飒"DN+&Zc:Q:eIνN©&,,մ0Ka$## ,ArZܟ\&LDcZ&D'LCy9-:"̂)`"1k$2 YDKf&,|S~7vr}K ~۷E~;7^(8KqX( CQgR I1&P OP)#δn8dL[P5ȀLф\G,}Z&Yb0Q0~ru+鹼},Ĩ 1̧;Fsl&ڂ& C)lʂ VڂCb*+Hh%=p`:TUQP"ԙ]U3EsXLZSKsSR5t8ڤ5G(Eeؘ;e B]CݵUljj[M-JsԨsV;>5M\2%ʝ1 "Z-fpߒpddɣ9RUB_%Q8 ݎ=h!xE]2A ~Xha _N8ajRƐ0@҆9%&,G#>cHD[-^X'0hSOqmHՄ '~İCmĤH;A S$c)LJ @P^==;Q\I!]˭g 22Ef0]ZQkc"&Xguki-]e<#ek k~–rDhUxJ9_>bVƧ_-VFl 6E Z8QyX98SؓuY3Q3Y\I?Z[ۢ-5\[ tCn "lisN۲$ CZv,B,yI[Ǻs!195xh]uDVgzظ=};C3ďoVT!1}IjݪU!,z-Y4Yqġ,ݴɒ]KlLCOmd)鏿o=TK#Q q̌6E3hͶQpQԪW_Vu#Hqep#\?z$$ŞڶBܫUz?ݼ}W(2qɴ3#_PYA{, SWxb;v782-bPzQ6R N  J-Xr 3bL-)bϤX3.֢'#H.*3Hb,I|$M:$$MJ9epc:KbF_,x"!&i.2eMA!CAQ.9F !e:rztcKd"ҐL4لS9K%WB1HS\"&3UUbeW&u=EYf-"ZlBg]y՗bWIy VbG_DQe۔Z3'H&~5YlweW-puw~ /v\eDf}d,לyG_dA G1~!BM0SL%c`h 5 8Øa,13>bD SL6,Ӝ,$iXlRIyp"{~hˑٖZX|%&De #ыk|2{3dF>kы~09̐^4#IuTI6s$ a J [c#h+qFE!a6b8z{kFN 􂃬PT.4K`1#6)>*$HeaX8wHG6(+8s*lJ!pZ5n#ZFF8cM]]5aYG1,&M8-4BTn) ,3fa&τp&eiqG}{?6qίcP7Usʨ:"geU-QN3 v alb!XڠU> cc.b 6EQB *a-X3"+RRpE+bCq?i~EP(RQPb3*UX'B@"X l)t{ *?ȪVC mQ#b-le$V6 iHBVLb9Pŕ}j}4 %h7 $-Єf9PdUeCdۨmG0wzC&6hw 6)![nsNJ"ی6yx+Làj,.]AKVƤ jщ PC.e+pt1iOah]ԉPDK՚ %iCI`Feّɍ Ս@ؾ~S Aue@}c|e4"oFno,Ba62W6]GcەcY$W6Vե&Num\$,spsQ.mW݆`z*DWy Z|amZm0-&AMo{P}Xkc / ;-H; fb̂ ˬgEPhDi"DC5QqoX$c,”!&SE)#YnjzeqC@zBl"K)7CdnWEh@ )a}c+ђȔ7Y4 0(,팒3,43lțpGl!).>I ihЌMxN@ L$j>H 'e9Q*G j@euuTy.h?\)/Bq!e-}E'\ث7 -`gR S|8O0_/$ς'T2f IHP?^>Є$40e(Cϯ? T9x[Bq-E,C q__`lYq–Dq_}"B;"E,"__6B-BVx:BA\N$a|.]8b(![>8[҅ޭ|؁y~Ζ|:uaHޚ|##e#tթb"P!b-a&(oa!~!Z6p\b&f-5-+ ۅxH(4B/,mȅ=A#Ă)%6f#62_ ߋ$8c1X-k_"1-,7У=c_.h#,1 @!> 2P1lI;`"\|;'#؁,&Hd$A,#Ф8̜*h&cOERfHFX%X`ab-)F%nY⢾ ^.ZbY*g`og ^S3%]e`BG_e\bo^aVf,l%k&fXz\j&cff%~&aeobY."/ uB4C'++Tm.llPf(A(\6.6:'+4'+}µL$T89cMI-I4:6OMʂ`,&MNל2B.tYI'| FC. K'|P PJ$ER?ʂ.11V샔1ģ87 vAC2;=WtV\Y(BEНW$I0O&XPOIF@f3LI)>&eL)G q)uB̙2)V˗f)+)&}i`Zj).ކ#n^(t/£^B$47.njL_-QARCwzghdSy ]|j$?8(A1 1~'L\Lݠ9UMj:DLjHFO\N;=`NNL&u EA;>kRCj+e&\)E@],+c,i҅2W,,&.,.0u.B'+1LB^^#8urjB6~#PNVg,lN#⬰m-Rk&m̩Ҷ+>CF-=P->/?3@4/^L1:1nJö'L'LgrRni'G{0B妈u3Dxk4KsG0N7KO0DZDܮOsܖmE9h3BdC3>2Uȃ R9Lo=]\>o-B? 4XXuf*]+/B#`l/xB꡾#@.rc^^sc1ցvȲHc-,#Lq8 *5jBj5Y-wxzCBJ+\\w'z7ɦc'0~?y~87Ky7cF~08}R8X=\NW88Uhk횳 e-w9(1 Pl=)@4[2L4/3_^æw@Ojl:1 Iw@?~Kmzw6w3sیxy7x9ǎ WT ?#>A垗W:gǠ .rXϦA7x[5^1_{R[)[*rC߲Ij+wvv:G1˺n:@JPzZ3:;ĺV8e!ͽpm?-V/&Yqp Ǽy&0bwB7/4BcnBJz;^q:Srmlg,azgJ/[9#86?<Ohs ;{9_*Ym*W@h[n(|/h]WwDl+.nn'o"W?7t4xaB5t]D[#o3\XaǕv$q#ѣK'i\I'=};y{~33P۴"KS-mz-[v/5{mZkٶun\sֵ{o^{eǐ!>h*P:2X1c\T'OpmY(\sʕ/_KNUW-WlپfCImÉk N|cr˕\hp$ABخ—ߺ0;K#N1ޭ<= :СHC:+*Š@ <c"˯|% -T 0C4㐵Ns0E5l萷fCeum6^r8,R9n'ʊ(8cJ(+<0i=82rb>#?gpyDM$0+sA/B =Dp0*EFjGyTJ_VXYOZT#F }:6_:4U[}UUVU-V[_F98#Lr`lRN*JhD<]iL2 !ͻ 6 8?}yOxs?M0PEݗ~.F7 CWD`Dt4ň# NQNjiԱFgu `=9c܈ً6Yg-ze,OnJkSv۞3ʩ%:8itu{,y>z@뮽.k1 M^𔕈=8F;5TK-S[Jojx:0okR9zey ('l&s}ho馚zڇFOuW0߰}1W#xUvWS Êo`:`Oփ\GHVrɃMk_kF%`fhBV:er\D|&]IvJ;nA90@+TBFhaO9ETH8BՄ5TP5m"ի_/}+{'h#KWE`dhCHT hhA*S_ƒ  %\ e$zmp[Uͨy7a7WBAi+_,)CY,ˊRҟyFS-dh79H#G>ˏZ)4YMa%S)Uy)r`BS56wmMJsc'"ni8P\mM5>]gPC8 C/z! f鞫 #"&-Rz9Q]j4&fQRw+zXE42:n/Ἥ_NmqgӠ C#z,]iGCBUPTnئ;mIҬy3)PY j~z8DukP\ V,aao%.{+u`x|]H]=mb/nE9f$WȐ~^c+PP<7)Py"a ^

jOP> TD CpA*sQ.x z%  RRbV|VGRKd#Ur"%Q1WO 5q kO[=Ĉ kk䰰l\ [.abͰmqt 2-ڰ6D.E7Ddo2@+2Fp!phÑ$Kȱs/M&ksxn'fdoAB 2 )"-b>1>PZRd!KxrR(rkfV@z*t2xmV[08bS6 ~S 390"@EQ @ rm%N:D'Eq&C`%zRT)a ^ K͢LS?eK?I@o,@@u(V|01jCB!RDB412d"&XC%C8sh8S48jT9e@FiSF t2h"HLhD2BdS l%!!S1LYJRLLM/$X}D$֩حm.s@14JC(I <GOg34,OBQ*]DO"8UVU$A&زr~S5fT6eB-GkbR2H%Uȅ^[Dt Ak0 tU0H,(YYD5Y'fh1rҜҌ.bJ*A&H+p opR1f2Bc]a Qյ$^S*uR8/CL@̣^ej%jb?/V}%T: Sdã|2=tKe" 4dfsvAւserBsfltC`8E!2ai242b) i!GxdR>#g BV]uh8a&nlZ`;=S7ζn9ժ.,%moLkdub(}W+=#.N*XGwh,/~E~uVChRz*b(ZOepKS' @J˾vHkX 7 U`9֨zi2tJA6=@_6$ ^b4#,59zsHOE'|aX`pAV A+XaeX@z^\Y @" V7.8?-D499j:9q.:a!`LϢXj>BZ3WMzK-X6-*@ 3N8;@ q-|b:XZ9BqӪB szږ5cb C.*m҆ըvD\:XAopNզ9*@w6e6Szᨗr;nk;pSơd4 ZcEI&Z]Dae:e`+F`MZ6`Bg,]K͢[ӔbX7ذiEzX91ñ:`DBc-[9U,恼Bkl:y),;,,\6E\7F FŃ.{02;g5"4x]Wy)I44y:() Ƶ{aE(h[m;8`97;H@ |%qu^ݸ'q ;\A$S0B[2ec{l:8 $HzĀƁ)8z6@+! XzI}7AARؓ>«` ;y,3_6-; 56/,^Vÿߑa,˂?9f"ƿ\ 0c0a?sHHŋX џ0@?H.(`h PD=ǐ!>`tzU%WQ;=JMPOuSF Kx&2uGчR.Y 5#CUK ysKIF53W)DuAJ=rģKMr)d]ML駄i(Vʨ'aOQjw( 8qS:szSRu WI@l+f7kn 5! ˳̾y7]0mvf[|4iD.y6vܱm,h|ݛ/qG̿B-ɀ0( "I?\aAE$0}/mRJ.fN5p %>΂TІ~F447G;D"(ŏ}Yvf4 maC P+u)E y7sH#ʈp؉QYTyY_к,N& ixIfXCloju#;` t#@ twJzw$G Ѿ dS^ t }${O0BBaG?ks *QJ%?'#laܥIL R0Z~)ĠK&h",^iXz0N.uK^{2%7& sYC 9CT-hD}QyZb&GheU`S -U'N  .P*jQТG t ]g"2z#m8·j܂1ll0g8:il]xAtƁTzEuƨ3wkTɅ\Ȱ1 D$5Z q+)<C`{ɖaIh ND؀ؠ6V+jb` @Ndh  , J:\`1^I=P: 38vMiCΖg(^Z$[~?e{*_QwSlh:K=vWs6WPA(svm[\fb*#Q!U8=Pu )[)u(-͡Dv,g7Bik7-6B x1B@k쀃bCT3uUl gC0 -Ądu Wz&P5B&#d#U]H;tXbpGLC~Nc#X@;A;|(~m":#.XdWA(m ~P(d$8pfb'Q+Ȍ y*]apgC : (:? ZKC @`isb@k؎D86>vS=}B0ts;:)a7~c(VQgI7) 8O7dPNG[UuW:ڠ Ynj/5Ә/嚬)0 R Q 9i% p ɜ)p Il㰐BS,v` f a & n6۰ EA 5 y96 6Mhe6KELZ7Uʰŗ,Z'+|Tg;"+ǫʭl+(sqs[[ 9 QsuYQ8ȏȴ;-80;/͗,~cͪ9\ 2;|{7Cz8y6gQC=$(dAg6ZhKO@*|~bK+<]y P#K,;10`k 3M5C8U(S{[zCOԽ˽|*ZDV\\Y %L\8p)5\À؝rDmٻu*S S9=OZɈ+WnR-_]Mm=0eьDMҳ-$j=,^­WqLt#(PtfQ\\^Իn&N-ީx~Z$,0n]]6նK`9~͌cz-#s-lx姎O5|UыtgvÃ8~CE&%ټNS$zQx|Ȟ9Z,^p;9^+LlZ޲Śbl)RoPrMUy܊_>4<-Ի\(.>V ^E)|+,W .`N?^q P(n@.V-/P7?S/u?_00 U"bxnM򂦳qY V` Y0Y{ * .sE9挩fj@k_EvN(XMF47-G`|@ ,W<'ih_7P [/a0[giisMo>G 3 0递Is}~ @`ju2v,"Pt/$ n>>r3УFMN: B > $5]ĘQF/~7LT-Y0yN'ѥطg \P'VƍhKy9(|H3@'vb)Yz8tћ738zF]9 ,e4V[o=aAK X"TU26=R2ģB$E??CZj֭][lڵmƝ[n޽}ǐ!>`R Pwjn9rW_Uw~EHx'B EH$#]K80DJɤ&ةx!Zਤ\r \:t1 8б*$|kfpbKiQ.q 2 Z %[zyвMc)R1 Y: E%`Ӏ3O=O?O3`yW=DePD;Y4ŅH}$*uJ[()TOF>f/#mhO.`jNYb2`LR(XH"Ld80B(٪E"qqeA2P=&Yk$Չz }g? أ0VT,x$v?dMUGYd1 :P$Ԝ: PwgA;.!~.:scz;hTR*J"\5֋fV\1bNRgabɧtOn/Ġ%\8n. Zʥ+uf郵LimV>%fL'NEuH8#n9cnM8IMoТ:)#w78 ,Bݖ11Zh87($ a;@*{c\.<x#`=yZIj0k|ώwc'SpLA'D&DbQds uH#4$&)5P" tng$%P"7 v4Y$EP OTeH$!{8ӱ*~;0`DIY:En@QT+cڊ@d&E03y+i <<(ol^-`EhB> {R _D/it.GyJ %J5' i$%5T WY[,qe 2[sSAӗ&fFFxbe-T&)TM*iNN]:0#>%F}XHQ`';ymiiȳ-ʄd ^0X4^@a ld%;Y4ˡG p;G=ґ/!E&^ TӂH̴p^S٤-6TuGEGR1 NBd-i%iW.hлqLJo}@kQ^׵ᬖ9P1Ld0ˮ}D%=l`6􏋂#yAJ~$%FmCFIVooӢ º :FR/R- wry9,b[Z}eT9n79qgvlbvEzuchĔB]3p'C55(=hBCDsDFt"=R0ac@Pթ)]YmDƹ:Iiܠuz -K ,E!4ZZ+YG-a2L}27r $ޒf{0o$G0A0# \oϭ"@ cY.t>p=54jˡvpAM;Ixu ]ӕt+Q +3rQLnK2ǽ ;jdUBkL@! QaK4 ?F/ֵޔ(M~=w i,͒G' c;`ִ ?/ >w]6 F#JTR/RKIʑ*<%xF!90 ˴4Me%8XKYEP IQqRЍ&#L=QƺDL}һrkt@>Zv_.$C`Pd`N7ϐ{.ڣ,uvGop3i w.U<W`98W8мlЩ{ aA];0PdX}y2f'Hs !ЃgʥQ9' Ճ 89}p! ZXꛧr;8[І'hP0ÁJ3IKG[C5G $D::4DANC;Ks!2n[`A .>jȅC8M:kȅ\8^ѽĮ8P̅exRdqR [ !vbe bx|l(3 AF;/=fԃ؆fh1kd0=8CK0 oG-FOøMsgH1WiZ Wɭ*},EHRlI|. ȂYVtE Ca>Wl|"|obFdLFe/h 0l$ɒ;mF”,“+$#9q9<aHǧTpT3 qȇlȩ:\F|(dBH 0QF˺44Cɖ-˽$I0ʜ\4-|Ԉ|$|ǥ1ҽHʮJ"̈́Az=lʰ:$KH|h/lx*Z͑ͅn$p5r9ĎQvTGKxy|LB ʬCDE,DͯDWtHlŊMHۄKQMģDC6P8lCtNàCMCMCCT*LzTʂ4}*OLϓ@Ot̫lKヹQƼ? Ÿ K{ΊNKˎ| =o'%9 YPPQJE%Qt}HBb|cQI:@PIc;?]@ Tƃ-LS8C!\l< eWR\\ڈETVkX0XCN4=uFGZ\NkeaXCaVf.Wd$nݳԇc(]pb+-.fb]RU?`\fR:W]`ǭG?CZHff/VfH6еd]LY݁!_NgqVSQߐybY0eTNY--nyW>AUTAU<#3cxS0g<…<;hSXhڅ(uXjC^jO^Ea捭df f!K!皦"eQaԄ/Ȃ΂EipgG؂<ȃ-by;bOzvYjg(fZXXeCcR`\jfccFfhkj=U@7뾆>p8k]vPjk^0dFߑv[@Q!_iжiݙe!%a[ =Hd$hBgK؞,?퉍x S}d ƞ 0F5~41365n44Dy`Ec` kk"{Qrի>Z>%cUE^\C)>CN%0Ws8l~Kxs_FsI\p7󫝇gwhkUp9ms79 W~ߣby< o rϺPy#'h&yϯ }(r `aM}aH8}}\ftldSL57Z_w(!O+5.ƔuoyBIGGπ|Җw,PԘx8+ 0y|*ثZ3%`k2VpÈ),Aq(1XeZ"9's&͚6o,sϞ> ʳ37F M:A^ԩ2*I麢Gwml+okՍcͫ7_j\.bpc@ z.cޤY Ҧ<`LϿ֮_[5ڮEw!ۯ]fk+bl6%vl5>۷{̘\?ot ]Sb|>٧?5g\E7A>D\Da,QHLX INфD҅Xˉ&csa-.x@N;hN?",| UX]*\1>,XeiJ!ZhZHoN X@ T SC$VKk:YsG'dBI'|ң6#NOOPgwU909ȏQNauCaA҉7Ĵ)7x +oQY: 6:Ԣ\'q|1x+"/P7Wj>t%u.w:Dca egH%ґji6Е"{%,yr?t<:\`N_A04Ocls=Ho|m׮.F+tLÏߺx蓬l- ᙇ2-f.Yjځڦ0HH3Y|[_ 7CUYRgLȿ*]o3LR.}JpU*b6IK\GMԞT+SRԳH-UZ@QKȺ%FyC9E1bLMn_EB B@SmfGfaVɘ֎hm6']u16v?[d FM΁3Y2W6l6`gAtA<9 ]~BkؚD1ȭo6E `sM()\a誂0bAN~|7'c*cBV#:K:!.ŐQdz{I:d(s*4b"@4SJ~s[cO/ @m0C?r@]\7#:2b`N _п#InF SOOF^N#jziϵ^X$&p~-t%"6֐ Z :1s8x #툣&m8b\҉4{pT:FJV:(xϛևi,±C)uQ#e1Xʃ#=d,4/^3p# S6yh~wQFfk`!_h]ҧ-7sza~U hGG K MMR]Q8ЂtAM޴Ye^tPI `OD^Oꭃ20  ői^CjZ^Aqŀ>|_4Fj0Ft8_4%jklAa?H,@dh?yk!>!%$]qvddD$V%ns bo_1#:k@ FIDxDCbEEAExIAYIl!@pIHL FɃ PXt`^`;t rI4PQA6ɷCIQ 5a&=C90Q\8Ði[9X[\҅^? @a`1ܨ$#\äQAb'V7}\GN}s1">kd6K%,_ wr qLOGKNFlLbibkd9Ȇ|Pp P4 Ïp46(:| nc BIN$:ţ M`9D^% xјa,&NauJ?\a FDcq-V%HzIec4;ІYmlk pn(?F+beP 'QJdpmx@d6}gxk8k@&~uuFv*gW=3ȌӘ$ " M LlW՘#d^e f 8 kvLkDt4(fڟV.vd9gŝdk;\&)!Cl|GjqZ pG^b=jkDiJ?P*,@WYz[%ˆ@vZ [\rJ%^򥊺k9CQ1IɓMЁY(^kx7씓 4$Bօrɻu2IĶKI>4.oo8OtkjM4NLtO4LOwtɼN5 kܴO735CaT6 aЀx3AnBs~0Ws>5 Z߳^_>ރ5ԃ?KCu^^F{1\Hi4_i4p ub+b3vc;cC6cH0kHkDB f0X[zpYWcZ6uk <[H]C6Dnuo==5mfvH_bIGvs;sCwtKtWc.R.DYG)nH cw'.G9I7~9j?=M\/\ӵ?q^;J>C`uox^vpc7A8sSSx[Swb뵆es+ [BĶk X]e{늫k9cM|v}*k7&kC4^; = x5p/8\`B`Cr6[cyk9( >hsӈMۚxπ(|h. Dˆv 5۷8f~ [9ł3??xSaksz;%xж8%+8]+Kn8J77 ~0qf5\yElŀ3p5냤GKks{CDx➻Hyzwz?n{KcȞ6>Y)! 7{CQ{O9IcyS|wd痸#prvfOaohsh|i{i7P_n:zRv8&F'>uWzn$wK;C?A-EeeT8 X=3hW59˸ػ8gc+[v`J=ô"Tz!B3}G}+/SGXUoiPIMWocw5H}X3ڷ|<7<߶W#c} H2~g }ŝ||gh{k07?66嚾\8ܯ>s?Ἐ~^=?0C~`~c.Z2:2@<1jL! ɑ81fԸq]G@dI'Qَey В&t0PgO?I:%MTiSOyjӛ1ITwo_}I[lٲЦU[O[o7n]Bb\s1|ЀQիM>]VXNFP끴6ֻ[z7`:R[bOUfמK9"&"GТF5iUQgt|J2@B+2 (A.p 4$hO 4L-N͔ &L9y |mG&wtc"1ŏO(KBn/,; "v&O=kM⋳(zΤȌ)!*4c@@#-1,pB)P3K)Beb Ӻ1!&>-T m4bMVS{-lm6Ym9ߔk-!zE9?Lv#J2gQ /O;Eʋ7>9qZ6[A )%k-mA!LS1ô|%BJb"Tp MU`L!^g(8X%Wv8@ 66衆V^!v|9Yigh&i@ 8i:Ӄzʓ%c(zۦq#9}\`)sjWZ맸ZxItllv̯&[`^{7|7 bG]qJ?)'GpueP8ZnI^̠<$pbB%h'8tx8!A:񴎠c"*i}4b%!DЄ,5zX%mكv, Vp^ܱ Qbp EaG$c;( qu"H9юu"KUɺ| 2> e٬6M'?=.H*6%(f } @8`@g$:j\0$@Q<`SYbnHG> H d6Цh@ tDXӁdpShJ? D>d :ynʄZ1 r6E] @d'`2XV .EĚl*B `FQzR r.җ#gR]5ŨJ`䴨YVreVꊌt)v<|Ig3P [mw 2.L.tL00 PPc|(T RV,B%Le+.==~<CfLH}@ګMOj}QEU6WOJ &{v bb‚? *^WnrE+W*fu@@ r'tȓ) Q-1%DfEGa߮2myux]OKsJhAt@GssS[͍}0Z[ < ` IQ,DhqjmsN`.xPVSc1xZԄ*l|AH8Lt<\[1~w ӣ\Q&tG -p6 )Scz.8m}և)vd#!GE/H'{lHX'PBZʡRK˶%ʡt"d#x"BkGxEE#Oޣ.͠jk 충\@(+ԮM(a"`B ̪̂4m*Œ -ԡ< `/*nRLaSb Z.M0&0좛 )/{oo. N&Ԛ.>&qmgzm/\,4g5Dg#rCRq&.,nW|XXx<qJbP;ց@h&΄%Ȅ@&.4p8L2R ]щ' <sb(TvEhO:b RB!PFn*e /P&- ;%xh 0 mCh.^ $_.mP/^Z@$_"*"$c .҄Tv.zgrR)װ.,q%'1&.2`j)A1DXs`:&dvldzl|ld,ȈLX!q1$ܲQi],pi$lסߖ*#1l~B]" 1?`cQl`N* W.k:-(g$ھ<<.)U'v`CAt ss0q[1w'1@:I2O)6<*#)!͂M Q&h6Y6I tJf7ws0ư7R;k` tLŴ& C.<}LǴJ:4M$;.T*  "T KHzi)f$xACBC5 *T:dv,鄢 3 Q2ׁ_#zQ[F+Δ4[Jv)zt]!DH+H5b]̊P ,7' rns/ᕝr4[n B^UzGQ ܰ g+zr6 PBZ8!wr+a@/IOOJOh\ڏ9$;:bB` jyW{B$bL4d@uzKzB];Cw@"mzT$:  . -:`/A ^-[y./`&.)#k02≧ Gꢽ;O ;jr۬Ceb"M#.a*'4 D@.C2Fē3C5:=z\6@cJ#SShW:A$d!hd˿Ml'ir >=)}*;DA! @LAtз^ ] HCSa3M5=E7=Г9'.a"u'R9)h{NCܵ٢o^o}3CE{$[SS+%fyy4g@W3ٵ7ʽ7iOǙd%hB"Nkj|V(Au˵~g["L0~ͯ=;2 Ej]%>C r^\/hu~C$sVv)^om^Ti})TS%nf0a,6a@dp''ք[d=Ҟ'VB)ɽS~ ﭔ?_1zQsSטs /@@3U!E&P(U?A_VWʿp: H`Az*\G\9}1`-3jܸ^=}AGI xB|\G K8sϟ@ JѣH*]g?>իXjzT >hxVgf_=#l-cgEvmwL-ٹo+apu9ǐ#K,αCh={=bDtGQF װ_˚-K׳] i7(KR>ؕK6Ać0eL ćwr]Ͼj``4o+X/U\eUZfYf9XˀJa .&! Mfd=v8piZ3֘&8Ʀ#&Vm}ܐ GQ'ݒLnR=ٝ?e%\v`))kc1k3jiqY&Y5)(8b(d],ڌɨΈ㧠m(,8H"[2䬴eII7V$4kȾW&V` mCnG5j Lꎯx*-&d 0jptf'=F1Gg{ Ƽr_{Ua(e`|`[2cꚈ1tݩIFL;@==I)ϭ50W^imhj+(hʁiU ~|-t2Y  ^.HΑ9$,3B 4Ѡ)=֯R Ia͑Ygpv?c[n~ )܆3'yV`lS3Z=n:(ټV)7#氉lĴ*?]RKփz]ZpWqH%w 76!oxq)^y Ը zm"p|+_4*P"shMGe!Nv"?b񗑏kSxH6:PDǂ6"CK,626 opzEvAg4cug'.ƅ=u*b$2 BVGq?Z$Hԕ?&'̤&5E,v]4م/gq^ge1l9g) Z!H% 9ڈ48R\Gѐ,$($Dd#ǿYK &IrcY[ e+p2cEM+B $ׇނrNDud L^%/ KL1sm) ~)MRZپDU縆H d$55Y|sb_B™C0簶 X;'9 {F-T5JT(E1j2x\jjb1z&J7|!#ZZ9?2)j4?_$-k".Q j>2H8vd$[N$+")Mua@"p?>ڢ% %(2(Eb@P6f,CNmZBi {Ejnʹ6b#H6+uq`G?&1)J}qׅ\C | &Vu6qu\MmIYh[v(Pm7ܓb(ʸmk P$loK[05^@/c /p{ !( )[vʕmt%bJU91 ]~O`&49m*Yp-RE6m^;Nn &-6X$L"V&lp1E m@PdKO" J:pb gj'0Abs-E{;Ȕ}- Ȁ+\H(Pex/W ༈{eD? @06|cđgjP|^2܇\!-_mjInY9|Bo`iCAꟜz%ΰ"c_%׸M?[,C'N֍sl-PMq3FR " }iju=4G73'{7]sbI,Gˈ!f4GX:C! N5: ab t8nݞ0k97fl:pMŶMLn_@LoDz\{# v=*j:l/M۬͑Z7"iUh>ڤ=vh5>!F' 惙$ >BҮg'& iUy+yBa7Vkbz:GW;tFslPb7lmbmkm`'^DunX / BVp ЄsvQg` İ qZ(p PXR AweN#HCd# r(R!m { bTp[_y@B( &vyGqS8E7SHa[FiZ(G70z s x[AqDzp(GA?1B6Ev-2-dN=I2T6e)S23#eT[HV`# ev4fn hC gpX}p iG e5|np g 6` XVyPTWVwE4Xpp uh}gu 1I E$PUi4'[DO0cw[s[>j"WpD dEz-v[D% &c  %?'vGrዣy@ѕ7cI@qWxYx[`YIkvcx2Iw[ E_A2\t.Nc-C] ›U!!wAU `爎'4PGP#@nqnecn |spv)  hn# g V0$0&yf(`p@ X(`y``t1p21w@a8Qxf;* 6c&jZC!r[y=A cHkvcy1G,`- (jyEkILH蘷ZGa5V 8y9J X6^!&tTæJI'mmJ7'~~)>Cey0Ecw^fU* ː吣pv ``E5) qyheEБk%$Aٹ w Yh&i0v`@ ) @R u(5v0Gj@Э@ ЮI6bE(=r]a:=x8D1:MeJUQ>FڥZZsGp1g*BleΒtOYlr Oɳ d@P*>J7)B*|q^>}ISk`V0E0gÀw P}gXAjw @P t XVV@j gʀ`qWIq3њkYe%%rOc$d1뱮UMj8  /V4ڤjr\yX=ZKcEZGfbr#Fk$ֱ(fCZۼ?!Y\J>A9]2x@,h2t dKJ؍mB`@:SW$Өegi#0كH4 }Lj5ópp T԰=Lp젪{[} #A9!yxWZV@סVy:%5GxVi`KYS - X8|PsaK-ke@y%@ nG |kx,ȌM:r s\\y|xz >aWcۯ/Q?PpO* 1gX Ҵhg̸i0 hU҈W%G%b Ik]Gkz7[ LS .y K!kb]s 'ko9j8I;ג1v]-ȃE{; ׷{O'TAAN 0kpPٓ4·٢EQZ I>)9wD)!۽o*|"}~:g m%yY ^^~ [\j@4a-06Q +lM i HMBg] 0>U:[޾ȟ?Qs&@郮?<&6u&jE$EL;nl)x0Nll2,`]fbyL|t Đw <0 64B^F^VHުN#MP z+­g<8 ǍP 'ph9A~˳|#c?=0az~C<=6Jq@_b+&HeM-<ϼ( +?A86 4KQ&(d`ӎPN`׎<U#p85n{x0  E~=Kfw7?q П%=gQ2Mڷ  08w_@{s2mȴ=@њ~kZgD.mb-XmFA װG?Nw1Vh ;N!ejŪUp`-c ,B K8 ň1C#5d#)UdK1m LntYd,Ac)b:㈢E+AiSI\v:n;֭2qCܵbSڮ=gP'-.c_[_7q)gONI!M8SI=@PǖeJ)Zb 6"E"*[#"h(2X5B mtEUqUzRLNKE<.e](,pNEu^)q#KVV_"]>Ikw70+yeVɤ̍gyoy,_tR;q^u7 qc_G)o}fB. xCeBg6O6kW? pF[Nok_&%kAr>h6mSzA"WPms* 8$R,r9$!B "x%#!)H'qK9*Rr>D8׹ :+xHbJB+b2G8v pn#:Ę2@V!yU pC5ɠ[` ;\ CD|{2kTЙ^ֈp hG!kHm눟䧑D$Y(&(&4)0Q-nAf4V%2fэF;Qq|CeֶsF9AcuK5ȼV$^ {Xu8=Ԥ8eea-l e-my8F j4#Ljl#mmmZSNjOKyvGVs֎gIq+ӥmFS8\QdɤƈKMaӖ zbAAq3j䬘T6u7!#U;F7]JŚ:ՁcH8zVUn)_ ȶ%qM|bzHV1,sBByMg{cX;晉\YCfu$v+ldw{^TS.!}\ Q60U2(`F,VqtplX0E\X'Z$b^bH)3~|iLgz@v5iPW7J,(Izd:DN $Ú6aoڐ,O>O$(@f/S˱9S,hk - m9aVw;yts/ZLdHǻƝ5N}{{?w n-hhpVtZ$:<)ţX'Nr*Y ̹9(HU2\mje,+m ;@[x$}耷!Mo[S."g][ V 6e&vj7#1{*ÁDqrHDB-)6JFn>yS&FPRߜѠUrm?xh#]&V:ӝoG]g= $^7#]ӗ*OӝTIkГ5fC]Q&|K?^*Y5-)~|_K;wD}-o4M=e ?.S)kkb@;;;&J<8>F[ HI6>Q(009{YpK躊:CSw{p{+;BEԫ|B(B)y1y!9) ¡!$!BCh"듐"1/;[{AAl  ?? w}< 00${FBƎHȻFl,+Hw@qij :əl4$IxGK ̋2ULE%3WsE)9YL\t0|E]_?*Ƽ3FLF>HgA,MC˴TwxKqdI[97;G$Kd,1KrK N 1E!BS &TNb!ġ包OJ7Eʨ((Ə5 \4>LOĦhlHmxtj7q|Mq˶RHu <4L[4LҚ͙I3RR-U)lиyPuUTE):` /bάS<:"$K=Ɨ3IAQp D]Tq9OTlKTCcL P EPSMWu:㌡,L8TBNʮC/ )V#Wx'Ne(O2A`aMҌFZ tQTR0j؆w TC=?EuH ɸDKoEIqUM \WdXZp$LTǏ!*JTt*VlLXTs ,:M(q8,VMfLk̪d l3E,+]T}= =YT#5,v0XRj4#sձLa) ! E(G~lEG$[]. NHJ_/JH*껄 xQӨS9QšZ7nZɤcS_ <̫Q`3ؖcDƄ91X=(.X>iFn9m;{Wգ*b!b3b0>pp](vEN-BzZ2}LE\9@.*Aᘹ ńf.lBlюƈTLF}l,n0QRM[eM$ҙLpIvC<ie`~x2yR=fp{2'jNP }U[5*VmX;ڡe"5_*(^g@yfgbxю\aeS*/ÁmˌSdfjB'#j\,Ȃ-؃\8/Pn~9M8;Zdo# 7vFe}V(CGG8CXK]:t^8Y0 Vګ>Ik[pT%%Z= p=CȂ!Ȃ=b` ZG@(w%q.qg1rHn|&iq-n)d qITmrjVi %;;__Sj^%3HB0-7'+p8]AI[(.8kGPu f  t1 n#>g?TzlPY@y80 t(MkHEGȞfP : FFN_& 1`i*- 'vGxvQw)gP_vB(#wrGs'}_'lڅnn*txf㜡}uW}\qL>!/tՕPHF (z7")/,Hc?v8Ss9K!1DE p9tP.RPj !p( `u~9uy0RwQff 4.+H j{ N/<'! B/2Yo 9x谦 !( c{~| @f'wCOВGecU4Z(㤕cǬZ=,cNs557,o sSzIAo*r5+0Bh:`BѢP ()Bh"ptҍdHA x9B# RBS67<Ps yXRhGpg] $HT#;`#`2jƂ( B@aDA,T-ɨcLɈ $P#@tA 8hFE>fOsi΂: `JyL4-T:è R=C=!v]H%O |CPYA<}ЁW!EAp՚BGV2#IY|- @a\zb@~[偫P{Sr53eOlK^شiL 1cԆ)4/$eWRqI-?&[iťCise'?~x-{ `~7s1hʅNNk`@YH#@㉋#$)$ 90)A3 t,`bPPQ9 7 f}L3Psf1,8,Gcԉ>P>샎7/ J))S)}h]$:d_QI7S*RVA A_=5 0old`ůjD@0]cټKЊf_%r{s=WmQP\IYb^'fy^X,xE @2Ѐ i` Ȑ %BEs"fEa ^Z^T#6xā EB@1 28R G9ڄn FdbNP81n {\09x>:TLdrWuf$Qqx d)Mn)7]Q"z(咧\uA@hdT^J5^1|&||A:z[߽ŀ|gf\ t&hd:PIBs/ *}y\b~jW`( (-Ѓ&D2Hi-=2t!nHB#4Ga&=Q `H`oAGuIQ  nq\َ}Dr \;.I: TeR% ׭R+aY*ꇪ ujQ*-{ ࢐"ϔ5G1 6OE 8a?Ɲ ;m;f.H[X|6T}uj-OBLj4&(vF/QG@5oy9:^wEDDʗi,U{:QjQZNYh?jƣl)4n M5VZH`ѠW?aP6q|v=2Oij`e*<cQ`Ae $sK0znxڽLPQ8,8s,iVg_Zݢ@{}_qifȭˡO2&"DYCirx>O# 2F~T`<զPu'NL tQBTuD?CS0SF ?R<$U '!,dF'i8ɈUGAx>WݱbF))'LJLɸu9"d"+OMf9 D2W|I[V )`J^a $43sLӿr>/@'ɗ4BU*mh?\7?>,|Vww+JwEzvEb}%tDM/ەX"]MvߚF_ER#a]ȶ.t{0"&pPnAڔunGnLR}?OALwŔ: lβs&m#BzOstZ4q_s4xV:5-`D QE}|~p BIFTY UKTKT(ц(Q ̈́QݽBX|AhY`B&`I3%CAس 5]a;Y?+[& ҍ)@2`^у,:C>W`@Wd $TZw m0EMA^#aƹ@'BCy\ l=-׵\>5ѭMs5PP8=B|#8<8l5Rc30 6ŒNT~X)P uP+,%QҨLAQy( c . ߐ8`QO$CTA^2D NN J2q!LC2@/ܢ< !^ýa)= 1,!WLxEZM1"j BDA D"_4@8MCYBh[&6QDd՝OUt?I@B\ᖱĆxShI0QBiq/si,\l? UE1fu 5&LM4'PHdtHTL> ?&l ɍƠ&0E`B>At F8lXVQ$IN;;I;4~$UIJ[ONP!ak <^ xŔb19?Z@U(J-"Fq0lOVaVE&"^[Q _Z lK#(GWcf\ 3kFb*1~DPdLeMpfӄځ&Zifjz h`|>8@8Bt(YQn~[BoF.@U`IPCCR1tK4'C;sv/EDA[|@J.`M,ЂbUTi4$& -<.l;;C4lᚮCB.tÎ-΂*C!*S@ F0j}lXVę$Y_lC0 <$5fW5 >$68l5mfP'~- kג--˨ސn GUFPGu`EKJ% LMeRA>",2,薑P,AnR8<5ƒ֖\.?:fϾN-&ohN!ClNVo"^nofL&*h}~oց+Wv{/GA.KA|~WjّA#~i肮(8I>z[C.R5R#5rnl / 0=0CwΡ6N/WTbonp/qnc|ڂ<ìC ͫ1(3l>I38Cp=SC5^?xir>p]l /H '24,ݢ аٰep1&h(2>dg2)CTzz/Wh}o/ܝ]1/Hs[Aq.qt0CL -!"+2xY%K%_r)mS~s3N6,;lC4(';$o!4ǘU5cJg6o2 hP8s1,BݔuN4EW)snaƙB,4gn06J4 C%VHr tN4QGz(LS&@ac@'K6hK@feQ@T_aBW5X?tY5^lQd1.ȆȖ,_`NH]73^ JXs v7b/6c;"@vhy7zhiiwjm6m϶l}mSn+W6uospZnq&F/sKʺHs^_w!v8,w8;c7Q_`R+So>u:JYuxUg5.g5F4'8^1;*750BRHXu4,ߎϡ8c76N7y8ozf9>>w@ 4@#JVw/Ol7O^4,8uFG9w?X탦k8fRZ7C/h& w9;g:Oשr!noyկ|-3;9:?nY+ 3pᰮqƶ1r_?{3JHƟn8_ywB&>̡6:|B -aCV?ߴ;,Hqsҋ4^4˄$uv~ wzx:< ] @?ڛqT+ÃĿ+W=H2.7w]4XN<<&;{I= лR#n*=Y1??ܱ?p]=V7Spz}dC+ؓ" "7gýG7c+}f=7{ԝcG zAJ٣{M|/q%o_> ̍BD4{?| 5l`}k:t@:5^J+YJ-i\gN;uO=C]ѣE 6uVzkVLIlӨSÖ*cL<"069 ֵ{74hx̔W^X0aÈ_+lƏ2d̙ 0EMў7 &ٳֶ}{Snݻuxic5[,˙7_N,YdR ֬N @w9C%^$Ţ/AdM+?yd቏?Z 0,A=zđ":4ܰ 7 ï*?-Բ<ĭpJ.ҋ/ Sj D0#LR^̏X2̢\JԨШ 60 7܄4LJLN䜛9.;;qgwbj9(=~GhH>4&A<ӖGy@ A> rP1Fu-Cw} W`AuW,ѫ_DŦj$VhⱯlI ]Ll;20(lK+Oˌ+230c52׷"-3;N69g9$/;iN=3Ydj.afEC'Jt=~rRn^䛍lkXeBOqNIE4!Re>Rv)!}u-b=Z 8֬:V+plJBJ&NZ"F!b0q+|d)l>*w$a,ux)E|`^H50?R?Le% LsdЏ';\x`J4t!}' Q)hLg@T8(UBq*_C,mIHyy/LBhls S49w<#xőJP@iU,=C6% T6g I]I2j0Z`rUjU*X`] )Vi;rƬemMfZsvixţ,Ǵ&[ޒyw < #&/_@b@" e&|Al8$S/ %8`~m#00 Bl8 tc@ОAJEjz!p@X5t`(u 4) hgzq d h>JO,("`6bDcHL,C)(PWz=@u^nC愝 7aһOcR@8*\,n:TZ*FQ =$(VTI FP"]沗,0oycEDqIC:k T\z/y|mdÊI Ni  DLtaHf ь0ѠƧct@B1mc!uI5D# I!#?L.쇝6ɖtI/0Y! `ЖmĥJOS R(DD2g\p#:G':x}D@62ǮB[IIOD)gJRù28N 5^Fr{wNrMwp4!'U:oK29Z,֏^BZ# .)J>c$3:wAiR:=<2(%&,L<))sNT=۔ x*s=<""4Rt$4%8nZDa6% qbxS@()R1P&o*7Gb3[)8B.UQ3P]$VR=T)ZS5%T'T'8GVCK@4X2HIgUDWsM@4DW`6m6NpqcӖUl` #Y9 Z@Zct  H`!^Wz AP540%4j@%aP?tTc#  9] X`6@ @`(,!zDrM'6NC3hee( &8T*h<)fN{qx}xā:V .fr, !! aCW~?.h*0N(تjHNjUExxhyeok$lk> l0`^FF7-o+pQ="b"\9nl\@\%O GsksAXIA>H{4 @ |Uz0@2 { "'4!Oz .+bW#4`4QT{CrMqd=XψS+^$w,'9Q#+XEZ^Yg-,Iʹ,ylS̬9,/l笛 K`CπMc몮ZRd"9YnEQң]m7n>I67@#K%B#$yǎM)2~7*y#AVyNV?ges`VbQ:)miMX6Rb듗eL=B"&؂ca$MHj52*.877ZPc̀9 ˇY'|u0bP! Z"=TnE^b Nj4[$>|2v=z`'' x@")xUOW1TZ+#ۈXvAl|pO潹I D;I @AE&;dzj[{;BSSCh##i02Fɔj[S kSIkk6zWdCƗ0A_~\f04 &:ɗO\P ;hQ&bʓa("cafB&Tiaa&~F>W!rU^$A;S'@;U];c`Aj,a4a ,F}<,ҫ%+6}t:)H)},=G_=¥,B|apH&cu>[:HAB-]K]K*WW=ɤ]z4O$;mOzad޻I=~=i>J oa᧦}c6&>^6;A;|!CI[rػEeJZCӅB\Upeoke0kE= FaD陞 >B!"!>˯^c_T~#K\a_^퓂BB|+Ps=TcOYU*ؽV=uk8z'_^<1Dۘ:A~I=Hg?z[W0… .n&vرjm2Ȑ{J%>LIWAvߡCDG^y-'R!J.ufh hJhW V/t+ I-iEV0j馑PWb!vW` ꨡzbQ+وc'jlL$1F[3TieqXrs$>]crx7Q5n1й#w=ҧooH%z`( .؉)Z8afMx^*~1,N|"/h묩H+z#I2,l2̬oϳsEgaZuC+FN.( I;}Ro^ vSaÕXd!\ lb!Xc!ލűezrҸ&{+P";s3P 6;:)TP-:yDUwdyWY\M|5ٯD W_JZ<}Ja( ׋|!{X2X8WiF/ =irM49is;c /cGcHDV:zKMgbe;tiN{WRd?^QVQhR^xP=T'"SIEqLBV512>V8!o7 ֐W#<YIEThl0pg sf r!n! :!`\;v$$J dk K>Y EBC< K$ P$(0\'d . R@T!G\"'6`"2,FeH?F5 ï`ITDkb8"-Crs:P6~x e!& I0F$"Ё$Mӛ>ze =5JR&D)ThT"iA.@'x TX`D!(=A{ "")!.A^6"Ŝ{[j*5iw0@knи]52 ' Bx=Aӟs`@yrҐh H~ub:FQ G5.V#A <%{bMXF@MӜfl X=QBhKrA{xAb7]0z c˗U"yӰlGT[^ J >O}G<<#@NԲlk6QΎ h;>VЃ=)Udlr?Ė/1mÏf?\-XF:@*Ylʣb0(`  yb&_FUofd^/f4R:`̈ok@Ƚr![{Z,a>89o{Ə~ ,f/pDƱ ' p7fZtw{/ծ&akp2(ʍӝ%OP.8v$HSE$,ũښT'*SJV_XW+oh 2zl d h.f8;p@/s@tQ D7] ԲkKjdsJ`q@]|VBnOk *aDW9`p0So^rV/Ƣyce)s]Y1FINs^FoE:,IB9H8q*Ɠ `ybKDKBK8LB  ci9M>exYHey]ח~(<3ob,`Fobp͐ @  H7C (Ɏ(KcX]`rHiLy2@%NTpǐA @09H%pzPH82 "N1E2A6pP8ָ͇W:p)dA_ŕB!R: JP0We:*wTR _!0cPŖ*0zU"hUH(V`E6ӌ *f],v0s7]0፹cxvp3 $U(Y:q8uQqOĀ'Fvy`;Dn:WZh" yI,ryg7vrpv&fx=@@ѩ=1[v*KAQz:nTh@S8?=1Q;a- ȥsg6 ~a7p]ו7U77v 3?3Z/I?>^2v )Iv$@3!@F PƁǂGYzT`o@ 0 tp 7?(ئ4`9$fb7{&Ivv *7[D[S  9)Bs`zV;AiK+A}< Y8\4B }ȿX<,gFw%IN-߯dKN ې I {TEK:;q41FBW)biZ@.٤U'9{Q{z`{0)/44R0/t9H|q`yx#=]JH[ A a 8iw:=O?B4O _Ҥ\S#'F *X0J-]SL5m -^u*X(~rQ_Ri(K;5 2d!f֮_]Whղm % Guś.&}nx&LK0HH &,JYgrly-b#Y3X@r5j۶EAc]n6&`inu1Ny7uZ$8X$3Z5oA'|+GB T fG{DѢE=kb(E\"AQû"vj羌%VPHMBghRb Xbb h!|(!5 Z"X ŎqƛK/v N'\p*)Bi(P⊭> ,AHO]Ă 4RH L1J!U/C2L$L3L6 ΈI&UUKcTQxۆY7\s흚tEGC6}i%9f[OzqOb㣾دrOC-EB #G!nFwb-xpR ac#ZXLJ/8cm'z|ŕK6(}A9[V5f+ffНi F.+SMQlLAլ2>UTY-MdPݚWi&]g_]ZD~!deugo[;k&msYO>sףQaWױ}7Hs9|GG=uշ츧NQ f]3aL\x=\Ux**Rr ^/V馣zөj^kYqFkYžTwe}ڎ u=vnλ>nj:qzq\|7=r^*gم -"Vgf'R Ѕk&S("3LC,13re+D.H:2R$RihD%ڒMD T"\*G $,;3’(x%-,m\GLIF93Mjr };}cԀEAuoyL(eYgsąK<?!E^K6$wM9@(BuEh Ci5әX5 'aU `;xS|A+HGtuDQ LEx{^ד%,cCq3i}V wl G<0o+NgZ6Qj-*Z4g3RT߶$o_quur՝ܳog\䪮utAI^`vy=GFroRёZE/P R%OV /O3zZ)S7حPV+\p9[Uisڠ[x؍XFTNBccr mk2!ۥJuEdSR%d%Tj8X6,&[,Q-kSXK^.J .s) sљnv&U`M3i lkp?"nC[WyȵŴQeEMA0f"[ІFEɈpLxlaW\Rm3Eh/n5W5T7>.T[7`AtuֻXh$y^ GGbWDwLw:lWۮ]O<KW'j = o߲Ak}VókQ^DƠ_m<ndϛ ؾh>X\h3x2@l>4` @ L@|=VKSJ%x3 XYJ;q%\_]ڵ1+3˱s>0֚>>fsҫ|ݠ3p>oȣP.;4)'x|mYG-cr+\.́N@C T@.>뾕@DEQ|D… A E$0+siW2(2xEq)@i  #dB %|>&8*:D~ؾ }1T.zrb?R@z皧m? 9 ʋ7C4zC 4Aہ \ ~@GK#B;bʫ dTef1z),ɍhAG,Eu`1oslAR?@ $BG}'*v?ڹNLE~Č+HNGH`ˇā@m؇MDHdt̚Ď쉏":L,WpA`,xɚT"bɝĞL+x&Z<肸{3m3FQ|2(aQnчT':tGc I!4wT+@ĘTˊ H`HKաχ̻|Kt@Q<ЋLPUpψ2 W, ZEP,+s2_ŗJ1-M IM ͫ 36>HSwS'oD?;ʸ1$Nt19CdM{d,{1DQ|y@-PH"XPHL3ŁJHm1@S>әKlRC\3/4AVXj%ؾ܁6O1۟݃GHvX>d~OND@\4T8HэUeh0ZBR\L>E35\H]u݊-Hϋ[ hL A<]lS CW`߬:\X*I1[0Ot`KYH \M@eUiڨmLN9EwĄ́c}m07$Xjۺ[ u>6@]J#6P`=@ L,9u%N&B~bO<4Ǭ@44b' .\24}@Eω4H?9%l̈`EYT٧@S7/s#>>Y2 N䋚A >&0΅\ہe6ee#hLY^Ҩ1l],.8a1݁=VS@3eek LYL-b6({gKSDQ́GȎv܁GȅutslɌᓎbB:Ź=Ͼ-gx-+`^"*]ic?'NPRH>OAcDh8\,K7g4c,FӌXFF9\HU,TX8n%Abf^6>=L@VcEWLTXW2I [E`2d/ OEK٦qЄF&DqkX%a_ pp qFj$f0}lcΎfM8t3z;ՇeD#9msY\+z!k6(=||V\JsRH^$,|=MHToAcUt<_c-^FXB5peI/EF|MHoM4pjE,>PtLBZt%bIHNqfgYq+?Q- Z9iCgɼcaNkfb9Y[q zHb{.Nb.FmH|"&;v]4 D5.t5Ԉ FtBj4D4^],E-8u8^u#+MtFt4}y8T6S'ދ]bIxZ:Y_Euд/x(39Ͱ㥰IcO3e_fkzqX>Օ$3:NxhoFm~XH6yw4OwIű/С.^+b|u eБxpP'PU 33)paY/3GGb2B8 $*@+.΢f Z>6Kdf@nm$Ԩm#E2ew ̉c:: z΁9nY+7yN,j^TcCn\VzTg<Ƣ3p=lZYM4ܑGݣg`K!NTZ#&C2%/n8YQ3W_睠Cpħ恘:?_@^ :遏/$}tq\$%CvXf!6fɚÏ//0hЀWW.bJ0/+ .+BTa0AFk"z`T3X78_9DD DM$$Fh cG fF 58`5ÞQ,]NG TUuÍOiNP,|U /~w'Nrް9.<97jjNñ_ a$  3|VtwRK0T|ءXkbO%/ԐYe8$ZbR?'`/yɔnF xf- r j Wq M*LZl}eY@'z[{#`fv@`ނ)mbG} \djlp 3Dڥ'pƅcINY9/[ykˈ;ֺDEF-bؓmdu⎩@1D#wFed2Gi /,B uAT } CJ C[hYBbX_~0 r#pLT 6'!;He NJfЙbfee{`ֲB1h%+,B#렇>(fi=c>36z$vxf7KJ0>i6"\jV\|@а д9܎[ E3jRAF]b 3G@F6itpr%Sq-)jcS:.'疪ZbH5iUuqr?OB1A^Ņ6'Bk( hE$Q0WuE-̿jLAA,Z-YwVPRrPhRq FU (^#!Y3ԟ!sj;wS޴Xȫ">1:1!O~yr. yRA<'wwiEN>%楍6ůuR@VR9EvnꅒIYqp$%T;1$`e:nyӪ8Fpӛ!=VA2j[1h@@OzbBM=Crbц (A"]t0Lc]þ iGhqM{S\ڱ**r>jM71 bWjp#{)bx#4Y`gËmX:my@ *mw;y2E>s wψNnzdd"F.‰`e>`2 f\#ɸe_(X8dQ2-ؓ ױ//]2'޹{Y/m=!n).>@ b XrM /k{\bM2^@iJ09O ¥3\ӺЄ=فl)r׎MdD{&>ka6 o+N| Y;yv0fn%,{ߋ ;2ѽKo~;A[C =LF0^Zul٧6i嶽>эcpk}~>dAPT:ћ(txFv}_u\;`5xĨ|b_??O+8(UOu/!!=yHՊH|&` ` zA &] jB  utd #`&` N2 ^AZb! Ơ @Ba!)a a!V .A:b!!"b"*"a OVWaYU`Ym[m[#U% ]^H/u/&hAb--b..b//c0 0c11"c2*22c3:3Bc4#58"V ^ K)y1*y)*L+L9R9r.]9p`++>~a@`A$0dCABBdDJDRdB *CN$A@zG6H^dI^d`3aTJ IK$J - M`"KNeP PeQKC5˲M|OSjT>4^<M%dpOlèWRA*c=cZE_؅ݛeFŞeCMMYִM$&=C<f=C5lFqg:hЕ&\tj"[;pB&i(@x(nJ抲h$nL"X$vvO&&& n)x\yHH*Yn/ugH['`BzZ)Fg*R|R{FC9L'~z\=Ce`"h=8EbƩ<- 鄊_j&ah"j9W Q%4* " 5E*(ˆB$ctϒcOwވN)])yf*{D8ߔ2V_9f"&Yc:Hb҃S8E7͝5џ.v\mRk빢+|d)A '6W1޻ޫj湣#OtvjxglMD\jnie%RC9xjlCk=~kgܲII8܂Z+ng&I(f`鸦k]#>[Mbݫy+ )Rtl UĽbB JDza&^*8$EC2H{z8k8p1Љf:matDC>q3,34E:V&A ,:+֬y,m0j(`>ꤾ vj:Ԯik8Xr܈(z1Hpyi"L9C71LJ,Ú̉l .KS,ֲVBQx¤iʬ>lLA\AZNBpK^c$ƮH荊Տ6lp6Hh -]&1#Z Coz&GDla -8kzQ\);=IR>0+:|pEB3q:Qs8].p{O1'!p7v;r82[9҆ɋ*)kA*0 _HoRUZЂ&48,'1AS =;ә50RXVCшY\\+a0ViiNAQ8jrq6k32%L+8U.mTr[3UnU6BB%T/l:+;+\މU_1B,NL&`8, ĺ\,_rQ|,H G̲<\8C>$C;)W n,_ [sNh.£8L.Qj[5b6KT3qU#r Wny(^) vR> [BDp\}2&C3Sˉ8lhLarjE,.G4&;f<=8+^ yAM?g 4Nx" AɈN3aơTZQm6nhWvbZ'z'hKIȆ)ư>/ň&\ AmfEej jw_k \lʣqDn2 2`q6Ջg`7G(U'dbX[ #t[?uwul'Fw3,y8a,41t &nI6;f>8D U0@G q:m8Qh+si hZhFav_b(ށfҳǯ){x[:},ۀ4'i:|U( .*5Tj+0^&4a['@[&BCjotC6̖Q}C@4yWiC#&qe4s\ j{"6͡I*دWy | ׇ^›xUF^9C1g+g~+:B[9H_z4\& PV˂,P,C ;77l:D{9ad{JИXҬ; YA5\Ρ4QT1W"(-SJ_(Ob9ST{]Z\G~W0#O#j*;H+F{R>f КѬN&05=DM,葦>H>>65񱏭iO_ُD % 5HB6;U">S"1Ɇ\ lE;\-)+Q'  -P3Z4 Y Mc \Lg`]Z֢~x1>fG%*a FT" |`?%sby{iWYTV`C5 91L|NXV*>4!>~A#H'"L2,b@h=KC""NDXTC԰e]99gGL0.^I!&W % ($VBxNq nxƅ`EKƠyLHG `ݨpkxD(j.)l2󌋠^Vhzc#?SUDžBTk OCHnU"-Hm$ #fҐO$mgT!$q KzCu`[ؒ#LnB>%^6T`c ͅF(LFT[ q"Ţ#Iɀ {l c0L=auPI2ψ 9͙2 F̈́7<62$PuAF6D{.J h68w3ʢe?QծK[z h)m)Ԉi'FI-c,wI-j %ؐz( wjYP.MXj2FU+@ I+#e9HYx0H9@uL0ST3| @ <39(ԼGyMn Y Gj!jbSŁw Tƭl+ErҔ4&- #(E)HMJՑ}?f 9u=_K46ľtKgwAn7LVDG8ʏHoPu i_672/K cJg$bbߘG,MV'|\x%xvf.kС` "yXY6~*f LQմmf"@xjD6i<€ۊZ۰, Q۠`PS 1ı2!1q1o9qQ !! Q!"%r")"-"12#52#q#=V#t$ =ʶJZN2%Q"hR&L%qlL%((2)r)))2*r**>2+rl1%$s+A"+u, "-aBB-u!Ē-~'f)݄R&20 0 0310Ѡ1s11%1 S! .2S093=3r2Es4I4M4Q4a+Y5-]!.%d.g-7r,۲6672@R/LݦD|93:AJ o'JcKa 1V02$KSơgj|B&4>.?9@@]3A63B&2h&_rBOBm6+%#%m &]Rz8Q2}'d-*.; q;f-03NLp5s/dg>jP?peTKglc@jAtLAuX.CalDukgrMqM,CٰC۴R4E/EF:Qu4R;}THߪH%a=uD5JU?_bQ]UM@5KzȴVm2?Me#D#|WuWM %dʠYE P5k9 ':s(G%uG)t|JAT N=%NsIE^s/aH1'0)^1VgouaVom 7K$ (VmĒb+/&$BB#3[U]ZEbFdu\ϵ\ߋ\..]iG= ^h2hUCL@OO`O`eeuVjv}W?%6LYMkölYZYp |reWP R[&pA\mv\q\o\s]1u*Qh1/4>iMV!i_9+S zl#kEwtfkuMQkLYMT4vOgvBOW7 U[vKx!9hK6p)p;ugw0 I/q27#WTA_yc6NrgoV;w3H7~VLYkCwle2#*%=D B{+&+% TxqA~&"e3X$`oyBIFWzm.<4ٳ{Iv |wKI'*3viA~7=(vluYY8}3BS6+/%%n)I-xĘ/ggVxIIXGOp8W{wM+r!}fUsks'EO85<YU=k6eӔmY?WxIxmYkybSXRU%&Q A^iSRЋRJcY,9c97BWל7bԝqatCX͍xx̌ɂxnzWؗNM`81{;1T[Yڗ?/7_Jٱ(}A,]:M7va7ukuWOmWtd4Gv;Mu}וٟQ/xx-UOG:R!adq9TtiEaN|GU'0sC,_~XylٰeWYVonyZt9y6sMʺ>TW!rxZu/@]I_9w:GW b&G4$$;)dK"b766ABc;eayҩ5;myKۗR kVYZe{Ϛ>YD>W8U6}7|fsG%j;;l7նX9Fl%B1;[ۼzx{G;G$]+]OZJ1ʚS71ܙ$4TBwJ\_f±Vcz,ost]TM\iuԞPڳϻŭzw<{vxsz!A1T;ȟaJz=4<Ǵ~R~Y&99.{ETmS\XzeyRggI #\ӹ5;9EO?9A S9Y7B;/mk=AѫyM%=sz899B=~'Kݰ>9'0[UZx_Gs^;,RsGA kg>AZSjs@l5ӿ>k1_jܦüuӼe7v{ڷEP~ae;{2Ixn<<#5u>_#>Z-Ws]Ռ"E۝|A_*ܲԩvX?9C<-ڼg Hv)\6`v3bDرjպt(IzPӗ/ɗ/=C͚zr̹G@YD#F t;4JuMjʵׯ`ÊKٳhӪ]˶۷p -zJMW1eWUbW'~0|HVQ(\v tJ|Rúװc˞M{n/[ݼ (Ѣ@!D̓רq]jC$Lçu|U=>JtSJ.Uϗ)(hWtمWgHb0 &VI(@HjaH$Vr+^b((b'TFjf7pƣH GBe$rЭВ%YtQ;GqNx%L^SWUz^P%|9S߁x|g Bg!F(Y.ᢆ2Z .6YucRNZ"\8ꎸv o"+A]tL.ERRFrHۑI4kw]zI^rR&Ŧz.Sigyk覫.Z.X#u'>*Z[i>g=Vꅾꅕxl㬷[4I$*Pj1b4,U^o.KOξlJSu~чQޚH&AYeT|ӹҸ \U_?nȱ^Vbq=k"6fҝuH覘ژ{[ (ÐfoVlq7 YA%#1!on\&:〄l7ZvwK(1 34Bf;W"RmFQFGss34?ņoVc]}Փx6im6Xjhqrx3|獢g*F^#6Er놏.gelHA FU:bVRvؙ$qIhvNg<{%E+R<(i̳GCOЊB6imkAW*iYVH>Xd[ _{kRR>1ag"耫Jl88E*ֱ+%9䁾bt3)qd(+G>t#Ѓ߱G[8R3̓P:A}* XMOZȨenŚ~¢:E(v1,B(z}u"78jT<1)9vE{Rݳ ũTUn ̆!EFE :IBTJd;0M̓-3I(SG;fBytHЇ2 t47! =#""#Dx4QSq o1iyx^r´=pCh†! 3~cnX?ᴷ!{[M߀tКVQrjMi+ŀL5SSȊ\>dEa}͹:6+C/fjڼ@18l%?ֵ әV,;Srjp*nAיXEXpdwJdvE'Mج4vtx,W\fq iiuBT:Ո =h_5D" B|QQ5bUA)Gȅ2TO$XIPF; N=;pf(>Ay T>衰1U"BQܧi2M45VTVU@t36?K.3}Lg!7[&]*&<\Y%}=zh?Wvmٶ6&;li hGʭ}?z%6IS57^3NܕrVg-asV݇_s%ՇNjX:Z[lR'n%d>o/)17$>/\`7^L/&a8h o!MH|9;P ODW,QJhepr;e({GK*}Dqvd>8)УGAy$rE\YW"px=4- 9^ՑC2롫UV?vmWme)O=.{3~[Yg}^gA]n,qyNre[<,:h-0) " E ܈ϸl6n14s r,b#cr16%ɲ%c4WJdCs<3eR:C' =it4=NeOKWu=%Y[W]_gfa4uewU|&Ymw|^ZWS]x\jӄ'Y愆{xp%x=7>@ip@x|&Y~g{]aHxzfC(j^Z6z0{eȅdHizR(xAW37{f(r(ĉ $?R)O)%)"*(r+2+qbH,bQ#%TBA ǂc:b.(苿8A$14(,*Q :Zc=FR@30 Weh=tY4DQX}p Fs?OFSX'?fa6v;9C0dNXpio5]Ho5 ^n\gv%|Y NEyol-i} nmvFz-IxmYx¥lWS6 ^[ڹLķv|lP z}4YمP CiGޣ`o?}S?`|s?"7/J@81hwy@{)}闢GARt AHڀ+:(RIJS(E37Uzfdf|ue_i@4FؚkcWe[CXU'}_y'y*: WJ*p >Ūj8V^_ "!î'JR8Oo!*w*A:!'rH!SyUcJ˜7IW*R3wsXz屍0iS@i* I/u0UfD;Lk0U^%w@V=e_,KكgiuS\jW6tWUZ_ 6kxp٣}|h(}gmSik|YN}{\\]: TZqKkⵄZ4^Km[tYv Y!:kjZ6g\G!j/pԖ`Kba/–/lO&"ar*7+PP H $O5"Q!%01s.A`WqKKSI^wKoRXX8dfDWqMdB |CNP `( 3q9q~b"4H *I c);#K1d-CӾoL9VavH-{b\f|.["/'ʮ$ &"s|"2!~"0[u),<@R r A5lG°"; ,sWȲ$$܁;dxkX_}pSij-SLwj^g'd˾"7a/:q'op,CpЌ&ѯtI'- $/P  ɺbLd%;BC%!|7ƨB-pSO4$ _[f к 2]- [~+p7r<~ aq‡Aa@Pzi92|ӏ-vr:*&xLd< -A&=<:E-<b=]g(km܉& (<:ȯq|b5:ɏk?aLܥ,N"Sՠ K¤r<`LBְ۲'i/fgk/GvdªaP0)쯷t1 2l9a=cR TI-X0A*ٚݥL q,[N1,Q9ڳZڢ㷮~,2ܔO\0$LqGܴ24-@ֽHM1HK,-RD-jRջKQ3asߦ@(GN75 8ǽa86@sZEjHC:MAQrK *B^^zu&G1㸴-;д@ >.~'ݕFF}dy0t#{!a /bp10a`#{] aa#CiQicI/3qH D-9CUCJ@5456! 8RA'$&x)~^~ 'fl"Q¸pכ'a~D2_~꫎$H!Ԫ%~:^s&6m;8<'$}o~Aw̖̄"ql͝ ߋbΩ^!a[A-B#'pѲR|Ϣ8. {aor-G!'q}jWL:N$HPV CʉXb/0hЀQFBljdPK92I&OTIf%prɞ\+Hlԩ0jM]VuoYWa+WYe͢U[qmWw:MZonGpaĉ cXcȈ#ƇϞB ѣ=j!M=zrU㚒;ADGj7ius;}&੟#IϞ5fAx˛/"i:twA7_D耣q'ސdkT1 9@;D0ЍQ9Rδ<=)† qtO $mɁN4m[EIRC:G {\6:.yV0Co.ҍ+R qhA3ڨF2%]4D#yFGzJ+ŦJC:A:QC!I/M4*mFuWq*6ع5X-o˯CS1 Ӈ)ˈ3@δ܀sHܐd?#5X;> 4-%D3w 7KGf5Hc-Ȃ,tB8a9d )G7.YLC*gߨOX`A5*}>ԑ767^<^l u]\. PpۺAyc"C9aBB:ꤲ>>4Coy"d ԙ`ӘA>n[h:'sEB;PZ8OH2:LuQp14$>m*ND}%xOOt5PcnjjWxkX`"XeW6ٷrkt}Zkz{LpWrSc07+#MHb8s~FICj~ڋ C%4 ك:(78ϝB!K?4#(M"V?H CͰ Ϗb;C H goPO=2GX"#C8V-?1\C7Li 63,iH`:f>G7>^&r>G!kc+9↙EA&Q # t9yN]GV 5w*OW=%O*ṂwpA;ЮEQC1PJz2B=_%i5vek 2Mk,䑘zl [iBlF~L<4wgV7˙NKW땦l| #i1}e"‘X(vtIdYDw0t#:Ve2\+5QjA|?6#+&]=iD ^.fRvLGw 48ۋMj5Wňl"m"oI :ď&J?Q6d7"Nl뽑U<:ktx+LHE?,d#$36R DAav8].eL%RpM)E=r($s`'9j^Y'4inDMGGʸd78M(.Y˚ָCIg׽(N=BG@MmX)ֆDLB7O^>3=Bxgc!ΫYAt tQ7/U+G̗FRA:;xHVzq%ϒ 8M8.,cºU Hx @(@ @ȉL5he3 @:˺:-뺽j# Hࢇtl'z-4p(z:x#E<;' !3(~9s9j(HhvAC2ʒ~8%3ӫFʒ4\ٿ~78@!OؚɒD9!RKt#{P*38QRS +TL@l*rQ,B%&*S^fl^BWSAhK|Gc?3B{f;˿4 L;l.iIv˸%4׈LKĻL)L-Iΰ)!,EM P IT P.%%D+3* s5c6˲ٔ1ۉ-sQ1s MFN:{&N!Ry LKL#rAĠqPLt`AtHϨɔ%<E}?P͔S;Ӏ[+=8>BEi}ơJ˞sQ* I2Ĺ`\N$RP%#4BsgYL|A,z8A[,,l,U @a~c0xu.$UkF_-;Uϫg'8 JCgMgR̒ޠ`TLV|[UeYj4j* vUjH. a\hVk6gkW5R*A!.m4&kK6m @LgĖ=`ml`ll1FT[le)F9/;,A Z>gizekcxՅHHUCfkf>L:%nf]fZ* FLuFoӟGn1Mn̈́ooqhήu9dN` W\A5XZZp(girv]ipNp ''pU4E*nTg4|FVguq;?uc<сktJر ;.WZpkBeGw둶:(])mw߽߾R0OL)eeXR>S=ͧL| QPȈ%P:_q<vaVZQmE%G2ITV,CTThS _0&r~c0~npga?Kk'*?\b, ~r.'4TGlXubYwWnH.[L8ۈ5_axWM] p9,؉A?%$_&\xi]ZXEwyJWufv8eou齎ZjnOVU{7,GR˖X]xƻӎE czWx\񲗹qޜqRW@m]x&?%w]p'yv&[Ӆy7wsFt–GzqȆ\5sWUsa-P՘;[XwnFMo}%S~oء}nܡ6Wg8S{Wrt{GXG|$p ?|y'wFcSr%OdqHӄ6cmxVvgH}V:PBl4 A&4ʔW2Y-S=;UcG>r%3S>%̘2gҜ)fM7w)ۺ-jܺ1e ҤMjZ2MJT):zV8tiVBpЭk=zDHϢ^d&Š^LX'Sl{ibFG+m4ԪWn5زgӮm6ܺw 4`8ʗ3o9t[on:ڷs;Ǔ/o<׳oːaЯo>7 x * : J8!Zx!j!}y!!8"%x")"-"18#5x#9#=#4;MCE-1.608/images/09_Supported_OS.gif0000644000076400007640000034217212511671246016014 0ustar mariomarioGIF89a"O,+x#+3 H%.4; S[cks {")ECK9)Sp 3 ! + >3O^kw#(1@/_ ( &:&?&N&^'k#'w&'('+'1(6J(P%(_%*`+.[?.;1=h6N(6b 6v#7d.7w.7+7-8/9@9Hw:N;0<4=?Gg?<@{=BJDCe/Cx,C.DLwDDEP*FFf>GVHSzHb IJ0Nd0Nx.N/N2O{?P?Qn|BnZn1nAp[qe'rmr{,sssttMAtywyaLyuy{zz{{|~~D>[A4g`1B}Dc]w,K-yg||DAFI{DNadCE3FԟNVۢav+CCfUGYU½\SÍ8õmĤ?ƣm̼ҽlȮԞW;ƣָUڠ<ͮӭص֮٫ܽqڲ9@ϗݴܤD!!ICCRGBG1012applmntrRGB XYZ * acspAPPL-appldescPbdscmBcprtwtptrXYZgXYZbXYZrTRC aarg $ vcgt DndinX>chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2013XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod," H*\ȰÇ#JHŋ3jȱǏ CI$^(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]S`/\IJիXj ׯ~KٳhӪ]˶۷pʝKݻx˷߿rÆ'F\Ì+XrdʔÇw|ӨS^ͺװc˞M۸sͻ N_'x}>ҩdz}zѽo{wãw] .Ͽ(0׋seww58^2 JGF8l΀ ($h(\N9Wϋcv2Ht3昣ȣ&;|c͑8 6*6PF)TbsX;Z~e}\i] fÎHƉd3LVix|xX#? =:t#c#5馜vx` .Bhacj[ 6L:nN>#8*k챼z16Z;pZ7켉k?﨣gÎ8v%f&eߩ~lq>}66Gl]/,p8N# #,({^Ԓ'^ͼ]j9ݲ`u#nzn07Po*3J5j?hs.x"=nl!*莋8 Љ2zh 'o1^BZ鏑?w8ӏ騧zjr+z} {yo;M nrL.?=ZWj="_Ƚ};h lw=2Cksl>F la΢N6.>.m?k\qژNӎYc;1? Na۟'H(/酚MYL`qsz< :l4ML94]MxEct"ʭu#׻7ԓM XMU^Jmֱg0$tc}0dFbS&^S*tNv{g7zG|wM6PMm S̞ o&hgK[5倆۽vlhp{к}MY(lάUe'WV{Y4nrݑUK)VIހq/*t?Fq@; jHB2 {;JF4In0]')-Ē;^ Xi30$1uX;=XvF rzmET:-|=>ϕpt(BtdV%|h2;)J+4 S=/Q;jQNNu8 Z! H?Uo)Zb?[pevwl[;5D-uȘY5\f{WhbކD"kO9[^EnB >wNc+n,+/J k|6p0c`/3׬qΊiC\69sl薙7طvkYM6Q; htx֛^ f[uc6尃ә!7){*ALT?p:ܛdDlJȢj9">u]"aw^nf}7p{<ٟȽ 6$??}!)ף}ݠmh@k=Iz8hzY)`hҾ=Ѓ6/C)hO{3Fxp=bڒfH_| WT_VW  W N}F zt P vZW $ Ѐux~Qttuve g 7F.#GGkfMW PV8 v+xt WxͰ}3R~cXYX[X)tׅRӰ|,vЃt@w<[ qlxx$fh Љ@0 ؀ oHD肳wOW ͰHXߧ~艟XvzŘ:(wgX~z'tsKP@QDmEfft1sቲ+ 7wx{qzH :X}t wv wɸv'O8?8eq2Wgp, o&u؉Puh1jщ܀/N3ّ?)w`~c irG~NtT ~F&X>H*njٖZQ8nwɑp?Y OurtVq Ӑn i\<OQX)Q#stHf8堘5'&rYH!8至 vy̠ra rr '}:X )w ؠs}Qɐ i" yx~闚)E _7iș`i)玵i,fI0co l4$PP{F,:u1Zהa{42-h;Z=:Ёr y9멐Q*aW '}!e`vNr2t7qȃ䀑tP ^t覧Xxo~Cv`Kt[h7 XyO F5<Ƅwa`~_vQ:j}^YwѦ똣u8Z'*YǝЛ rǃ~YJN|IyvPIw:wv7/é 6&W{1Z Ή_Xiy*|Iu)jJzaqP D꟩= ʬz Kz#ez@;JYu`yگXIIU `0}24y{ǯ(x[]}=ZJQ_ꄎ Gxv`%'KyuZ sx+W('뀞 P)/Q9ڊVygo;wbhɀ1e J:xZ6xɛ W} U HᐆvpsjZ Xڌj: `/$غE)tR(_8,ۻWrt=~ v0,ð=d(Z-*/lXz; ̹z<c«{⋕ wU ں׊=(;UKFbX[wywr ˽zQȏrqltؗ~4r69<;vjÛ :_:Hdj>< @O犅 k kt9ئtA[(V©QIˎ[ܴ<~rl؏M;\'̻zuLoZl尝{(रMהrLxo ܉u :(КǧKl抯2̠H`/̏:l H\ i@0MRpkInil\6$ j)~IŨQ7 i XjʫIy\RRxKͿ8 K/ֻP}\3TzPt>Y0ԓ꜏|,r̗Ma}m׋r]_[!h(ٙ޴[,\βqN[m{0|]׵-[ 0sɭszjz%z坈~=xezN$KnSKMNRJ0b^>6_%Irtv*

)E\s0߷}9 :(ݚKݿ݈ﴼ[~-:?IuY^.^Wx[}0|K+-ﱮY['1u*xꎝ*R,J4/>隋|5/s>ւ8\]O؛D/@BMK -rю0q^&lai{,өP*=mxzR?y\y0Ga X41 *ܫ}zqH_T?UWؼàO- cUs.ۯk>b"qrH hԬaKN\o e+Ar6њ[fv1o^9pOrS^AJ+2,7P8U:Iڈ#|E.`+& #R:v ꤣ-Ji+i)Ļkp㎼ ʈ ك=qˋ jɧL7(rʂs w2-) v/2K33+s3RM4ل6;kR|$ڰ S:kРII*F'H'=ɿ2Ja&l|%MB0'{klDcPK? < FbL|VcO<@58\pB5=$ZԤvK[KͫRPԢNݑ mX[ԑcG!|IRO#ZM2Dɘ9X51.N9G~<%.-ƢnH9JWfٟy9)=7Bjyv(!riYa&B޵Ղ!TEB2f|zJJ) sZ^Jѕg/"hIZh jS2xY҇](5PSt:~3XLls9QG5j-z$; 4?1qQȆ[-bkd7u8u:!n]&;kK~jw5~{:/Ga=Zo!?4F EaòO)TaO{JÜT'#ɍ+|^[*Hp\&] K(d4dR=6C72,d $C~i39t:vv߸9f{PF< f  \V _LCs̋FRW+թ(Ƭf* 4G_!6앒)sc32C߰ȡEQ] YYFIZD(YT< 6 e?UH'W#]!ae}YȠH"6q\>rA #HA[^$0bLxLd0c*,eˤ&5 'f0wA"7M ·!2ܔ-Zt, -OFOlGDCj1iiJ2fF@'j1=E&p1)(< Fz2؈AQvT$KB3 6Th;Oy˙J>WIGv1#7|Jz4PSiB_Ь@yCIuft%TQ-ֳ0EfYˊBtlR+j0íkR[JW|UݝDv#,`Mn,:V'h6q(鈸Ț1UYb"OE[Rʒ觧]Og$njc:jִ (R&= m/TQv}QnWhB7}lc\TaN\\nye7ծY1e^D|Lb _XӉLβ'%h%R':N"R6ލp.^|X)pVCiij]\Rpcbxú}i7d8&!C&+X7a eiaa-Y.cK%5f4Ykf3 dIG=H:PrlXom chB'ZшbzhHGZ҄FPDՁldlB_e?F'MD&էf5ujXZֳ>L5]2t/XQzϣf"CvlhGҍ؝_tt3hr=v6)R?{^Vnx[ޓ l047~ jr#8Ct3{RYpG\L/xj7qKM^#ԴE \N!´ym~uu\t⵭&l65anLw3Dy]xө8'{~vǷ7̸:jG)'bӯDt#kOb,hO;˞l/ã]g|To&/ 辻:"C~ YW|׽Ïg}fN4!v OwM\;Nwg]|'_5,st r}7ypJ]uI: '~I7?e'8ig~ֈ7m2IO2 =xkm"5k6 =௪4@ @$ S28v!;oZ"7 = R"A@A @/k|ȶoX):c;"\;lB'ӻA&lB'D;+B|v8["gB0ü+ùyB6lC7/$,\ 0˯*B9O=I'Gs=/CJ,J)ˤ:=JuMI>+LTUT4UM]URĬl[J ۸5}U]A2ѫ4 HLbi((Qa͋d`\VfXP~oI\E5P( x ۻq E]`Wl݇\Oxvݢ_nrVv9P[Z]}:߂(Vϥ߂VtY{Hx%ۇ(=ٷޗWr L`\`\˼rueavXWn8܄ւ[=LL&b}᳅]ڊh֤` MWUއZ Ƚ2m܇1UL[ b``ٻ-9F{-pu`bqc؜X-9v@blE٤_HuA<_Oek:NWQYd%T]lɬeMGFdO6;d;bb"͒X}Hq&>Aʈ+C5mfTX8>e\m-VH}dp(`2aX}c[L#fAD}aM ?6g΍`]nKNBgdPYQ Z>vmݙ݇`}dY%:Vn%LLt Ei[6.yihUfҔt-RƂS~&L S6}+f8d͵W K9:h:t]$bkrmhU S> { ePGph[=fW\Jdoc֓Jan鴮kNexؤeaȴW=aҾiO&e5dX†kb[C܇bLfM.f'zxRT#tTNn@.o fbV9gkk=ޝ瑵n=MWڝ}]F6^nܦldhL.}x[e\^M㐅lS [׶i \fNm5eW={DQd2v[㪌_-`l{WGN*Gˎ]uYFVYc lc_ )r^rM=UghVr[5O*dns m 6m\:oVQ[^$q~Fr/N?~yZxompovMEz>w@pWO.^nvz%e޶yդt۶zy!ڢLܒ'j,NA Yvy"@ԯ`vxwm ^̤`?pq̍nVz]V p4br^ l{L#Dwxet­YnVelCOn3%o ooUPT7 J,* TZmuķp7IԱ&YTLT%U:,h (+%^cPp4q㥈G,idN'鄴x%0cyBt6eI0rsBꯞϞ\&mRd7vX)֬Z|+؇pꅫwiUvwѭK:q}m߶ ~wwf1Μa k,8i"È%iԞSnyt/[v6Ksw½-{6m\䄓.tt3OƽwxϑXr3c_>~i&6[ *IWYO?xQh!,VsC^f8aa'nh(r?x჏:󍃗ה 4PAAR &l1mODWDy }Rjl= qa'f\]]HWxcV)ryuãeQ\R':j,ʤL]:hRyZ&ZWx)"]f֍ר^j*aɵ_ :+z+++Z1{,*,SlJ;-*X[ާ]ϱ^ۆ.~m\-// <0|0 +0 E[7"M(l$wl\f0-21<35||29we#8w7T8+8;K>9[ڊ*7=;iqaͱ>;G^;N;gNV١f"fxx8s/\w<{߻O;'*8c;t,&#珿ϽT=+@!ptG @䡇u6lݪN/2M|b`)ʎhyn8fپ1m7n̝(Gqvѽ{IYxӧ{iZs)ZLh9\Y :HjReV#RFHA2<`݈fnjb[׍Kjo9LFuQU.oxkniqL8l bts&xɱA˱㼭1V\\cj9z_"[e>O-_Zg<d/{hDkf*X\*NÑZk\%Pz۪85+pt@2+r,c.o/LԳŨ[V- Aظt&8jr`3%+ih XNc5hf:d1Ӎp&k5 i ֬l׼; lln&8mw`9 n`-z$,d~JϜtU`#Wzqm^XniuM` *b9lX5dYc_8a#͉MQ؅\I"c* ׼MYy!u_[!wV$eXSC5W] Hm%N<XBXC=z >'֯=%b`}OyO]0eDOu7 Te\AP\}b9dyUUߝY?dJ$+7< ^`]L@=xe0eMy!@e eBbfu IcA&03}ԙ0!d p%-X Yb'igE 6 X1/V\"[zra^7$diU3`eH6m*qu[=0=fԧ0'3!N%h? {^pmAզ*d2[NeF$(LO\ΨvIv܃Vr!JcY&pR' Svd~Xϟə>4iv);#G/aN7s_e&b[NBNYᓚy`5LFZY( N\1**2A'cb:3#]i6+ݰ8'䗳2 =oV:}+W5pV9GHh9r+֑W:+9R9!.yj +6XdP4L཮*6lkB:9+^H>FFz(lJ,ÚXjVQx ܼý|,,jym&mdyj>-FN-Vmhemv-~-Z؎-ٖٞmPJ-mԠ--B`% n ɍ&..6. --BV .n.v~nd˲T:-趮.쎭e-)2R P..֮n".l6>/.n۬N`lP@Z.vJgt"QL9ЖxA0 /8'E`/>aCl\ff,Dru >s* .r VK;?Wpʹ_TB0j) KGm-- <{~*0T05y;C1\Qz0C4u::?17aqDZƱvh\,pP GC?0C ̢,D;C`+g$~p0UA2].7L9`CZ":W30Z/6co6k/7  e: 3TnvW$8C?'5?D= @2/3Ô5<2{$'ALWlΡ^Eg4a0 ?/CLH 8O@K3rpU[,C u:M2S722[W4f7;$5Xf/66.K,L@C\ZvH[tZZ/D[Ӵ{Bt4#v("vjC`/s36I%Gn2vr`5yR8,?s1}1P-roh6i55W2[n6 h, 9xceqη|t"rE0ٵ?s2?ac6H>={^ZM1ڟr~%퍑W`~L*hK90"׍OW|gݼ| ]I￾ƖE_ }0g֛|=.54hӨEŝ kVɕ07:8b9n'MIn\s5c[5:[G 6|#ބg(rað * [#4JcάysԣLS#mzSR]UҤ=26[na VnY s[ Z3##H +8:`)tz"' 9V ^6 <#(:ӏEۍ4h u|'ur Ѱ 3PIpQ4QEA -dGnp6ʩr,h!xņ3 cH8)^* 0$k8>2.3"Ǚ$;K¤P2qMz EEQ QI4RL)RN{'qĹP>0JJ 8. 2k,T̏r)Y<,2$8f6!;OZ6;S 0v +5D)IRe6ŗ_~_}8Pm-Yì!c<-$ Nv ` u] Hֈ>B=ɵ ,0$ y !%l>;t$Ғ Ĝni~^ub} &toˁa;+H3/,9vUKK7ԣϟЃ8s L"9?<^+,WJ]F7Sտ~K;@QOw]Ab[Bv$,-r_x4. ~l/)N'ty% 1@F)!vpE@QtCF_H¶0oLK8v0#ItSY9QgY, ` 3tƏp|i0*Hn F@6l^#ҐHqep|$$)MJilD򐄴M!=YJ0rT WJѕT qhD"Ж=<uyK!pF5dY̐ҘDf2W 䟦@jF~`E5L1w g89NH:SQk]huŽn;s&lc>ICtmc;[lbKЃJmg?cOТ}?;I:;zzӆ)UJYR1LiZS9lJS`O{IURT>Q*Rk]Ѫ*.KSXZViUKHJ4e\EHy^WiD ]CY@RoMlOr6>d)[YD Vj.9(hI[ZӞlŬO TBun-K?ٹ# nI[\zUr\> Ut:]4u(v!X~xwvzԸEzۑ|SӡW ~_[ <֪XEg`"Q)\a5,(5aƮI\bWs] ^=i\c X9檃ua\dWŦld'6\ʚW!%.{yFa ݆RϬ]4s1\g;KZe?YwN+ DPhJ  L 0 ,d P8(B(f;0 f8#Pq*B^@4Apa` <&@`=Ѐr;@BhEO@x T,ЀXp!p!PDQ HJ8E"bmQ" h( 8 au[ Ax=|_P- >PP.F(B]0V{Ѓ #o ,`8&v.P &0 ĀJAm,8!qD04hAPȡ CPAЅ=t΀cHԎt.HB"@S PD"wG" @ !>os# @4VKTw}rQ,rˍ_Y0ݜ@T[K &Mx@^|` B@TT! PP^AebZ((@ ^F @b,Zf d \` l$ ` .` @"dF\ @`~ p^ v/bV! ߔ@@ f &0@~ ` ܠ MM.6M.(``&^ HϮ\`$`H^| @BA@}@D 8o`joho-֮ ABor1 TaA @`ƠB(`J!a0kǶ>#( a 4B!01b"V% "Ž`h@:L@ >< ` ` v @0A 0jBQ\ Z`6h(@ 6 f@,@mݔMB f m3-6sӐr$p F- $nQ @ b@  LM A/.  !kq$` .`>]`a@ Z@FM:yA~@ٲ 5Q~` !'R p"]FsNv/D%aV~ A~ ` A$@L4r4 p !#I\`lK_6d̥x!J%cpd 4 't%bMPH `&#(VF@` @X * |@V ^ |@+\&B`B L`V`T@m`7 `0  VFF@Q/6x 0" PE`bFc`!_Qf4I@zjb^3 q f}/jaV c(2RZ`I]ofj" Da Vn` a \0,QAHKSLü4qjMql&eM]rMN %졩T,a I51`@Tq&T 0l b` Y-SM @lhb@6 mH=`;B TQ6~ARr)^NKE \G2w%^AJ ^&r,'.V$>@8Վ|`^!`ވN8\@ ZUpA€| ˎ~ \h`4t@: ~0 =X b[6x,+_`rbwq zr kf rv. `_ ;F` jQ n53ߘ@ i e9BM:aծ[qFӝӎWFJoAR} 5ϐ *2\TD R~1*[PT `ugj@lAζn A鄮 5G`b cmG!lAFps, $OGc1 9:%Tu[1TM%!#MoE 7@\TkGj!a iTWϲL Z*X& Aėa %97# !0"M0 ]H $).K2@!^]FFtWo!!Xj QS=W ` V`w<ZM3UxWy `|@!`V~ > d!U yTTf` ,@ f {@QҶ{@m( Z7$ @iV P`;o -&Q mkH+zi \z@D92_ lb~_V 9 <D0Fʎl{qՎi`^]F/^q]Cb 0XH.Q!]b,(Ă &Da #'"LāH(@\ą 55BRE sVK5֭Y…U*tC,:fѪe{6me㾭v.ܼtkWoYv81b^b!`UXPl+d_hȞ^PZ4jk\>2w s֠\ؐ%sY.<6c[v"\p #@"DžV8Ppt^D^Zx@p+0Pa'rF]Ep@ & `A @ Bx.pC\@=`B& p#p Xy0IA > FP/rD&2LHJPz#_@ pwǠJ(E:ѓ=@z1FJTE1,I0>^4M0GI  $+ *e( й8L4 0L](Z > Z  yQP²%P"`,@¦oHDZ: 10FX[ Y(pIbn 8(JO^d* 1(@*ά HuYXbN0<;5ss6L7>7+/]YAfggYg96ӪFsƕ7^cEWmYR66kge5s-Ա ӂ#v@ډD 4D(AM@zI$ +p &|`Pp`0$"JCúEY (XP"y&RFxA @xA3` ^C* Nh<@Tw- b #S 9J ܖ R= f d[^  J0^@| %E$$V9 "4- .cp\iYXR3fA%-fX%-{YK^rĥ0}yCJcu|S5Z^dkf8õ56q48I@9ίԛ= xcʨ=YY$IF ~+0!hW!"|@D  T /ht L LYGjN=@L`v?H^I"2p>qC. D<} ~Х$Џ ? U1`@0MRA&`4; LA&–98rB.C_hB ׃GY T .19( 0 ${U쓀 &,I^U$@K^BPK0xrЄ8"x)/t " a [-A1E"F (l XGB&t ]Du^=H& i+_y%>YC80G\njV1[|bX2vqc\b&iV clM\5PV }SL޲#_e6UQ#-N=hc}C5/ s3KI4+<$:V"pO}v"pR@1Fz! /x  T>r;}h@&h TIU|w z@b#W ԏ$rR VФ&18]pF$ƝpCɁTS~)z"P!0(>&;4lf #,dA( 塁פ$<:הC Aou$vꤑ" {'W_)u-dh ; FFPH0 (=@ "G!]@0BnR%8,4{ 0VXsmڒS#+\r6FRk  P#ѣ^?#s%!p (@jHAz+iO{`IA"ц@"F+Xl_&pU+f`l4*>c$>_` 0Fl=1")b"E F4.@U#G+-?&3nn^'RnL2?[N]0rP('.N0A'"AB_a _%3aigATgt.)%aUVp p2-#HQq2EGE 5A&J{v,ht.6GNB'8'7E,})!Ze ^bG7@#.-@` A]>Dgu wx(xw׌XGc@|Z6pMgPN pX7(6p#t 5[ AzRCY Rvcx#nNz(Ta8!|cgP:gWAfP<$1YR>ڑ Q>0);MP>: =:*0L`<:Q= ">%c0XE##X>aUgV.}Bh_?0J|eEWb/UTVU>YR_p[*$e(sQ>!q@(O8q<n"ZFrP+_j>3T0R:YT:w=p_b/Bw)%1U8CilUI,!tr2B0&1.uH3y$  10z\  z X#?t@H @@_@a1c V23b)c0FcZ b6cc͔qyKc bstf[ڡ#zs W?q4;g*@:p*= 1pFq:RP $P^Pa~}F$L-D:зU۳:b6iD&`"JY81/I0$i>ÔL!mZUc2,/>2,a,/Pn'',}8'g?R'E?hD=CxS[Rb*oڪYYu]pZ=[5#P"`8r?0Na!L\#Q2 1NYnsE .R@^@Hs$!Ft-gT^]@p%W$;#`T`9Y0 :0 Q1cHLKKTLK!kL#ȤLD4_$z$5Kg (|vR3Z DpjC*pZ@w@g :@Q[Lp!}>@*RoA-[nnp;:=!,QAE"g$ P>j: ɔ!*{S2K%%)V!@ 0 |l>.B"Gru2GFou]9z)T+nJp,@-3&`U6WR;htF .ZϪ¯rp =+ @ >?ЈR ] r"] w2$4=#3CA.LK431w5,c$JNZd6[4+E,8I:ʧ}B * =,=@J `BJn(}!ٻT~a0vYiUpk|}ʔ=i"F?0S !RZ[ۧ+C/g $/ʕm{ pk8g[AVƙMQ qC^ s\D4UF>GOݧ!P,.p#?.`<@s^ӶW)+l@WJpRp[\=˃┺U3ZYpyY⛾,[h(q,}x,A*3D|#tNgz,$ACԬ1p"'R?+rGFPG)W*!tjv+0^'@ `+t\%}-m)ž1Ʊ8L4)+>;|-GGM5nF8I8!a6Y\)%~Yg n Z 09TXc>[qAn G: lI^u$}>;%"* 5s$#=2Vw)3%# ^^U{r.+%ۋ5_;y(<-Ft}@\;('C'1 !q5BƊ,띝>bU,B$Y#A-t+%.4\:v1!$ڑt.T,&m^W|x )[C`RW8px8A%P'n$+ {L_t4,{x {66!1;rB pVg 94@K5㇣ w $i/X$6",)4l森\!`?ۓ؆x Q\;UC& ] &kpl|p#@P[,>p'ž-fƝ[if/[IEIqu%Zh)TX+<OE`S"PVMRIE־<'_%ګ:'Gq1饌FX-e8Σx[0aN0tlrUkW`hWızc wT'A |pÆFTHn}RH%MDRJ-]dZP9s憎=tQF2 =A̎3i%>Z0Q **fE*Lz4ͣartAh!CB.D \$I"|CΘ/{b  h^&?| J0P (_y0a .8pm7@ 0wGw*>p㋓MИ/n$qÇ @|A\1bH/A%# %,J袈~PB"P`'`΋0GHb (zp /xPĈ p   &"#N&08pp`=nq X+13)Q$3 tЉ86c|nB\pi%wJ R0HS|8I;5RItPSTs\]4VYgV[oUi&PŒ3Bܠb(h!!jS - BGxDE=k A Sp@fA7|!,0BȂ $ k#/hп^b 8̇Z -zw?$`ӷ.VC/`>9# УGC (LA$^@# 2CI/0)࢈~ǹ02" $pלrT<(^u<yVQ2}4/[8S/PJIO|0u# & T*_H0a&ء !L$,Fw!`G~.@̆ZXJIMOef!WSBONrJP.|B'΀<9h҂p6 . *4g 3ǀ ꜞ3Y c`H@ҙ4|.) [7 #3 0pBZH$ەp@Qԧ3Ǔ񨀼c!ț2\ o8Nofq~8r nջ &䞞PiOCx^Ђ-xъ$P4(瘮xaG>2HBf'iJ04=ЬpB6 ĀPrT >*NQBBc @Ba`3qay#'A:H#Aq = erؘ|-뇩JBֿ}eG{RyshI?l.I'$%(,6N?|k> \`c}&EA LhiA<BG0 C!#HE_@b_uE0#8B1rsjDgm@d34n@[RSrRFΏ5L ,vnpPA .Ri%Q.1"XĉphG`/ 6ْ6q1&%Où$9*.X#B1Yq*B#i2A 0(>+0P.p,h [pEX0X !ѹ"/pZXkX@~PCt9:C:;C>C<=<=4@;܈W9Y+scXKX* P7P@ {$p-)06p&X%x x94yU<"k8@ *Ѝ; p5)73xHp1"`pC &h @UD7K96 `A ! ؕ6' pH?` ;8Q?YbB/.BA*=!?!)> y06* $DH$+#—B8c)a)'ās9%)#L+{B9[9xn:뺮C;LK4 4̬)Õm#( 2p)LLC(`)n@MY 5ceHMԜμg M m@q<Ώ P@M NMu`bL ͔+H‰%p98*}Dx`h5 -`IhGWX9،Ў5rTh@؀ vTŀ-%G@@Q-@3 @>Q7r4.s‑;ɳFtvÎ_3x:/2CL#$/#"@A,3Lɂj9,HaTj)&D2!*1H.B)F*9#*Π<(*"QȂT[X1#xKWp,kR u9PiVUp]6#2$k2k=2mUlmnOy221$ pďp),)D`W0flWHM-d)utא۬2Y(ϏYMt)5ʤW]-# ;؟F؊XuXuM}e5}Ͳ}؈E d:XOU0)! JS/8`&X2Ž!`>Hhna`HЂxDUؐ53"2ZJ-Xk(Xh8ڞ@ ` /IP- 0dlq99S0a6SuJ$@ i? @>Κ *@Q[" _,P"8,Pп)R@?$pZE&Q\䀑;uK>QQQ#|K>*&Ra-pY@(9BE@D6zC(Ja(Bra҈um΄׏8, vEkPW!TTu uвHL,vbb/NWL(2b+*^cXL,c7N -F (*&@FX;x&`{d  .lJ8XL@jz, p9&x\'@ЋP4+*h,FCt#[ . *` jԊr0zQ  1#\O k+ IژH%6P8J)q T$& %ߡ CPc% I9#b=#d051e0%.6܈7UH~IqႹGFЃ1Za!E1aXpYǦWvw vLvv`͌bbDJL+fba8nlFX,ak=LNŶc@͆HEEϷ$U"k6XX'e p.&P؁"-Ђ !0 0b7(@`ݰF%`\L=]#N^cG MjPkS (9,ubr(UPn>p5IA0P;鴀F"ӣ7 $TKQ %قp9إJ] %!:1pP@@U(iZZZ(iDsy0[hv%*#snr(\[X1xU*'T#9['xS8jj6Nk&>tFoΟ|k`,{ȮtebgLDΑ(5Wlbru+atytbP)fِpXu`^P `vu`H3 -)x1L%H/q.(w r$؁!h&,/.:"6 CÎ7ڂyw6>h+0?ipf,^وiܨcڐN>6*ƈ AQ 0* qMXxN5PQ@*@x_H"T Ax$i*ab*.p hPPU:PXaaPp9Y##|8ժT` W"qB9 0p\`E`,\Gv H=68HZEVvÉ)za_af;\HlׄFHMMG^[χ`J~\aP;t8QOW,~'fw``w @,"U\(FD>~РGN.? # I¸Y.<  >#D!ZI#R]\pX.\hB G`T02,JX! 2 "^pQ`@80,(8p?^X]خ F%8'4'x9 vǞQ`^xI jb(ꄌJ!NpR e(pBCxGԭx9+ꉷB34we]t8H5e}H (è ou VҘ)$:[zRXi 2::/Xi4#hM%`} 4NtMPЅxЁ٢C-nu[#mo-pK\Ζ8t[k5ʺpu#MCU/G;B0p)6H(9H B08`~[ /r`#-4"T HLl` a `|! `A`@ NPB| g+ B &l6ʼ%ֹ 063^$`b LY|2E| ?pA \0 TXȒM̶f60 /x`}G8`ҝ P҄jA mhCҐV Mh ]YU<Y%o@COhs LoΣ4&M) %("_™@ԢEkKh0oH!=)Kt8+TY< x TpRv Cfw 8 +ߝT ?A Gp`8`t`5\`3/'BBG)>{V%E~`N*T7U4PI Vv nPd3! v0Xnlj vAq/jtpf*g=@сWPԍ|8P(H+YkU@[=Ak^BltA,Ą8;C$$L$JLLdLdO$P$Lt5 0QeJ0 SP BT"KKx <pS |*^@؜yl0rtN5 `x( lG \MM@@v,d\ 8 L0#ppDFtS?TiD&N`KTUpF9f ST\ Y\ HR\XtK8@@@`á`jQd\@|XLC<="(,P_:JU_E* (@(|A0\!N@@xvf)B3,ztp"(5xȅW=pINV ɓPINF I)bn)LWuQeik, h @4*PA` < p 0N%!<-_A@pA%q ~L5Al f؍눀U HpDN_HN^SH@8M@ &wK؀Ť ܼT`ŴǠSX$nX̀9k8@b|YHm@] X"BeFl\bޓl(|ZIB(¯-*hylBb"AD|y^"B(ԏOgĨ | ,(*`q4Ջd"NAdA8B#BrA(Q~)؆)Ziن-ZiP9iܖ# @z@O ȁss$n^̀6N #|BÄ&|4 @7t@3j(*MhPJ0 H~%fHpJ@" l tk8zFy `cLX@%Bn睑H LZ Fg/0UٸTFNLRE|S䐀l,pl{  Ъ"0{* hAøG" [ˎ̔H'*2 4i>j0)@(N,4:2BעB+|N$M%ۤ1O1 CQPqW @\̀p#B<3-m\ 5T+܁ 6tD DEEMTԆ@z@T@pƴISlnA4"ȁȁ}@ŴiZPjEH^ @f\dTtj<H<صF4X9䠔xHڏbXЍ`ES\GD_;UGIzxZDT3vX#$p*{ *T"03'{-h.+-"pPmiE_fGG΁؍@|0B<'8A 7e-ԝRa1xMC* J[uu\J :q__#.AshR1 h"Hn &T-#̴nxLP,\,pr aPd!~߂@D8PL&"N5dD4G=k8b@mA N/T`E8;@HeR|ݼMM,jLYGgxFd2EE`ydFͷ0#hJ RQq"A45%H#J#*thAQ.%gԡ#HH]%p⍴]j4SH3-2J8zd#Ah/a,G0YWZ-\© P%`)@%9 0*A1H(& ܒ0AxA tJ89.ҀJ}N#u 0m 8SK}<|4G/ܸqE (@a "@B !HGr (E`d2&.^z4q4+֪p0ȋrջiZ]a†Koqaō;^ dɔ;~'۷A=tiӧQVu_*txAUtp *zTu p T :V80 3PzF (8B .P61iR.zɃF,Z.9(B/|h60t " ~, (p!@a'z@pFR`pFp"RXD,Ē.)p3 JA^Š*@; F-(N bATGE;܈x &/h0,3C Z`b 5/H`Av 0`L ,/GJ '1A$& sQBA*z+VS~͌0~ w~8_MX` 0qދ1X9X/BGѡ)>`$9 \` T οr hq/zu3T08 !0s /Q~C hD!@\b&qS|h 3F141 %0Rh#u> JFj4L@+X.@9(G0o?y&^$s|`&_rҕ$ JPF[` "@Ā#"p?P0!DOa=8"\ P܊5 z#04"ũń(\T_XDQЈ&qZ*IEkC7Fq <⅘l #9d r`*`F"\]a^!t00lf{2F,aBv͌8΁Y2nlkZMHKZ ?* ,Ip "@u<"!`wpoxhA ` 1,\}%4h")6m`&vrpS0('0/`l٥"p >v s vaE@H!`# (A@Y۠Dp$(%% *%:<+TzB% @c`"tA 9@ LHTOГ4:4A q@ (~!z ' p [_*7JZpHKfU(;Ȕr7<&P%gH#zV@ *?2#…&p6"8~,$*85DeX25g-XKֳLfYa> ,pgXA-mG CPkZ3U Ԁp@fPy[ (q6\#4m<@^*{ƙDB0w!)I&h=\ #n=\ɀrEr)+:uEwMG AzmH($1W"D8Q2r*-[@ &2Q -EѠ'LCx0$K%U1@~pSF. 8ȥC;6P\36]@cӠU-XϵB(%䀡HT>`J xF.!aU8 (v ͎H5 F0Td*_Qbg}O_cR8`]U,I:=>NpE2"EH,Z ' 3xˣȣ<Ls@g~@@ yZflHzGL:% &0J(l \@fLP zs~bJt# R@(CE`9IsBrHڤ=zqdwÜdl" VXBw(@(JJ{kJn L}^ z*|jڦHst\`"'tm E|T@~\`KƐBBb>@N/ : P&@!h]a 䀅x` !`/@.a` FaabQ%f3*1Q4@ L` t;B TF`@8l(, ^?`@8Hi $OC~acPz ~4jKff` f` R@hi: =`(l (&$(K&RGD~%ƒKJIBBNƤKHLxBwǐ"jʐ @!\cibRb |#&zf|@'tG0gC`E6@vhV |9q'HOMDDMP |J`RG^JF-A~ . `,wro%_ 2,#2S99'#9+c99s3o3;7nT@ȱceX X 6zQ8(B:Dl ZnRX`vpXdl$ ^WD!`%PP~jI<|@ *#`Т ~J ,`&j?j(t@^@)(B|=0=OHn'F`@Q` DvT0N p`L1n`Mo  O-Ne.ǠJ@yzZ a/` @ l  rK@€1`dL$`8j^u=Di4 8`@= 8O`Ǹ @hsP ` Ro x )@ѯx_СᱬZ+[[A[\ŕ[[ǵ[[յ\u[شs^ PT15`z f ?-& T@4R=EARn&=V$$`?9 v |bZGh|.Gff T~K@v."vD )o?"l) @HI|FE 9:lGFIHJ LŘR%\@vx~pk` ȄL 4o `W%\OaBB P bI4iZЂ" 6@2e! %|ڣnZJ$tll VB$6. ,*^A* !$ A @ \8[!:K{1{s{S:9|7 ` `#^ÑBK@. f TVjU5Q2?iG :%jf&T.,$ѷ8"ۜ&GV`Enp6æV|vFdNx(Ij?9Cs&HFPwx`ota2Nv Q LQ^ɚq  Md8䆢U @@TX3gfqd+F(H.f4gWdML@*~G&c@@@ ~|<x{QqQ1ab(}9[` QL7 P!"V?H2V< IvUҏ0 †VD-g>lCz Z`r#Z8f=c;čM"j-ÆSc.D܈c$9P|v$@8 vzh 4!wlĠx&(o%v Z0JH` `\@iR@x3 "$ =FLmȀL`f Y|Jdj8VfX^䖿 Z*"S87鶺R@`rӎ5|o|x|94ș ʙWd@`Ә[QeZ7V f $:gB8 TݻND`Z"X," {r?.`Z^(쯕h Dy{Vƻ"͹(B|:nbIG%bKx1EdoDy<D `PXB {Ǜ$'" 1nn0 кO@: N,\Yhg`T"pD4#qnfK`,G:^- %Ff6@:Ǜ֏Dik,@ 4X"mS67>Nb.R- n{5rzmnM"+y P=Z4kiB #el)T7v}@h 6V@ gCBB#G[kr0Xy`УQGDxp ùx x`Bcz@. X4ɂ !$^!8J$A-`&E ЀH6Ѓ @C| :,d B3XA &C) QOpR0 @ xA04Z A^5EA7dPRKD%TTU+ ^$DctWE /Q@fzRD@B-H$!P! ` WpԺ 0D^*gjGopL,"#/\#qp# +d36` sAgs+ )@flڦ5؂.m@C<Ы0Dsn4T`>K]>1 :@=Eި @6{)'_oP^ S y H@!HX#QNYj XQ 3HkZOVU$p[A`0`;h(]}G!LLr4pALh -a,X x:9K PIJ3&` PpD 8C~4CW$ /@Q vMhwr$YȢ1rL#!9Dc?bS&7Nz@3 B h#T%@K| 3|Pgfd!( || oZHH&0 \A.`@H봀B`RQB0Y/AU8R \@@5@h }`A4Dx @x T65*: '/&(|>Ё@) RV!Iz%00 O*]1"جT4 08*Yjq)p ,q )')pF?V'G?|mmo[ڶoy+ nW98Ze jR(ʐ d&/'  EZ`e|*c@*|d^HGz$0LZFU.0 iDU04`q NE&< 6LB(`R^ ..@^Áv!<}\gRVxऍ))>s"NH`YZB#Ѩ30@\('x굚PD,+[hC@NB7aUU X$3x%H!+v3h `Ƈ~^ #X) (^]~$U8CgR.<B b!&ll$"YInoߦ)qGrOvv;QpA $fzP@Vt@P6ڜbNdF G$%F0LONQ,p"-wk@' 4Br"C݆> &~dx׋b219 (A> V)=CE.x*z욀:Pd>:b[xGӈŠ Հ53o ->vֳS8ఈʿL؂[sD ,"H z±Z p! Qs@ 2 "%-y5 tP"Vp{7&_xf1 .p S Vi@ta$NZjjM֢~S/}-lοo6a># 0?w!` ["` Tp  {RB$@5SiE!;`83E )aEP^@av:&{^L0qdqiZ+>@D <*p$s(`p@RP<2?zx1vGb(A%pi+@/G60"pFT2`qVEux'*Ng}dIV&1`#+bw!1jluj ?0`!& :po Y^P&C4ٖ8IaIא$Wv$F1+'4`51>p6j9y:AB=J&"P6&l\CS<`%0\ r&0X5Fqη;e@>A#W0#eQ(SplQ# 96@  v)()pdІ| .z@p<@ (fHgsgTP8sZ8Y荕DbzaSUt[WE7UR';#/W.0pir/(`e @A[pInJ cfF@w 1NLP" 5eHџ1ZHʠ :55]9gIz `^Ì%+ =-j%*8tQuK`^imYM]kK'}XHR#UT`m+1#0B*k^>E2+8! " Bc#< @D3A)Q* ;<&'!qGg2 E^~qYx6mU;dw3j `B/A/o<=8D W1a+ΐM*9c9oXi0M^/ J ʟ z$]U <;r F&%E .A;0P(0DaS4@<?euWP/zn ?<>`<?$P41^;? s&YS'rz8#PLo8* hDWPR`F:kꛭex!/q#04JVt 49[Z# ͠ ^ZNJ7r@B;F_=n`[ZC#H~5~̬\^sIW؜c# T>`P_@+&H=I0@+p br:.9BDE$*=TV$`< :?B,rh PF|.L_PpBN &p:<*pS3Y0)> >+`F"A'##OWz,uvD#k'ṗ[_@P..Lqeu8`Mw\yA( Wk+{9#+@D_ʗSY&p sQu~#5Pq0@@@/x t*]F ٘or@T=J!,da n6Fnەn " p}xaz"{=!d+B"01&>&f 0j4ڽ>>6D <({3E<'WMc79ƶ&@ZEBQʮq8!,4"_B'Bq~ =;`ka@نkx9CdNׁ WZTx$xa㒻,2vgAi.pE<#th ςəpB)z"zw@T y ` Y iTp՗.p H$H8TM..[ +. \Eĵ\\͕\\隞[%]Z g/-1G6+'45L P#/2]89;P8b"^¬1@xf}I"%d*d0w%0gJ9G9&=%0=pd/r+Ql*W0|sRc>#چ 8}S"#(_ ouw Dd<8݅,%!a[j#640(>#f%4PUgS]]qX<0A QiǢ1P.lL༣TL܃aftyN@z)@*L1TE?0,or(%sJ93"yaߛ"$xH7\aLPb Aǎ X ـ @<р Ɂ40"F0 B0X@fQ|#5Ag5fnd71A#sg$d! yyqĿV g`@*5V֏ 9yZԦVa$|@ x)섀*Ixg&Qkxֱ$vC$'&D;A9%`<A5E (aK#A d @  ^Hl:[NtFd7܄+LZ"PUL|y  -/mc6.| %`Δ Fikz_jzC 8*LA[@U(88b| @@؀Uf5$ ,h**_C@h+ \p/.q(QbH,ը>UjLխYnZ\uyP10QJLx@+upzM%+@N,/"I.%3]`--[]@v:?x`bc>/dpa= p_ؙ+)x[[ bxtq b S> la NP8/Rj5U|8!$W~6rS?XU{|vMG5/_ v,`*( ~ -3>3>F櫾^;/ e*Py$ dٷ x `6 xh ( 09x yĕh88Y (/+(0 Z /\*(xؕ4007jqg)l0+\AۉaAANl/` &pɹ!/y 1aE/B˨6|ìL9C2pe|C2<,M`UCBaPp`| -`+ k*"*4{9py2"7E/P"?Xt~,DN5~NNN|N ON)-p^oTr$z@>^cX>u5􈒦Xh,z043@uZ0l Xi+s9hl8I3$h'P0erl@ kщ ؀%nQP6y(7 ۉ6) ! +KI8h -4 C\`K`CK:>P)&)?4BTB[se>A ԚWX UbTa&B )) 2 ˂Q9S8&P,%(K9+<9 V8x+h"8ȱ"Ш jTw}FkTFxWyWgy5F[{$p,Osl㰇,yQ(a"я900 嚨H.pY;'6$t39x X&=8$9<')O=;Ou2BsxxUAH,#W}U5H,AU[]*\%D ZPX-hI+ V5*"F8- sYyq]P/pz!b. Ha.`WpWн۸ِڛܳݣ_W̅g>^(^ۇm̅ t$>^`WhÅHlȅW8a<`F u؅\pAh>`^Hm\U\sjAȃ5}^l8R0aq>_.sh`e>fr\` w> .CX]jk`i\(~jpjXaD@(>øeM-! Ǒk;!@Z88N%x -Xz#Q( xc 2I*0JiHAPSmI [`.8G;^Y@2\3G'pDz\FȪ=?pfTNLTfjֆ"gmmN]p<=.[g[DODP8-I-0V d)Zh֝-qYn&87 פ+" UH1h-& 5ѪҬкr+,"-Ai r8s NmP5Vp\5Ōz`^ Ɉ>tb^Sst O5تԒ10$Qؓ9};)\Q)X0 :?PA s2n7ldSI6,a(a٭0WQc"  px 0.ڏv*PJ9`S\Q% h8KPIXE`g`΄\ؗQ`ERKȴ?P,pTpjpN]AXnV?mpXAu^gaÀ]kb30n+h"S-JYf4MYv;Xx@r9(lpv7Ò>r,-r笵Wss3&@mLPꀝju\tdPiʸX f"ȕ3؉ ` h(6y=IdZptHnkhZ`Dr؁QֶQ߲|{*16&ُh7h,y@gHw!@!ES E "`"D  X\yCQG5gцZ2e eӑ/ yA ,jRBc5}R#HPUQSuϠWI&ٙ¤͚&Z5ڱ"C)gά[PZȍ*Lq"6RXAǖ-r8K#YXDʕ $<⇋7Ѯm6nRj>u_ON8rϙGWW]8;qݾ.o?p楫v\ƫrū}C/ԦK |LHz]zg9ggr(zxt!z14nACx+p@ 8ظ *XI`%7 d@ h Dh>d @!D p @< (@Ng`V)@"d:NA 8CI { AAx)t@B zvoh3mK{.0 LL2O@1Q=SRH43M5UUWlUUylFW1 d0U(2 C$ W!xЬjq &t4 *px6SA^vZm@g@d.@M`.D̈n;dG6re?6kowf]xM=É#y_ ~m8Km_ mFm3!/zݧ/$H}$N[5XW0?:g)dzTą#XЀwpr`% F Py&0 wyf, j]61iT^ \0 H .PE`BQa4A H $0D]:* H e,|Y@R&5ƒ&4#"@K sd]4q&QCB׿"Q^L*V9T6dž b{3A1[X,08 ^wiY)m` Ѓ0mxL ؋vЀq@ 4 |a3rxAX`x o3 'm\3)g6lԜ4iMln3O) t^?P ^Nu\q)ud}re^(D[fup":V AÐMv)We\LXq*JHg -D$ @Qh"S (Є >F4h``oV@Pu@d2,=P@ h Rs|@*RC!@^ !ăaZsֶEP8-0 KhQ`lD#;d4Еf*HJajLsK= ͅ0 b@Z@6F o M R0"=:LpI.T#Hфi|6 :|"FGG\&Ebx0ncx7qx!st8F49WE9rOwn1-x2,dZ@ryNxC˸a]>"?{(>ΰ2`{PS@^52:F=^4s-@91 ԌU 0,iI}Ւ d @Y P @z2h-)t\@P@m*"1 %,3@ӧ2u'!V . L`;\A5*AP-6I(х.]\NB]1 ߊpBAI9?$#uǬhb[ZA۷.y h:^젨m0Ѕ,\[`@g' =$1D$ \([Ђ%.yMmB}j_7ǃ3rŨCjN>TBˁ QK)D($AqcLqBoFM0.50R.~_\ t(*8~:U= 8k m~ P_ I ]n}0UX.PkZHUT\͕O IjD@ 0@-@\֡l8@!`Ipc+ 8j@ ]6BIՙI KIUDdV B5\Cs}Tt Vb ϢE"j< ,OL$(/B[ʹ|@i)*2@C(P.QO#)1&dNbJb6qdRcFe*^ֱ=@lA#J(P@!O. L<Hc-В9ZhAP .T^DZ.fI3I8 К>@wZr\Z]5ܞ5_YUwNUBH}ep _cA\:K@.n@_I|J<Ѱ hdaٗHB[4,0ܜ ׄQXpQpN*LW 506 l`PPΩGg О?B@(MiAvIA++J}"\15h ̀X Rl͸&1"#J"$f,$v# W\"5ZyaȁXroDV ~(@W@B*Da3B8`Uj^r 6j32Jy5fpd M 4*.2Ag IdZZ+ >ؐ<.BA,Q7_ߞ|_}gGXs'4>cU^q ? p_ \`Td@Ӂ'1Xq PliA@S UƬ@61D&l@,0@l $f.!FHdPc -  `h00SPg?H-h \XA D<Ф4i HIC$ qiBo| 䦆SGE($_ 815"e"& ) 4<@Y@F6'-= @"^ gWoBF0u K 1a$@H:BЁ$-Hԡ M(D'ЗF9Q% |ZP.p@/tʸ,ъ@X]BZ]"de!Hb - YԜ P d&@01sxfZ(pG!(Ȃ.r6+8 PH)XzB@R!"˂/n!X Dd! qdx%  Ñ!wOxq \ F1hD-oBèE/GQ"0 $+ Aj>v0ڀ-@$ڐe .R?g4n`C.!\A@&*  Z-@"ҫBjQRԠ0~[b19>cb(P0 H *`1݀Rӆ5=A@T`++l+T/<h\X_ .J@{tPed!EЀ`f\l@ζXH@!B^D l q x&D`xPp6BLXb^%%)^cqNz $45rPRە.26m[JrhP/1 {E; :/umc|@ o* *BNt` eBr3U7!? J*^Afœ^7}s{_\tV߂+Π.ki\{M{@A~ŏ AEx3XP֪$pr]23rӘG3:] Bx3PCwe&V܀M˜̀ Tia }!!(ggQ&L@g@XF=2)LlW)l CZ:b˺_mM@v3w;r*`; +Xk!%jn x(,$9DJ* eVP C?`'< C~LalgvT@ F` I @@] yZ 2bTNESJ%UPsPUvWUbV qBd@)@ - z5ƅ-V*@bbJB6@*@ D VTJz`BZp)8]C,-d>>j0J.wR Ԉ ԖC@`@FE=#hD8͆M?~F @ r WD ͸-lCEďF֍ݺkҀGaI-rd@$c= hf@ x AAr #f!3 8HO|>d0(%N#Zb%W%YSd2%^f&]e&'{ `@\ΜX a(( R `@(Q3"t(2+g Ii1^H튆Z &>F T^ b.\ @00D\B5C3fP9s`Jr ` sd(`B=: hCh>jv8Rk?C?O1)~e~(~ ~kĠQ9ϔPi'RdEZD -ϖx+r˗ A  q璺 fa@8$2Af" bbD vO\3*şRС20TCC=tCC3tDKC!DMDITD[EOCg|FoǼ@PJz`AM*1R2.aeb\ *(T b8"_"81(D@c@ 3> u\162P0J-C,j(4ܢ =m T?I!L0>7DsHK_P+e6`*BI`<`8g_F@ۀ& XHnz8Q9@DAE:3X U/;ėB"Ca(A!)1Ԃ@=R@9`faI-rlG RS:$ `}jRf䁲e4b]amac2'kboRbuR~pc36W Tb &@ @Nu V>$ƈBYڂM,'eFT@>6&-E f/>,|\6.g(f-^0"Fk<#3#Bh&fjW,ڲ FO. Z@flÁTh֢=5>6RT8Z ̀x>#G`Bo rWCD)ԆFEgoMX͑YudfwKVaMu z=IIc&h HaHvK!uT v@\`_\ ``Ps>¡BeqpUt}~UPc ؀eTAT!8d%` H@80:`e\P눖2VP7F.c׬h#<,htC eh`dӢ_foV\R VǷCo!6N8*,t?L |ku 븸 y]7OX_ͱDt-k`ANA'̩AꩁykΡ! ngr$?a= ?{3N #`"SfN&?ٷ9bg H~L=RWA fN ߃2@1Zm4,Bvf+L5<4G4Uf10V>h:_N6#]C, .NO-D BJDHq A ^@?-!ZC6B u,YB%3 (Qr%H R%*dȨQ'!>-Ӥi/C~Z &,B!k1!Ci3P} *-0aҖڪTZSҨYMJ-4ըS YjֳhΜ-k#E$"AڠZ&ЫG=vl"Vٱ-zlƒ4+[TZ]~E]wOnm4lղS]{މ-Y^<ԫ[Y U̴2pD 8@ p`}q p H AD(>C8p\ ;y`Bև!W| @lJg)x)FBXDBm\)qAJ#]2"Ft'\4ф/D@gEg0P@r 2L:)HXbE!TP~褓Kd 32Tc.T~ǧ (-TUITR$Lg=+V42KXT6,$\ *,.$(+X0vmgvplpi' 1 ӶO, Mp%rAWr*̲p\+\M ^||`J|@}} LH9O#pz4p ^BX:3~}X}[Atb~<)A%LDP!@\%IFoH)Cv?0h >Pqh\iFd*kK\}JR5!}iiGjk|ѫE 2+S-dQXF4rYd@3YE,kqF4ĠaYDb'&,J@X@1Zhbc'я~!:pC;a?~"1B<" 1Jtb!sdepk+rP 0r _@1(Ӎ)|j6l/Bg_D*23"4<Z.P+R| Eg{vl( }@ ,`z@ G!؀` `0 tC:䀶C 0xZـMk\c .Z'5ɥ )~a;5LMT"xaFA VTpB Kt'"aIKFR!Jæ6td0,`ar ;_)>{UԤhJ@e5LA@ hLըVg8cx.a`" ``e([HX0#e544氆E찊c)X.vcC+3Ё.XS@3h3 6 񋓀cʂoYFQ$k$X(/a/P.ZQC $@ UPּCysT`=`B@ Ah䵷] *8 .,[O-ĎJlBy`$X`.PX \ЄeSG8"< QYh(d!I>2!\L=*pdĨJ+ Ri5WIU `1 ibJT(@a$E+ X_hXJ+8 ؂*ƀ"`AD*@.d ( 9F75kpS6o8ESʔ2el? HEe/8ߊ{Q@@ ``8D9u`>ڊ`Ԁ@H7f Zq 7gkY9Mt _o<8'AZp mp՝ m'2 V#0/=#[N_L"|ˀE1 Y< IzT'B QgXJge )X0 h4p 2Q (ޠ U$ [h܆`Y Y!`~z.YaAސPMo|+7_S_og?X,"=ʠgv ImL:]h 7]08  i.(^0UBp80p 05#M{733]`M*rp65~36"np:pVBj `b6K' o c `t0Op ptb(dv $;5+"0ufR%9}|\qddX1Pe?g=)se>FT!Q+Aq85pfHfc@a|PguvT? '@PhKA 2 ڰUN \j.@Bz ̐r,]&@,//  p3k QDx0C؎HxE[t~Ǧl$~~P G ^txGȑ n nE#/%p$*'o3!K ^]Zsp`^c3` 97g83p* ASM8RMt3@c3= M b  V bU(@fdkhtG4B;2Z"[/~i'N@VvX/J`)1OC weei@)vɉ=--Q*>y0IxwCpT ,R5UE y0 b Ƞ  bcBP.4g 8po@iNY@uMW b3p`kxiZtvjg9[cGFzD  p ّ!Jn8P8P +bj@:76]6#ps J3`B`!g1qf!RpR/+I6#Jds+paW@W0ՠ `zVЦR @ p*F7PtdVtǰ:p!rOMW‡{҇((@ QRv$@eg.@H9`)Aj1+7 ى2*S=) ɫfH HRA D 'A T ۰  @ruu~( 2FPrZ/7^04T'~܇}}} ~ׯj}1E  l jG_T Om_tKHEPm"]Pu`!-@ @BXp Ϥpr3I4@,=I@(y!7hS!?s6Lc*\D0aM p#;H2Mp Rpw `tʀ SUڠ K _q b؆)א yIfKw.mpat/4'/;:/ {#򩩊vS vw|G*'Eg}P{+hǚ 4à `z6 `s5tZN@|/# 5n.sD/L;'kl6v!la\I FksHUFE9G$ HŰEH `PPF3^#@;DB/`W3hK4!t\F ^Ke: 85qsa3A-K(&o5C5l3sRz tptBקSװvi0 Ϡtfu;/ ɐ,#K𩠪ҳ)fDe=~7S6Kgث=sTn֊\lvs,J]?Ip mb X &zo4.M.\PAZn +B[ s|X5YΒX ѱa-$?P/ ;@=L $a1"JA{@JB%`<r3*B3~_475?]hhK^Kp>L tvUaU% p b @ q Sr~S E r,@d53g&4 ֎$0L>@uxɻx?!ʵ*l׎y=*|훵 ,jW k˻0ȚUԡ- @Ph({`"I;h:n (T7H_Wq|O4EDR4UDEP$VDm@E-,r` ] ] W!"TR (p8/3]$lеErS6bq$[LDKboI719"^j*jriIAйʰ b:mb;0 0 xSAG%Kw-N 4 +# ,B>@H"i@?EAxD+R+vfAAM@8ʏʹ؎M˼l,ùyƚNJ:hM 5 C t < _<rz| p Z n K 0 P :`c>!+TL3`BL!b5Ib(\Bb.qM7ÁH#7b3 JgAD <Wr Ы  d %v PF տ X 0f K0 P p K) <.MPQ@ xz-v<gTdRiFflFgLl'xΐRv-sB  P@< {n z/JA G #"/D/}̿LlH W вF{C5BR`$;#%$<bc3 qqȤ L~#p6pr@"_$MN4 3%Җ. DF :X&Upp;PS~tUQAK P3H A}b-RlQF7VC1yi6DB-[fE5i WԤ *MOA0ZKv"Fp 0RYx4y,X`/VWđ# V\yd -XRj>uscǐI8ƚ/s9e̝I\1;qݾ[lڵmƝ[n޽ OPʙ5Pr5@. hXA*>!Eʏcx&AVX]ę؀ xPLP4Al8/ Hh`1I"6 E#VPtOh]nØE 19L`1A `xp bF+$L'K #z*ޞ(E5vj胡̱I(3px}II4h(<mjTCI1(s\8*Ep'" @ mK,LB~P$japA85@ŌF4yhh>ЉB- FQsƬ5`HE:R;B!3n@vT@!h TX (@6k_R`<(Jg84i5j @D#Ybꢟ|^3Fh&A;`F@VCA``_dm0HdblH[#0DVfE#pYG$nҋ- eEpD(ֲV &AIJmLV[<wڝ6LW!'TEY!ovS"]lTզS~S Y=rO/frhx!.A*" qp9z6c*p| >+<tMHא;qL 3V 7!x XG =:c)t@ #0q f-"dP bP,X }p 5?88e`WsI- `o`$$I$ dKx1r  \CJS;\ɍL[F ֪굨^i9{9x)ެcmz|=+M cnM Mkٝb(RHV*?G d;ܤ6~8<[v x6`BpD,BR0t~XD$@p1@,~8lL@|X@T@d D@ȗs) X9,)"g )H3 ЂT "i:a*8 X* cя1;8 @i !ʙB@9jIhz`6p@6(-ʂ8Rx2)YPX+YKa&TʮyZs5X?I=:`:b] [ [>&]Ԕk.g6UIVk4y肼!Y#sɉYAۮYX3Ē`A?|& "8?"8zG{LJG{839썃Ԡ\)"p-q!jX ة$\j i8)RYh` p(CY)tH8Hs?I"#@`iG"؂'4PGP Q!yj@ahO6YVP#;ɭ݃:ɥ=轔: \gŋ MlZ>IL[I j'?2y('c+ IV04)J?<TPF80 0NN 0, uLȐ{ rȇ7P!-"phtK!8,6P:'hd:x !iv"h:i+.2 A;k|J,0J8쀟++k`"ܴESx2Y3q ` 6i=Wْ%ׁ˴ڲZMWԝ0Mh!E[\&:Sl>.ŒT)O,RA_pqmІjMpFyM<u\A]dܚt>RjQD]i.Tmqp1yTPɹl$>2U09S .`?ӕUP0 BJ j^ b} @qfE3hVhf5i2 VfX0XЇp s9_kfe _3#W3߈Xl1[x9@;Xa hH&X%U!ښ!`/㻡9)äʥ 84 p0;p$P` hvP8[7.8I`8=ZPDx.pF@`MƃqI仈-ӈDžB@_E]S峦|`hhiPXqQҚyT?<`L]U- 7Kvr7R!y[-HX^^wVgxN A0h%X(VWH@e q-At-Vhzg~u`levvXh .$R`epK*`5 !.JmUٙQ*jӺv%v`1hJv[*Ȃ-.xp#Ћ d8X[T[A9@ȆdD(l-$~1W^_:SAE00Es܊ZM_dSLKEFlR)ALaoIjhqܴFmHfeΆܜ;ROdyмdR@QZ* ـ;+*:(D"~+Qb"V `` P0^ hH"xr.p&HPK/pEw3h18Ȃ,l"h%l+mC`ez0\EaS.mxn4EqrBvihypwiMO#;e@)XAd%f-)37wwzGOph}O P(g`e= r t {x|s6 G`W GȂP&hkaS:"CG4Jq: I B%*li ..ɢ*R`XsC% 6['p0 18otvb.,@J~w$GڢLIHeێuރ_\^OE D-Vo()Tj?m]kԆ8.o_L`~ .5? $SB6xw܈w~ ~{G5A/p~ֿFA@{`Wb  V` Lߚ OD?cj\ r$)s&͚6oIr.% 80j!BG|Рf8xb ,8(" JPƒ6j E;@|8@`{޿,$ /Q8b$8u>2RpH!ʕ>׳Wz72&c,|A{g DŽ򱗡7_}W癇* 4ҰH8.#53j8@99?8&:l tg?s}ڹ'Y)hsNdBz5 LaAmELp hW!v}!|"B^!L4L4-b3"06N5B6.92\s O#p5nl )]qε2=ON\Soc;t9%@Tt1rJ:ld@Lf9JS(CnW`F& ;*!3dr ,GgΤ PB Є\3UL XFyT ` &@0^> !t`j0=PQV0 q/  fS$b"7@ C\ D@c B)5lwp l M0|wBQ6@ġE"0912 iO^m>s&~AtCo/mrD&BF1 >(< vp+@ N؂ "` [WSVcӋgSO9ǹc޽F=B:.-,"Ba`;ñ`aԥ  saLa3L u*3vtvymd_wD墑#?׿p?4A SB+$,ZH*)4(˜iBڜa] uV89] A 4Q ,L0 hT!Q0EE=(xFlP[_u_Q!_ -HY leȕg͆@d^$H4Aā(A@hͮL ĞApb)B[b")>[*Z(먛W|860e5U~M}IuHuTIJygxx~gC{'N$3;C7dC8 VJ$<Tpxg,Q iA \X|@@ LņP^|@ƅ0 _QqLFPiY\Q_YD@Pk xqg L޶@M`nh`84[N@&v\`@@ [ d:@dZ(c29_.9x1,9h/u&h 6UgZtq{, k0p*Sn,-*H 2 C|$ @2 vtuA#P'jH t􌏱+F5BR{0,"8 &L d| U4pȞP8%ƣk^Ռ ]AE%UTHPդ@D]_L edd1lFo\\"i[!ne& %о(@*:mF&)W_v*&ge[[yA09*vg›j&d0lJ0xjlrkڭ4)̂)()ē2*B#(C-3*@H0B@ Ā*5M# ),*H n .0Vkڮ <TL,@ 5(@_,d ` E l A |ElWa^aR@ E Fuf ro -X l僉 HA \nA:J镒 vp͆^b 6 dQ[4WU[W,6p&1RmE +qpzaa[2* Cf0x4B *7B1,_Q @-( CoÇEvr'.1q2! r!H2r#8@h $'i`(\EZ%@RU*W'KVxL\L^ ,$Sc@@^𑔱^<bQS<@ L (FXH@0 !рZp,#4#Al)]:A^_f%@?ˀ p M-m/ph-,pGv/&4tCw_1"6R۾099E0(ı:qp*ÒB4jU/+/@C)2XL kzgyx:5z'ySzB:Vs3*#"!q$" jſ%zk Z\p^kUȼ 0+@mL%PkZjK\kf+xѴrt@9p_jrAuV& А&p..czb:hDg_zt 7.fB;투f),Ì׿ݾfЗ4nc4,4lJBl,eX#4,3`#„Wn`{`jg y; Б<##4@ @ $yZ ^hQY *FtNj( 0(}yXPS!HZl> K.v@l0ͶYiŁ# B#H" `"`i\9@VA[uk!׶!i 5t& 1Gؚ)zq䬦2~~{(8:mImIW u$R{!.4(O򱶃`>;;>K8A })fe<4q)IOV<:h3IyQP1*w#sO4]v36ؘ2~5Gk۶E&0atAn_jղn`Z5k)S$Ij2+HX)Q#Dr46)RSuk׬j*&|޽[۹w[7o߸ ?8Ñ?_:nv}{{w?|yѿV*`@p ĂY`X!ą `: B  h`Vh.868PXtЀt0 Dp  "@"`! 8H"mD$- : x  (S)RPA< S7wXVP 38c^p ޸ȉxFH* %$hpjLO='0J 2&ijk0*i+j,2KZ,*h .K[kVQEPA@7ȐQ- fBdLiSr[-jw׷X=8 ^x` ~aG|9AYd 2 L J3 !PClK`!t6 .(PIL@)R \ $=a%>򂱁|C6?$ QB1 :D " R ,sp3 !v@ڐB -|p$8idP `AQxԅ&A 'fSPqS+J5-1PFXƜkӠV zXj*agXJAjF/fKZ+E[hq?pi$FAG `1j@^PDHA dl_mlf``'0 R_`1FB- $D` (@ =T !p"D J=z>@hA6@5y )$ =ے6yho 'o"@s`z( }`L$:CuD4aWp&"G8B\](pQ<؂`m*O-q[ND-u*+A5YR6^Zt=zdZ4l.rV-݅K`gZbaho4a 2fY 2۰GXt@>6C!2hE5zQn%iGCCpKaSH0>`e@  (*N ADM<2΀t "&BH*ktD"P`RVDZ mlHj҃<`LB@bS! ҂0A69d!%(T k%HXv;-tK\D&1b"VUzbY kv%ڄ b,k4QP+0,? )z 4 ` BeA^p `nMnXW ,-UJ!` zkS HI:a "!ȁ0g0*IJ+ 3)Iord=Hё"N&T`-Y]b936w (+HZ؂+&|aDDFPx#/AG mQ[jڷ.{r[ZT%rob=,ۮ`"%ְgKx% h2"h FEH1 DbYㅯ?_ ](180jXsl 3V@B65Kg:@QTgHgD FƬ H<BPC(Q'N4jll@ lx`҄oT9K Di"g:%`o#T[`U x! #^ #bn>0 yYӀ ZWRvV́&]%[5B C(%-AVal. 0I>oeV!D`e aƅ^7?lą~a#"l* .$z/܁<́ddv@eu K `s$DXJ@. "jQ@@7ѱCEB7 ^AȘihj$B^i.`zj6`O⚆H̖JMdD *Sw~`&kUC Z܀} x5 F@sO#>E(Wr+$ iK yTp:w)4!A_mJ'XJAuDC!uvC? :b@x1 fGzADʤ.٣JJQF*SjC'jZ1xc#du|]dGF IJGtnfjO?@zRD>$gI&D~ΠC< i΀ "$G,&<TQYCfJzPGīJ$ڑ6pNBG<`woZ \6ЮWQZDT}.r)%~bviT8X)'[@\5݂B[ }v=lv)$> R Bp @lHy Hzzz{%h{EfDv5arxA|_9E] 3?6  B X!D es,2N.@̞FsRSR;I1Bnvs Hu@>,`T@By6$iJdDn6H,w BTV' ҳPm)Z/T{q_rأVK 4AO S:[(_'k},[i-rw8p^)kAR%~B a~kZptf AW)9a-b)c+VQMBpMBА g@`oh@Ch{$h z`2Ǖ"E"#CBv@8NY+),BMsٛB&b/Ɛ 񻿵f!~PCZ^!0mbگ x1-TItIHtHաI]d!@5v{><$ ̬Də?`R|IR&&M,7&DV Dli/ A$i17ʬ4D=tM'dJI<Rg8 Ou"uXp0T{$DG} m8Q&Q̰#QUesYsw ""l)&5g,̕tc&;tWA>O8D󦽽ê4X2KgNCу/Eq/22/I/ESd]5{}\5޺>C$HXf'dej0=H8͂ l"g&;:`򗯌@ PB\e R>Po:@IH6{k$RoLdo PБEBV8+B`A#c}r #\xG!{ȘHc'OC(QxTǵ68bˣ f͒Lq( ]F`DIM۵Jꓦw pč\91/}sɫgo.\wÃūlw6^uO__PFDG1|-4Q# $(\p1)I=,!rzSN>*jeDᔪ{!U|T7ΗMyË+^yg8,Q06А\1$@1@Ѐ A Bx3Yм$.jA!꠶FGN~b@^Ѐz@ #N6cv‡KyQ!T>'~Lt^"Ԅ@?DYT>D 6@**PB eRJ+2VcWe[x_Tew>3.-6o>f"4K--Jޚb )uQDV;Z6h8y#6 !؜{Gh1=yR&؆=& gH*@KsQ~@  यkJ#f0WH l hZQ mhڈd$]`;"P(ND@ Xc y`*pR8*Ѭ M@VP(iF;Bz H?hBP8aRtJr n!jw]R!KQ7>LyxAo,+ͩlxZr( ~WcvBY:-jaĴ}ԐE.[̳h)Xшt"3A:tq"AL:(D5zэvD(C6Xa.ΆfR^!&@/") Z4 GCu@JԢΨGсWT>;FZC>'Nq6hI_UmƐAZSj" e-#m6|cO> 11 n_}P1 c00(181P13S+v Wֈ,g?Ch, eR! 5"Bf#0cpa&n5sn ^d$!g KfTrD.30#4E3"\W2"\FcC3RI3C3;,<@lZZ(q0jo=j<jG4 JyMz+|0}0e<--P֒.q` P1ܧб!IaYA9!85"SuI2 x"73jcS "!R48wc8)uDT$R%n`X$s,h`4Z{CP$(HNcET0Rc76 Pcw{gD "97/!0 jm[[ 0oqԠ*r pHBfaKz$Nԅ]$j]kY\\m,z}@liM5j>!N?bDDP0^/i _^)fz 7b٦n*h&5!e""&v/Z)X'q% +PGDW_$"n f5!B T@."D:$ctq&DVy$|$*@GwF` w .F h{QfԈyhӵMB⢶+]жyED2;mY> {,-#KdN>Q? WJ 0@Nj$ {h$*.&#9%y2)6.og_s8;#!5GW:!$Rr}Gg g~D%+cGrcXX!EjKS9$*2"*yi!02$0r+=\ZPಸuYE? 5u {MٔEdeŶ X+֔V]tztfYr]z 'ӻC Kq- 0^PP0O 2w~D~~};~ADz{5T@6Y#W$UA/#;Yg(`QgXSNr51b4| tL) P! #S}3(%@W$T$IHg!RHI;c,N?5"vJp[v_  kR z]jq=i@<ʴLsQy;+UΥ +MHT ۣk),HUyMqMo= ˗ۻPzOPK [+ yOnq QRpWplppψ,!43!=G !-PVxE!<s&Hs;&! !0$H7^ BP4 JZ4cDF~*YI4nfv3rBdBh$ 4;@,6ZzSH|[: HALb@7yCYyS+gL(:mԖk\ѕۯk{,{~|-AƼK ?H, aaaPa 6Zm&b)b=F@$P"g(`'iv%yDIb .v `eYEpJVM8vn=bG"'帀:fhFvK uy0q ."cPYE]`ZZN.P[h[7WAyU̇'%#ݝW < m=e+i.ljh+{v,O7}ܻǏ?=[ CMԸ  }o<\Ləlɚ,RLᶡSgJ7x`Œ)++&b ! 4M0jG5X2";p4Dp\r25-Rt̬!6Pt<'>P !p@0 ``8`% @@-naxTKp`*IzyĄk3޴wޠ>b,l}Kme&e0MM{Ҡ߼뾮; ^ @;^{P 'ڞܮB) >>5"@%Hx orHDSG.c&,sd'gn=qRX)PgDg#s3b4qYGBFAy{JoE\pY`: ]p?{V܇*$jTj*^H Ꭹ#۱%"O1 7/=T9')5`$W'yE,tC" r)0"0cnO8b3,P \g BGߐ DkR(!H?`i0pנ][1 `Ԓ YP:`q6jǏ&o,Y+b$JLFM>|c}YҤ )Qbٲ%1e„n9X|ͻC~%KCH -eҖMiz2:Uysf̖?w_hͦQ>Ykn\mܹuo7E"LZD!…""HxbG;<bEA H!,<a ,w:(ڠbs&!V!t.Xp (@(V` 8`h#>^@"pa.fq",\F`'H``(!"J(bfG B,2.xz""1*J#B $)b&4ΒTZ*fszdp*(MIMEb**(?~%G$Ahy 2-N]-iBUYfAegTK&kiglJ 6R[VfY[-Ygh#v[n[pU RH` )xAD a( !>8&خ@vX`X@Q IH-` uP 0@ Zpv/|@~d В '(&pBfi xMJ+p$&i8(,.k'[l`ٍ2#*=DdK~83OJOY%?25ܐgLu|߾~zvq~ ƒ@`;"P3 . `A ,H@, " DŮЄAD0@B`% !ڣC!0; KΈ[$ -4Q`؄ qD`PpIK` G-lYBx8!H(QbHM|S\901䪡j\)9r)*uCd'P%SPPa؆far-bK0p/" F0w@q}DwgMFm_ UT @ DY~^qQJ!y-X#1P>1"X"P+"RZ`h2RERХK CMEZXr$iAJ1=As,:4S%$dX%;B$;7[+G1i 9s؂X" ؿF7hhRh hFRhF@"0.i.p9D.Gr,8+,@0]YIJ:R3ͩA X41%AچVhmp`T_N_h9μ-{3<#lJ O\IOɚdOO O5jfi hj!Piyjj{ϧF|;2J@ &ȿXE#->'Hҫq+rYXY0rܫ>*Ȃq8@M DCD'^0Vd%\_DTu.+8e#Q1K+FVS V _a|mPhF2E]r4ܴrx0)**y>K йػz=4^F`cFx^4:&5J[O4λ_S"%5`ϟBXPEVp_N_eTdUòdÚ>޻tOOdi`x`K[=2RWecJq1.a`ޖE`VkuF@1,)@|u+j!02Z1q:Sm7> FJ]/1ˋq#*YlSLI/9@c==hM٘%Ts;hD·,NhA&@ ڥ_Hd_mUTT=QPv4¾,VZsdkeih{6V6d3h j&j\6m5[_KH cHo!(_`MXa?f I@Ex'L*WƁ D tFkF #F >ۂ7>Z \(|ܫ,C!hWI8V%8m%; ?22m&*&55ӐPC<[AYniZZ_f_`ԱuVi&M;L We[F=\o`oaO) Y`@)`-64{%*y{F+AfAWH:@0".&V@گ*-8/Ep7Ђ -qca78TgXX90վ*r0s1.m%OQ%`@eI5We:ٟ_TidnK`];-A/oE?Zލ` `@tN? ^Dq^c#Hc`O@Y3Vb5z bIpeEȾV9}FWF ">c0b"0҂%030@o p><0510s\,Y;F:eG[e>s*tB,D9xELԥuL~G&LZ o7=GBKU5ϋ[[uy}yMJu[QG5Ę y0VayHuUu]P!c%aR \RA,_8H`h?VV^y7oWpx =qWRM3;xvCyh{8IO=OYwzT=_Td\uUO|iEZMﳵHdȣPzܠ\}UUȖj QyO cEopw9m5G*DhiWy.W~_ˑbWZEgp9$p4DjAG;S|}|C~,XВ%J"\P!D"Zڔ0ᤌ "#HjԈɔ&CKҴjۿ91''\t&R>+&mgE~ZJ*VN#rJbABbdž[ڴjײU;-UV4g]|ͻ7/^ /^Èر`q$l2h2= -XgӮm6ܺw 5&(GCtRB"eJRF11VPR &3hA{d~PW }%1dA4 !v5!QGci(VHyxJ*1"%RK)(38\ʼn/̘QTTՏK-АѤБRI&HYYE)Y\^쥎]dc;hYi٦ktɦj&7^F?ؠpi&[j`f(z)htڅ  0 5C*)4G t2Kd'xy`|}9y$A!۶ -G#G}aJa#$[.)jKۆK5ݴ#8`4S7UpM1dV.tՓ@xI.t%Z%!]rV\s饗/lu̗1 ͂\;,={#NefhÊjTehm?,J+Qw4aj-"]-L5 -4/Š$@Bl%TRI)Ⱦ'_5[FhL%DIjyK~ln.U."!F'zꫛt륯yw>L?2<6\/2"pR ^"C."Uŗ_$L1i14M$$ u*r<^WןIٳ?((buc. ڐ0 PP: E dpBO%0d) >C~%z@A"8)Ya]]"bD~cj XZX1)4"PDQhD/[njUXLӚS$|64@mjV/E)p*S UQ UU(jIjch OEU8hu,h WzU}=+{:vE]}Og7%{_rt4J1bU؞ + ~cXgұUDGmًF@Ńn^;XO5Ӝp'iO41!(BF>r["ͭLE jDiD#$Fd0 @3fZҚfn=k.͙UkC gs><$-LGcIf_TX{DG`6cZ TESG&G v) ` Cֳfb^M[L2A2-.Lpb!LR 8x+`d>P^+9<$0.|.Ds󠮏Ś@jq#PAn%8\5UܰqÏDY1D)*(A%zKQ-kƹquw[\N9ez6sĉ@.4W0 0hx2e,˂`߼Hz9N'^knq*DɝfGx% 9HV6ȴ]I&`c` +hCQkވ; %?I Ai^uInr>o\[Yζ䯂+DQ)M ń9@8P\ iBT T P( `˔JTKŔT-L^(j`nL F12!  lpw@p#2<3#A9Z_]#<Q`Yܣ" NQM5HGL] DA&Ό.A,bXTSG\G]HF%۩@VKdȊ-BA-QFiTUE8@U^8_6ס|a]H؟~ԛ饅Z!*DaGGQ  cրn94UqVxdmbeMщbBH*2iXFޮ^r* %ZBȀ \$7R h,jRh *8LF=&6!,PVHX%.Oo*T(bWDHD"!DhDVmjh΢ќ̍F́. x?kxksLP+Ȇ0?,E&"n!0âȆ E4gf$BƔN,Hm'q)ޙ{";B, 1h\ϵm]^ lm`{q&Q<Vcsbe%C㱼MSFDOKf$(j4 *"zB K:-.?LZmBmSi~"Fb4!r*lrFEQ#PdOzO~yNNP9))1g( 8 du\_󥌫F*qgj|#rdTZ\jd@V,Axn`(Sâ'c~Dv?7t}qSgIoXOS^P^FhK!&̮+ bl}ZLp$ f& 'Dm;L)%8qfflrlF-C5y'g2 &%ht[wu"ϻL@jL Z$clMר(f ?CZƼ@hFR3DM|Wkۆhlhy@%Zf߻:%Dc7tY]WqWuU6iBΚ-4^6UŴ'D{26HrML]uևM"Ǫ{E,k%S6 Bu*eOm0m(pTB9N_JJ$X}}=DK85'[4,\JGh0GŁ|Khz}?̃,D+0=Ѷ*ma]4D*C7&n{?JnR[-.?¿r߿'!<|Ӈ!B}ab4iT(JFt$cD5I~(!Ô}X#H˘/_$;E4)OC?:(ңF*e*CVJPVXv:k֝1,%KU׶diS\Zmw/q8n^} waŇSUvmUɎNL5S̛4wt):3ɫFZ_lٳiמ=-O7wŕǗ\s˟n]smw|xɗ7}z q~Ȑ'OX >x@K*4$Pꃏ' ,Ȩ J '2'?dJD)Jꩯ*,?-. 2 qkT k_ {˱Š<,bmG$L80ä*;~rH`bM7ސÎlnO=nwND]F}tro&j#5( P O6:#14УVr wR(B~*( { JD^9,ן))<԰ؤBڴƵe1Jy$Ǿvqpy2ǦKJm^._Τ73z^5]*N9le7|mw~av)Xb-x'8!^qҖ]~CB>,ҁ j!>hT,ڨS7@WUAd5WZoЩO$إJz)X1+eVF ƯG-v\ZҜsM%i%uKW$2xפd=W3 -4\SW?8=8:N9& 5]Uw_O=vNqX}]=@% o䇮BG= WzBZJE=vk:|l6^{?n{}INr"o?pnQ,ɋK̲%9|qJSzTJ)z@n9a#L UBP*hXCn>9aEP2TDYЈHĐ((4\{e-0I:$ lZ&=+mVXަeI1t`6y0I>J/$f1)E(Lk`yJKg㐂I^UZk>iGuDP8<%)aJQҕć*_VҖ)Ss&thP!},N%τHĆpirڄ2I{MPQњ۹頠TAٹ΁g>P ÎDJ*I5*D'ɋg+"u{,|@&𗭘K5ƋɖL jp8VAd%^JW`/$# uEOH;Y':9}3msgGЅV*ݤN*C$Ifb6HLHsBUT(>y165W_D~:*ɥJ.…#6cz ޑ櫻$I5vk$\8'npn`1C_Q&Cv?% <|J, j}r 1WEm mU?E-E»WME!d#6~  aiNKȿ$m NGc3bJEձRkU~^ˊVv--> {ڏVis7èԖoivL*6JoBN=s~7qŜ S@D3N& *W^ R0ߘޚVR*l2si-}Esq|Nȧ.2ɭE: uHI I(qvL% F2&)=) ێ̉3cM?ɍ?sY@)dނҊ]!wS7S~|nk‡k04n: 2/1~o>.sHH+g(qʊL(0 䑖7I>#?Tܢ?JP~@&rK$~j%qiTJVB'pǠCnPDZLNX$I&5IE/SDWoFR * Fhd.Ƌ$Հ $YDzt'#_4Iq2 2I)<*Sj =4UMUU.œKk tTh T*-!>Xf!1 Os4zTE^ Ь(Ys5PP%G(/S/ 1J(o)q/)so)mba&T4czfE(dX?Qr{s,(rN9Wm1U,[B-rdbp: 0/*՟|p2mj|С=싹 ű6϶6Tbed3L.TLLi붘hX#DŢre'CpTȂ?D&bK[6 Go ڏm^NjOZk Dd{0]5;P}7wmebS *+~+5S-M4'{\6q9d,e[ewe!,Dcq‚f뒞.oGh$0vTKt7nj^uS>3k+jT9(U6m?XCiyOxQ.sun_nIn"7^W, {,2ҹ&i͂0-PE[p /wggpw]g R[r.qm q_嫱pW2: lQUlҎaشp)Q#LKъtF*i'͉ŗpVXO.fǐ1\Q)bBO8m2uu0!0 aY0l̓Gu4Є +r|"@&5Xw3aUٙ94RsbǣTB $ v{UMYX| N4(ėd2^+tXc+‰Svd-mhm/KI"\s=#İ+_+Fk9۬JWZJmJ[:7g<9,1 Sw9s:#!:O1GYVBz',NrM:m0,"i3tK¡]Hto; &xB#7YKl2: yI԰Yۥ;Ӎ#['I;xUy34yyVٖC[Gt(!vK[gk$wۃIѶ[{www7Wۺ;˖6y[;ۼ[O[[Q+ћ۾54{4[ b ?S@#\'U¡T{@)?o*7[^ #^'+/3^7;?C^G<)>S^W[_c^gkos^w{^臞B^>=^꧞^뷞^Ǟ^מ^> ;MCE-1.608/images/02_Bank_Model_Chunking.gif0000644000076400007640000021304512511671246017234 0ustar mariomarioGIF89a"  $>+  /!:]   !0;#/.(012DG3123M|4KY5C6+6E3>( A_GbkHoI?JOK?1LNONssP^(QyS~T,V"Y,[o0[\][\`?c1"cdfdN:dOeeg?0gjthc#jsjxlhdljko on]opsqqy0stbvOBv`Kv}x[chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2012XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"H*\ȰÇ#JHŋ3jȱǏ CIIYɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴS1իXjʵV3bŠ+YhӪ]˶۷pʝKݻx˷߿ Lp]3uJ}fǐ#KLg⁋ٜxYFӨS^ͺװc˞M۸sͻ N%VZv{nU.uݵ{kl.xyqC Ͽ(,WGsagv5(^2 FGF(|ɓ2 3$h(,Wb#t3҈]2O>3;8N3X/kS'<“0ȌcQ9W^wM+j+,3LX"m ׆LX9R yvApkH2G|h{ V#r"߮rz>17ś▗#))nAܣ5a[HJZ4D70q`D>Y:qjfn%3 E!3HA:jXd$IAYe̴ F$3nzsTbu1kIz/ ;%yq80f,̘ І ܠ>9{3'A&ă:CGJҟAO|qQX;=Ewq%ͩNuRgRA.Ƣq=&6s=PD{`w%w\#V>Xy8z|Waxa~/ nprR~%yB)ЇyViXa@n`p^W XWpvx( +>g>``!`ErgXzaA p *GyCHi!vhhzJW|j`ы\7"#8 H eqfX08[%XGb 0b&XŬ C VxkGV@ވà.iEFxhQ* V1@)AvP*ɒ0p `^Ixy:I k(h A )x7(qIKH&i 6 c0eTpejbaeTpQe Wv]k٘ f9q4Ƃ OpU:5Jy:31gxƛ{vgWg޷!tp HnQof1A `y gsAm5ۈw㹊9h<'FA Lmhb ͉9h`~gМ6sYy98{!ڐF uz&k'y6 IǢ$u ya#kDփlIL 0 pm,yeji &( *נX8Is:h)WhbPȥɖ@ *לBjpwx_hۆ! jukȦuj9zuj{.z0o^'pu܄dZTh~LIyxx1<læaBj˄X7  D{?ƲZ|kȆ؉LHt"M%06H | !Dihɝ1ϙ &hՆf&9n*ٜ'{8{9;d(ѓ燡O z~ʩ} n1̀Vd kuˬ y,Ǭ6jJkhuP˱ t p|ϦzoX(RR)2IxٵpsW_qk_|9~~{ ȉ.\- XKd6M-,lKIx||DRmE,X:g靫 'm;rikiJlԙg 8o{૪ ϫ-m[i] = Ͻ8Ѵkr">-Ch9"w( n8~ h7< <{Iy*U˧qX;<Ɍ=כGv\wN̍[Z Kmښ*l}ȭd҉Uq<ْ s )GǴu;E{Hr;{(˧8qjb8AfLzsk?()ۂF5swލn<[ tq2ÔkԖ{wmXb*<4NS]Zm6>ڰe0j\Ƴq hgٗ7:9 8G2ssǴ{=Ѭ 鰘1,h n(| MWH@ gI鐎W}Wɉin5@~7lqⰜ7hznXX@~B~𷹾2ϝt%܀]-踼~c[:[j}oΚ[ v {İӪB`#>LNzߤOIh 7~^&粯΄Jh{޽mzݸ/T]/h` )ױgkm0ܸy~߮Q<N~N[W]VG jȬy=BL2B'R dՈvOw`Mwűᇇfq ƃNΝ )۰7/n_0OU Hh"k=N?,xClMgp =[tΪ>=~_؏mXM[ƫ*R>27k@!M" u%݈(w٨ias<>k .̈YAQF .d kp(^bƐN E,HL!36h(KHDŽ \8RɄ) tHP#O$RTYĠiZ0V:M(+*Lf`6` Y'IIӐI QdeNSBXCiV?oywTl{)LCmy.V[/b (2߆dfp('+R ;m[mܹ2珙= o7^³f5s⁓\tԥ_:չgxsΞ{ܺ:' ɂ/1;aB|za* "*!~RkM2BA,%B YS$qJE^( ŨH,HYI!P @RȪ.a=*(-܈D1 :i CDm|IG^Q j*ƨbcfoIo8C-dJV4lD40LtHdB$K5Tx 83nsα/n9GNW]cU$pYR3/ e<DaN) \O of/]V]DTfŲV]y%Z,ۆDal^0S6͡,wI}bFJZ(!bf3trEQBc?aN4A7MQ$5T0]tFߕlnBedz~3{kpq՜| ;6:نm^[l\1aMY|U qHk? p˃t bwʌȲ;QXO ΣǸv!d"ro{Jۜ!M h]xIK$"*mQOf@gFffiy/j|g}H'%z4O(itqJTU 9ށ\ܸuS6:l!XRp;5hjeB)2mt?E81d6RHI j"+Xxy0CBY>̐2<42Ál nrK37׭x3ޚeI8s'ljbO,Ah;_Mo #À:Ź=vs܅yLf{GH1οÆ&oB~0j׾58[zW<`W==|髭Xӧ>W}CL|w뀣x;^w^xx}{_Q9}/VЗ_0ç~}# sF[wxoqq`g_{Y?p{{&j @@67鰆?S?|0@ @ 0ި*1#3AgYAٓS?@At>㹳Z|n8B$<¬IAxA)B*AT;;gs,P$,q(Zi&|ʻ*|C8C°+͓+ <㼷@q@#D33q2&ћ=9DJDӣC0l/ky@&/saHC\Rlb<Ð$FdldLC[iFmwl\XCTD}PuPO؄GO*YJFxMPTLEXMt&yFPP\MZSQPluMVPbTMQ}1RF` RL=ΨɆP&mR -Q%'u<3@,=\Hs=qEdG:P\؛:XHM9}RQzV0%u&f@QFXRE}m#%-A5ORyFTMTAhnXQmRBT$y]J8USYEhBE::͓{<OBC,GtDaxQxSiSZYIaӍRCRYeUKыDq%^PU\eJQTCqդwWJeISpQKH5؃eVp{eIEX_ :SP, qHTH<#|hu\ppx"*chWh:ӬY̍C$UчlQWWE{%%҄Z QyX@GZ eZwZQJ0HZW[ѝZU [ zp0[OLGeZhtq :$gi9Жd7tPEWYET}KyZWAhmܲ}ڱO5]TuTeݨN]Cqݑk[FN %Fxx7}ƘcX|!3gFY{A]E]dpd ZE Ut|5ōlHRuPgSEn_٨^U=Z\.Gx$V_r$f<ýqŕ8+YoM7__SAC\ݦuM_]=M y`f==|m Fna#8v0^ۼ ɑuDcpQA cpchl\dF.ea6FI`5vPEhS_">b%&.5aHb$;F5C66>8ushHe-Ln^=?R~8\iH{Sa8^GcmmdhAhdU Veh;e^Fc}EAfFcHf]fe2~>_#4EcQ% Bt&:}VcE@;f5}'Ye<g[>e]VI\ZvEFV fl,[mqGX/2mkz Z5V͍Um$f~PAfgiTfjVZ{ 6jTg:4V`a=f]V-㫶C =DtCve hhZ6< j6jTJhhQ΍~j Ma&>D5jn\ ܋;5wlGxnPaюjW dhhg(=lXlndFxgNltdjx=g1(DcM{鸎ZMhfy\if5mk.ڠvF0<)fd VtH-.nQ6:^m0mn뿨p^m k0j/&َ` 6@0\=YGnq <.f&ER.5r$gRVoQ D(r&"A8qUr/_Znզ#\.U) -G.RMmQ54?nQC808W0_5REsm&ss̅rF)~Er`\q1qQ s6{;5 RUWV%迈4MXVuV_uk$~rUVW9EvYTwQC s^9uY*}'sRgW6Ms>79vAV*eRAHvh'w9xvto¦ XwzЅwP&ilX$Hqu7WpAtc@:Xy;s(5Xy:hcCc_y5uwW+򃯈V3uXo3X^,^Vy9`]uT \W99\H({?*i99_joG{g6zgRFl{J{g?p5NvZhahg_BѯFyTPuWO3x ogyzoo&U{tGWݰ7<]o3wxo}:JRoU~o~H|@sz;ݯ~Տj/~FOdE,8p: 霱c … >Hۿ7r#Ȑ"72S.̱\ҥ9qWsMnϐ f<,c }9hQC%l$ԨR2sAHj$Q +*\XP=PX#•[QhB;WF`F>~\ns$ҙZ \80⊫V(& y zˈGd$taAs)dfYVk4NPwP3pIB_&sduۂ v(tQES77 .<R^SnʔSj2vj1Al!*0n%"F'%w*$J7΅G'qr aCZec*gAƬm V)}t,V$t`nr/:;Q7\nBmzP!Sf{׶ėb='>"n\iר-GUhJ+AqJ5t8óN<,94}sH+MKY+Ⱥpƶ^.a,sTArm BD:npE0F&aYaQgi8CF0] A4ah"Gh2pᇇy1A">pxDkjv@06YHm<ʫN;#wLCncɯ58,l8A9Ԩ43M)QC,!'?P~#P'CCP;7d0eR G28 RA@o xtAOj+LaW+^p 1D;B"( q8pJ $VIWY` @ n W*VD:= ` hMX#T&n-`/2A (!# k4=7.\"#x!]Lb;ܤ'5g<@ -60H0fiD, OPjC| Y$tH&I -)^sfAő>`,'<ý%:0bkvX)j9/Tюy7+\1ǘSd'㱨F'3qg"L$B1ɠ25)MIPR,削YHm)5!yĭ[B \iNdq.ݧvZ#ߒ&/].GڄMEөxePLFPezU Y8A T_&Eݏ0pQ@q+ޚm::FfPX7w׺˛FV%\}+ A`ց]&DJp)pD-sdi" s Rcɦ= n\okmD7P\T^C9F m̎X]A:Rћi}f\d91(X 4fl(FPhE(Mo֦ҡVĂiAeHX$v[*&LT^}, SraȔn8ϱUV>UMWh [Z JP<% (D C)Hk)<[Y&i(orD-L{Z}e%)h^QVjE,jDeя1%e}%$B2L!!7)ك8B\rؤ#ݠ1B}⊕( *Ħ[jDja&" jn+$VJD,f@yB*H  r+YHM BĹB QnqkQFb](y5'b(C9X*. <%=p2ĭ2On=jGh" H4\콢,ZIHDn~ ,2!ث~+H(Oz^H:AѢ2:Z`6Bp'DS6e->J;,lS]8؃ʃǾyBC*# 2B){a2^ 6.(+TCl*&HCE*-NB#4y³O&+މ*!..q<Ƭ(GX-B:oY6&mj/Ffq-J)jNa5J<ҪtT.>gέ̦j&hJmR²n!q&߮;Ef>0j 3"8P>> Ck֦n~eˮ 0 k. _&ܰ"` po>F 0?q^Ky?pWJE1/F S\1$I3|~(+FdFްojQqW""ߋgPH"k M^M4NKv81(k2)zNE#^2rwro++e^|,;X-K)\n5ȯChr5g;Pxߊ(rs"KR9T@C:RS|TBT=3ˎ$a8FE >_rr4</A(P-03/tfR4+dR3hE_gxG@z`-!SfBO9W.]u~2uԴu4sO32¥FE (Qt3g4o81:3TgV/,tmmnCOE,` ;'ag%H> (̵qfC/:/n\*uaڴ9rsHiVRiL 5DaYF#%d#g+Wf_Y|B[3'4[%O1 ]=SG-XjB H0%v06\i#7bm+I4v3,RVF1?eiV~J<%YiD7Az_nǶDpfi JLel7P YHwm*%6T07~`rtO%_mO0t~RbyMXBB LFJ:ZĂ!4, J6DN A|D+hݗc;V#E)Ed[K818fqb{gнӾ/H,=ӳ$??м@C~kuӾWzuw{;X;',gK->Bk p?Vx3WG6̿sJ>0 4x_(~ k*M(Vx"$A|X5cذB#I4y+1uꈙ._xS&M0 v f̙5%ҝJu6y3gPvk:_?sȊODMJaDi#E|,j,d;w,@J[߁ǒ9vhG1#ɈI0<'v(PЩI7f5s ٍhY#~rF6̛[N{vkMj-&]W:Te<ן3\ @(pB? cp ,AaDF $K, ϱl˱F,DEt2(Ѭf~au4q튏!l3O7$qDGYtFlÍHL2K&ʅ-0o,QI1M~:7dQIE4oJ"D2Q. H)/TS1,T|FN/ԜLW aԜwCIu k,Cr` V0_N2}lf56HɆ\f}P)3,`j3 Lj6\5ks%NBZwmwtI4?qԜT1UfPG]Ƨa>aAxaPEۖE/e]h*M_ivg\y5Mc,Ϥ]W2*^`).\jix噢F%UAp1Ɯ ~euf\婍46fpHmF|٠qnj Z-7Ong$U\Kn]?[Et{#w¿: M^/sc^]6s4g꾤ޝ{5GXc3XTg0/_dK.Wx^ྙ>fӛ<)N{kM/^re,\7TUT^)sBbjZg ؍P!deg`<Ep#`W 9fPfaW<EW,< ~EhB`>-H5ġ61,BbacCc.㢤~gHN3L(XL"?ydrOJ7׌aF+-CYÉTB*@sπ Oiʩ<%S*7T R$ڀ1YLc(Lg>є4N &)MoR38i V| )Nd"&"eS|&!jQqh2@A dS`@K 4'C zBH'Ǯq F7i,ƱA7h:6WmF,p APZTIURT>QTZU^YVK ! gH (_VFXԍ,Ƶt8K1 1UbX>d)[Y^f9Y~hC[eȂ` _Za@!R&iTiJQ7VupJ6ԯ:+2bQX>ѕt[]^sű tNFlMcJ ˔*in^%NAStl \`-W7ڍuLp^oaY_͗F)VYbjW?ٸ 3(@ҶAM߽ȵwȌaQ\e+ʈLhYЬd1yM3_W~;d^ygȊiG8B4~l Wѐ..&Bt柝iOԡ0eĖĥI$p9ʱ6F9崨]lcl1\ P6+\$TCC4a'VLjfZc.k ,+N9.}5Ur( fpkݦ!lqwXDapP][+zkoq琤1(pc1Y}>Dkyho^߼>ڈ˳p+(UPepъ}"8 ; eEø÷4o-{й: qvxpvqow7 ~W?6]E0?Y=o:gJW]S%\w8ϟ+Do9f]׾pBL8!DfH#pSۨAXGl|# /p'03ې@~Gϸ(N,~/.&*aLFC~O@ 20 p.~n:pRC@ ʁBq1l@n1X`D pz(2D 6@('C` \>>o# ա!Q@p. }&A"Eb'\'O#C q!@*3&!A m'°@# '-1/q F! L >qD)\V6oP6w6"+].%ah0c !&`   2~) `@<`=@r\ @@>/?A<:R\@@@ ; !9C;:/s>),!2 ;+$ϳaaCS`RFq(9"`I >"=Ypa AIo= a 9 `3 @@3JE:lTK`,1KkFSLH;QL `M(qR/ڠM $@5r \8 j:@S~S.4 $@!S?] E@T@1 Ё +RuC-.aCsUVyUa 'a] `[E)`/p[~AQ@IUŕH3 zzA^/TT ԁ)Ea4; 5da&$IO6 @\D0 ]r$S FO6;R0oVonW(o& bnN@ /cR z;m,4w:R+p\> Z`2S(@ y2@Yo RvaN Q0a1O p` 3b5 a|#4 r\Rc9&&AAv֠H*׀W`A" ؒH ,. LHH)@cnOK`v]Q `-GV'v^ Xn0~0 WnZ(0TG5EDҀ9w^˰!^T8W//5TLL`u[7Z?WvAJ0"Y5T2bV@^5aK0@{b!t|UKY pd]k  @&`jQ$F s0mT@QpmrP .R ك xQb y_Px{|&N lΠ ڼ6ʉ- T؋wEt8/zX$Zp?8&]gp(@} )q9L_@r+yzRN/lCmM̰FOZabsJ6 h{qc~yLz10[vWz9&w 3 @jФVr `R׬ ~0 >'Xayrw B;EHXOv=8#AE vKAG0\AZ1WNYuQ@{yJZYyt*jN嵨9/fj z!WA{с(J{ 78_KQi8 zAZaС աe??m4&{A%i  \z!!Y[ $V`$6 j؍ELJ-ň8ȋx9uX;ps#TI]@A 3*xCZO| tYZi:>s2W`6c)OIKj3X Y(͔R{{4A'4S`PYIѠ xَZ tˡ|E yK =0H /2@bǃ[3{K樖>5W.79#FG [>} PqB3n!ȃR3u05qSH+A`]B+`\ vW׮%t S ^O$yuþD?A{) )G=b/tR=@\qA Ɨ``ZPm{P{/B :/~3:C6 J>`o6d`@{. `1$a߶SlO!l>Oq 0$pa6@kR2֭\z 6ر=/ڵlۺ} .ft._xwo߿XaŠ#.8Ò?{3gz -aȚ>:5Y@ը;vl6 qA)1*)7Q|D#BE5l 4`߯?JDSEV5h#FJFr1zڧh#Υ{kN\J&`99 QPBDmD (d!S "vw$OMn+-XȘ؅c1ƦxmuytsH֖3 @ zЄ,K;h`bn} L\yvD0B!P^Nۋ/-jQ1Tz/q Y:4>PӺԶ%jM*&#;a{[h8س>g>S;ԷȄ% Ko;әq;zp3^ku;:Xo~ky:מ&oBoic^Y`BF|5,ፆ&ts\Їv0:0r@bEcF"P?zvιM6hd+wrM?ϼ;%\ uFݮ|%aSxEc+롑Fؓ~MI6`L$hpؙb">8 ߢӸm:^.S}{ Mh[blgt1yWm{v߽h3EctÌg%z(ݍfoF߽++_g=,M+E~ ?+Sqݣzg}E}}Ezz"~>G~w~.'Zp6HvftgD\|Dw|Fqb(Ԁ97Rb}h15g~'`J&a F@8aAaae2IyFsm'} 7r-8+/vH+3(5)Z8r9;(vW[Lr\yF|wO()Q%U}WDfq\8)^Ht1}GAKIdželZZZ)FՄR‡}o>'=ssg4p8)6{GqGh uPu&b ٗi!X4i͘jSX4Ȍ[i ` x Јi()hri c؈Ȏf(p؋Ht8[7uzŐy吩WLf SpV l?w&7 Y(jH+ڈioj8V YHPX zpHh@hŀKP"V Hpv qpUPh?_Е&BfH@sIIjj)[x Nui6V W`I;@@h!#h;)n4)01ٖT0]\ m8Y6@BF @Y:0IhQ9)SIh0p lIE0GٚYvዘ ph:iz[ qYxBhv ~`9ڏfP0@ki*9+Yh@)j2ًZHx R` :@ hIp6p@ɝ0P R)F ? }8B5)gG09Ƌnm( &~ͦȦ|kZZZ(h0 ;`hpg~PXh{ʙZ; m Yhl@eg}w ?`Iਁ梠̐0Г4g;JJף6 h`PF u֤h0HR jxJJkBYn8qH q"P= Zg9{up @) HpOI֠{& uP *hPio P0Pb =`_p P Kœ ] :fPZ_fx fp(֥ }Y?8FXa;i G趬djpf KZj砪vij絠9X阑 ۡ- R+hɡIvz+ fJEt{i<Ƞ ;8 hw Rڳt{ ښjXF ['\;g} g+ (`㪽bْwK~q` 髾JL\꛾iᰀi@kV*h #h<ٹ Yv,p&~xƺ wp lz  fBʵ,yk@ \[XE`h<`(ql߻Yˠ ( F ~׿l\8hp (W iԻ,h(`]u̎̌) ZmўAͅˠqY`; &lZ( p0+ n̹ ŀ i8hzn_ɭNL~ނ%"h ?  .gpP4*gf=(Xm&k+c` {]4?QZ`!BPhkf 'P#:6>$z~Z*qM/w1hMY齟{` /0 U @`҃'@" ,O_'bW\>dp_Ie%  W/ U, yuo(pO pXL@ D( BcP`%Ę-Su!%Kx)KLDxjEbVRv ΂lNC<915Df2ŚUV1exegYjў-mq&Y9F!wt:=TG+XĩͧV\қ[oz}k]2,P={/>q;\ Gd8P̌>d״;Hc`HDD jX4Tt*; :$$2 :Ejj! r!VnIf*EJdE)N1L kJ3 T,6CMi.%F!F5ӄ͸Qc th`  h =.FLM9EH)oy%S+ꩡGjb^z::qƚ5TCQ0NT6&W=%b)i`.fOQ׭IT;rgpuW z"c =[o7rE<̀@A+0 Ā ~!3Q8C!@E旋tWSOqȹYag)L7\dG) .1&Jh6D%0Ƭar ,y)d-2:a}aĩM> T>@"$6q^ѸA`#F"BPaʷ. 08&p%x&^x :юw#g 7c9;b VHB623Z88ё#&^1& maɢs &mH}D*2xI:|%,1 ҍaT,(*S@%A8͌Fv (AsH0FF)Qy0C5g<9OzCm/#cӟ=HhE$.8f}N7t+AC.ࡹIa8{*sYˊ#Bj!Ʉ=hi+#d )+hk&Ê 0'22<(p,tsl Ҁfhk$\kXLNÎ(Y3 ^@ ,<*$,+4A*#"sA%:"aT`ca`Z8?=X/1BhI ʊ`B*- hX@0|1#CCSy hqæù> ْc2i s)0&q@htQ?m )@#+4 x L DEk5EIUGE:^9 /;Ae<s@m؆Q@FVX$ $V(+`BG)QY#tBxy̑${B}dW<2%2#ńdH 1Ђ,HS9>p3!Pt-)P:00$()$Lr4G%Ivw d~ HH'!HoHKPSH(`|`4)݁*8cЃPT(PQ(σ+Q5RHTpU$ ,Z;hb%aM 5(b-8c`$ -MSŒ؅8U$ Up.R#Fsu&z1 ؂m-0uA؂!`4 ALD"(0+? 'vX86Ё$8xTxXf )"H$eN%zLX?8[6"]MVVYCZ"TD`8`U LO,ٙ]dmlOV8<5Z"`Zk@%Ոcpjݎъ,J9\ܥ hM0!Az0Q0(4eP :PV8ڊ%`%lY%HN(ٕ̖,8eRYIT0/]Z]h[EW-ٿ-cYٍބUD1 D0[ QTP!MS]5W聣ۃ@RXlu@)5pM͆$,x\:d:l\+*Xn^o.e۔,>R?d`h44 Q\%FϒJ B. hxbcIk ΈȆY?ni}ii\6Ks) X^ H^I @DW-`j}<98gfjfN=EB &f[(`E. d$ƈ4حXJ`O&v_,lVn0HoŮŸYd[쐦N],X`(|ndhkm4Mӿ:a<}\] m Ώ]&XN$ԖYnψ-„i 5gڡ}L3vnVncY0'dEXbHWg# G@pO1+K:3}"!,8 (88qfh7qr^hl%=rV+[6]OQME!^05Ǣ]f^sV6lf0c*Ů@_Fc],~5;X NQi*i716Iކ\~4P8Shihg,NvA^1wvZ8h`vo٘sCEp]qgMod?qzyVtuE[m߆!!H{6u{¤x8nLj&(Ԅpbf/^mMrdNF3Nlvoih>ڊfpl.Gk&l>%Gh`q]e@?߹m}PO1P\Džxx^mMV؂w28qJnrXGfZNȢf"LxԿ1t(q?fD 9)ֆ.WH˜"K_6K%3dKXt`6LaS(#"SDډV'OR:֥/^evLUPu5XĉmVL`%0AÉڹ01LV`#N01bd1o2Ilٜ8qۆ|@lm ]z5լ2Hu3Vȭ{7޾5إ:2s'XXX(>KyXi̹1E$_f )BDlf.tKS$a4K_}e}eR]\H1p|Q_VLqXWcF*HC=KoyŌ\~0PGy~&Xv!$L9Y<`fL9R70[dPjIf[Ɗ'o+[ i%WuYu v~D8 {1- }~4-(BW-xX(B|sz~U0Vu ڇ)`YbL>eI:eR[lC,)`naG-L y!6i”n*Q~}p}:aZ<׬%حW+WKr&Ҳ*Uą[3\LvEasf3.hHp.+ꖉ)Ę !$q+۬X0?E0\S0D-U^9|J?2~)1%q}Pȇ7B>Kt ~[(8B2?DsדKX9'N>Shy9[.1tkhHt c>MA\m5B"P~׎$D̤K <-{pxΔSorM_E/qC.-wm9>O?f[+ hN5H$vrZꥻ4-~^x\A="{HVy%{?fUP|(AJk}k_E0(AkOgϸ%c  jFMm(ZQQ0 @'M(!L(8 6ć1{ S0oQh%poDJx66q!D62);(MJ`"iQ(~`B&]~Л\ pJ]pa(84CaGQw^d|# (l2u8Ntm朕LA:mPe'c&ueb4Ё\PLS|;-8&ǽS !9좚K~ f{c ;3^0U6HpJ"'ԙNˬgQ]Ԙ6Olb" 2 z&mH4P %@4MH6Xsc Shn\ sm"r }I*fAL,^F& ^fg F_)IWʤd+Y%lLh$`1 DцI` !a(4]Հ*,^a#u\.Ra…2! &K/rِL;h[*"Wpq{3 Q2|܃LC`hj |Ò(K3 4aHe +{P5/!XW jl!F*2/Ao ]HCӮ6LxX/w o- 1ܴY[=;VJv]>L&(LOT>/v0g47UMS`y^?1bt\edPzIKpyEC UarL|w}.aصX! ǀN-iyUʆI.WСs\>@9wƓLWi 7]!ܥO|2h  T tWdb,0q z",֍Y`U2#8_]$އ!tDn0V/o)8;fqW86pᵯ3-g@#]Pٰ` a P Pp;D׻=x VfP:j_ڊe!nw4bq=\?oS; >DEmml=wODm5@_N"4LӴNJA]hGlM:p;_u݋K 83~ẂO.Mc~ +.|n+!MSBmyc5n=QYeIn.IM;l:Cf10dkxYI ܟqAToKʔAH`6a`E0"avyB `EhCb40!f!N!\4]*Z^!\T!3aM R%:e`<;T;i1`l@ 1d!`b&j&rb'B"W4%')~"y&+*&RE1dB)b." b0]?b0b(ᦝ."#*W+>)qeMV7N>Q['qY  4b .GƸA1C*P!B-E̥ٛdq+.4)0ߕ]ݱ=\q(dŝaB"%%1-%< HMIbuΜ_}}u +dh,b; Fa=BYZ&!F*pHQ"LdaZG$Mb dZ)A&d$.B,5\Wq6ܜMK ;Be 2UU8{e[2KC pW*Ot5ʴ[&F&\(%$ !WZG&B˴4TZŒ Ƃp,zd*&׺*N7j  #p0"6̶-@F2OҚNj̡HYP>R@tYj̠+AOW鬭- #+ I-.+bDDwUi:ZJvh|ۄ6dVfd^9<ꮁ:]ҭ + &ʥP -X$En/sY\8m/b$0MSHj_ÑZ`Bɩfw [v~g0 KlٞJΙ7.ݚnX\ATگW.Fȏr-xD 2M( ]1 D$AE;;[rzJ&D- h!d1UDn~nJk@ _ӝ,s .-k1M̂ 2!!KAlB'E,`4a@RWs_ DP1QO"Z1Aw2FӄBAZZ@JH$ 5?_E3QĈ K4"Pl0JU1N@A'J.<%;_ @Pq}v7i{eq#L4ڦn4NYh +OӮhOc43 ,+tA1T2FLRT1B|Rsx\E`HH4YEATUȵ,dy@AO4SIa}Twp<2s@dPZbr7V3C+hN/ɶN2ĴLo2m;X-{n')r'(38ASSS1)H%WlBUq`dAu88E0 4A^DW!;3eyuׁ* 7*6,I-73^D,_́SD CC;x-dAEscFc;G72z\Ɏʲ6.lV1 kղ-㲚ĤAxBq1a.w0K/sDW5pF@|5E΅HLĂA.yr!hv#DyZdFEBN/Dh"78+г Gķgp43C]#4tG{xiriWrߪl l2@xB!@X;V.ʣTS &4Bʫ)6'D,lDB4{Xؼ˂/.M8{hE;3W9F,.E'xDr\B_ņŹD!@5|}oG[JD~q'D"@@8P?&TaCҥ˔$eĨq>fRc5'QTr VnP.MIÂ:-bQGbiSiZ 'f] QjlV.Ȯ$6+,h^ vi>l1cC*]LMcYX(0t<~$/C^2˶6gE_fva>if`23?0ЅEL¦I94ح 7D2SЧcb֛{)Z/_"i$>$+t [?@\BN!4M¥viC#8[6cKC8tRP4MB`,p/2+^sSa&g3" IH=4ӠMJӨdJؚu7coN/KR96M#*2JasӃ"0ǣ?~fas*>CCT=I @ ENO~1muչC* •g^ 2,"{Y׈UF2l*e̮YKdz75q[#ߥ>̉."VwUW\"|_j9 m.]!/'#$1Hx8=ߜPWक़\8 J@,mA($hR1#4S)qK1kյbA.VrMlp⎧.7I X+#)]'BKUm\tZ$pbһ7 ?j& 9szEv`#t5#"<>#yLnD(da<6 \PI6@?ݤPKy@( ``6'gA ZYUv`}%*H*r:Qވ&5SM"gQmQji)"f8'*VQ+2E@ rGNBp.l"Gr:l+qKZ, ^I D("f.N.%t6>ky; & -RAcƃ<+wҁN\5Gk#a&U0P,tR(,pQNm(d`YO{zŴ3g[ ӏ+Aw"r"f1I̢}$[1~`)Q?XKq/D!O2CpaLJBKAqJӄzl(:SNG'*&42Tr*@kIa4(H <,P!2hMrSsOTc .DQԽ.( SkUN {#g>fC~d}IJUDZe]ףu| Og أ2a!X(5cy dJ-wUjWۮwk ,!`'CV D*ɥҩ"dUn[:_)\= rܣ0- to)Rֵ/fQuN(7pPB ,! T:Y(uB Ӷow e(fOi<`F@`08Ȑ[)S؞p IYSˢۦ')wq ?s&PLtG R2qh#^)H{zё/TrςAd ! đu<ī>%J݀VV<@ȜOJJm KѬ>J*C[H^f 4C\$;vR1i3MEANTBϭ.4mBFu@ΘZ%2 b OX(n5#m` N 'N)R¼Eo{f2 1r\b{5uwf`XE( lny`8t֔c'Kd spFKJݙ{ mLzL3AsAй1OttG-pDC<˜7wq'X!{7}rYhAvےۥw i&1+ޑc#8퀒쟸w10q w1%1"l,yc_!SLE7).1c"ڏ.,.s;/;LOJP:X ^BNB N$ʮa`Rh@(A UpY]{v\mp/2 =+ʊ փ!uBު^!1%q)-Pp #̡PTG† 6!`%6%imq1uLXoV6, pBqnbrOy!R mg%h1q/)̢.nAlh?^XAFr_92 r h-jMPΆH F!A^b'*'d M$Q2% ! !<&I \!Fa!1#)_B8r?R cpzaTr))DNȱ*1&6GW %@R%4 !AA!)r..2 RUϣ|Q/- +m w/Θ^d%HR.R)r393 29#R ~4UA1Mf',x2(St27{<38s8q𳎓cذ^d^Zts717c83=@I=C#P<R 4GS\.a1R%`;qS7zsA{A!4B)Ra?hB0_Bv(1^ aE]Ea4FetFiFD2woD!X4,pjH4ItIII4JtJJJ4KtKt+˨LCgCW2r?~R':;` OtOOOO@ PaQ 5R%uR)R-R15S5uS9S=SA5TEuTIT)XR'[2!aR!4%"y2!R1W uXXX5Y5 2dzUZ UY5[u[[[5\u\ɵ\\5]u]5[ vV5&Y^5VQ"^muvrbnWU\ T7uZ! `%vb)b-b16c5vc9X ^K*GL/_dM9(y` c5 a6bq6hvhhh[?{BiaP:iRD9P<ΈO(!i@ av mm6nvn`i==4a So[cΚ-Ul6tQӀmr-r17s5Xsp9stݐ_CtSBNo6i  Z7w7xwx%sm oKo7oA %h(opBfW'Z1ko7Y 7|w|ɷ|[`zxٷ}}nA(4C~W>X[ uI >&V> >@AV []o` \;aU[u8X Af``fV(`enFy/M/v ౷  @ ]UG0 [n`]CU `{Aմ雴u f;`e:@}5yۭ}[x -$Hd [@@ձg[%=Xr;uU˕&"es'M6ȷ!%@_\1]x 2 ` z+IXIË{ݜX`:đΉUύΩY Fυ :XXXS|[\Y+]+Ņҹu҅Ʒл ]Xm[/ Յǡra~zsDqBu@o46抩 \+LA v"C2.Мb! T70 qI"  <0;௏1Л N t\Xq<? Xo=Ù>`=# X d{X2o@݅u1@=^^O|X#~@][M]X7]`O Ɓ UXG^';^N5 3]o SoX#57ޛ v{Ataeؓ)vBv8a 2a@'6=Q=""`!U'B_X[U'n<@@?K>A|Ug \@B?@uGf-C# HUtH͡%h?@urT_»??{ x@^&"Yt*+a)XFUWSaK\*j 3:aErѣKJi!zRaV*MD2JPIrh)G Ip zQAq I0 A* u p{P1p z br  wʚ~ s p 61W wnЪ:1p *Z} j*;jdI)x<8m9o髙 z n 06q : mp~ 6 { j { j: { K!{0(+*AI89IkɋoڍS+zJzб ZjO{Pz9zЭq z+a+*&V{0럇 y%{˵p p@nq}f Rc0 kcڙyY )y pN{ %˷]{` 9 U n{r }.{^ s+V{ j ;1| i럔; D`)Lۘa XۼW  V{y | y)B1, P"Њa۳? ? G{Fôx@1ǿWk6d p~1Pěs0A\`z@ | T|PjXiL쯔p 51>3 ʝPt)Ę pJN epe0ƒnk  ],{l;,{ zɓZu+[}|gڦjl\Ȍ^ :Kڌ܌|AΌ<%'M,ͻ*Yl h\Ŵ1|=HϋP-S|kM$M˅ .c2M4,;t"Wc Y8PX "Ly}5e_i, q; jdc =iqAd ioHKNV yP ~5>id hnށNH蠒}EN @T^WdtnZtua-u?>j@T>?5Ù6o݂qLpfiYdE: Y@?8W~WAFnKG~H DKJq00走0_5UP)oP *(0./.ۂQ XS`RGQr `f0P1 ,s*/gE a.h 0ODQ ~t mD0|^ٺ OR_pN df=gm9 lAs5v VDWڐsN;۽ݯ)6P]ooz9睠Np l0&sfp*_A .%-)/>P)~7*0B oOX q3?,bH!ʁlEӋ/^Vmm3B\H3 5W؀k8evjn8/WbyPv; `L$6O(OC꺡A@AB%bAKf( yC6~`ٮhśw`Q:B\:Y>va/\KE9ژ? s ,Nl4dns?X/ b~'H0- 6ts\ 781hfXC2?sC·6!hw<` [9H:Ѓ6 8 Ӳ-Љ|mɞnE;r"~FfC;A#<"s#:A".9xȻh%4YE c FuK6HcE! HvqJ|"^K8iQ%)Ry,DPh$XTm$}* ]HH+ (':chhqXEħ>|c @O~B ZO[&O|Mf8 _ %P& ݴy:RH,"||u·LE%e"=H&؁bɢd2Cc)LWP^gyb 2(zeRȈ"GxIUS!BIXi· 3fqe,D Bz"4E'i .+A?"0 EZDEnyaD6̌j4h-#Ȝj%׺݌3q+6yb@bSaOqF r)YpųIakbZVD0CN/R E&L$$P1jͪ IJX<}|hȫ"&JeEU ciHP8}E!,$ԧ$ .rx>TK1ڀ`Qn6&@ZִM2 [%Nf9l"jh8 H(-hͣϺE&]ZD}5㎴]=3-XY8"?'t" r%-1=KI%onFW֢qA,%iE/p# q)=՗$ȨAtKZ ihE(A²E&X{X",< y64%(ve(nBtqՖeP.+EQc>sIb":K-`(|[%6'p")]8e'8mj`, "Kr$mxUo5YHH b~* 1IX:B(>ò%` Bbw" hQ :ҟ>``7\^)X@ |YSިSXycr#,@2xPVqct1@aGoF$=Uo$UEp n1GDA.m{ާ4'KkB2kwIYwPyQUXQ[e-B DI0$q-~3D'^:";I>)3؊⠳r/ىbaC͹?Dڒ27fx6+>q]0"h?3d@3%'!!37K+:I% (%tB%#\ ܃).0H,$0;.4kb/C8|;<8h3..?Bc2I/é)YȞ;Cch :ZЂA/I9ZALCG ]DBC1AL.f먵&`JDȬIdb|]؃%؁3,*?#1dө[LY2+$yjXTzYqz{G|[]yBܫKȑ{Yh/)>jQH+!<\HÝ:9sHdTI,lHaHEP8ʟZsɇX:$4Y_DɠGqIF1YƖ|13ɧIH F!H$L'Bd)$<ҰB$ Gr>BKD? !I[8Mm̘K$\KֲVmV*l5-,^UW@;2iU[кTVĄ498a /Ѓs%`HT=/~uS P 7HYKXj4>SDKGUǔG}\Yl~GY\V[TXCחiL[XLٻ,O0heR?P֤`LpNXp> 7`p胭]N0h=1/P[-wlBUE-\WҘ>xZ,L[28T=$ZʅLZ}P̄R-s Nۤv ̮]]( =}][څR =\Z-7]TMII^eѨqΜ ?p\ ޅZэ 2`H8_9߽Ѝ6@^˼Ћ XMY ?ɼ=L [VLx_&LX؃e^ZfdNe=h# 2df? b9henfmlFeeg>oe=fY1l.[bfw]g&yfWNt kn|efLTddNh6P e e%hhhhhhhii.i>iNiE=&ӘOyK%iiijj.jnGK^^j.ͧM>bjjjhkjkj>k^kNk6벎kkkkkFkf~˿5&4$\>ݷ&o>ʮl˾ll=H{vlˎ2`nN&>@mՎm6bvmgɆmN+nm?Ҏ1H= mn@fnѾ4?0n&9l&6GeٙGM]o ț}l"=AFN\p^b>LYe\8fv>6L GHbF:J^a<[`)D=YW>el_Bĥ98qB!s]M1 nLR=߹\F1qߥ=_c߄e?\.`' &s  Z *UF'ᤎ!ii``7>f\OM3]LPr;%u; dMLj;(u%TR7UTWwa1mM3g=˃^hw xЪj75`טH]7Mx]Wo`Ȅ7o=ȋ}M,wN7xԃRx|X dKqQdGhyPтlPQmݥX =0Z;E;>a>0VDVPegqhkGTd1N_{uaiseMj\ϣ9:];(]>Up܃ TT{5ٜW{@mA-̻7zt־gҿ/ND?_eO'-R^qqn8%&EwWT}__G؟B}|^OAooYHdgq@o~>]'Jwf,h@Ld0lGۿ'Rh"ƊhHeGg"f,?@=*S F`j,NJ$%M2lJϖtlJl6k3X#Eg.q@:PEQ7Nu /00EUǰr:eiA,aC\CGjpa GϩU 3; WW †6!AFzU'0LP0MaUZDb18L}  (+wC Yš8|sA"QRm.x/3nNzԍm#Cw`#t/NRwObf q#DlCC}SÀå,Cf!Zmy,6kQ.JV?330)lS=0-!Nʜ46!D|N9jq)\- ؃ ֔a2K!~.fLBbo8ޠĊ3H-`/xN@SR1!s6ÁDPBT"$.QGt",҉_Թq!r'VCy7 CF'xwڙy>QI *zByd 8)zdG} VHu  -ӠA.HJR XdF-Hw8 R )L X`1`)yC\I̫WX4`M:8D<7 nOx6 <ٛ8T/"Y (2aJYWt!MA xk rQ|-f$A^ *?H./^yg,@|4ayK))E`O<_S>'HMԦȲMq W@ 5Avٔb|$ 'X@ B:+?LrY$IBf$hr$:5L\2dV&j@ @=GPH R87pCJ)t />`x,Аꐅ Ay^) 0 ~dFt8Hz̚їɃq`R=5'ʰ jT U-vV!RTL" 2ˌ=l]`.2q"d"Md0YG>dp8r"YL6r<#/9T~̢g8<. jSKB"d ya#HB5;zs^|m2 0"tvjv7(̐`Ìd$EWb P#_߆(I߃rmb 0֤HӮs0Lב+!~JVGIȟI⧬o4fPArO,fֳfh?x+)-}f1ӈ1b]xmqC;]43b+V1"5k678SE0fZ zDmUDtSXb鵲>dacש LMi5BUfH#ujMOu5H,)a[ n5 2{+v6Ђ`lp@-&ః]Icќ;Q7=N{*~xǟɾ{PP(&[h 2 8Ed5‹PED',A 42VqyqģLbQ")L-4 AA1_N  |&QWA@N*B&+̏SG H9ZMXATXA<>9E njP0›%׊BTDB^`DQX L0JNDc1b /Z`Rp^0!TLWp-!AmB"M|By1@ )Tc5~ Af4AyGuP1mLpA.\ <#$'\B5^<%yAģ<ң=J51 UC C!bHZ9OCC"$[~<V.Ї1G!RZ'8APSda3d0BX]-1b3Z/Z1#2XeXB2jNWP@cp^3 B7VDDJ'n]Ys "mi%?R°!IO6‚YU/D8U3p,cHZEX&r#:]]Adf/̂ QH hA S&!*gDK6x@o>nTxOJT駅]""%"r#uM`2$$q{J=^>5>9)t)|'Q'AB".\eB"[aҤLAPd&!$d*;!V!dAdU(Ui-$&Vå֧̬+AtD(^jbINXSX1]ZdYw`xjq[NgC~ ~t}T~pjj|({X}+{,kT+x6MYUJ!l&l.>ٖuYFĹt~}k|ȫ}++{yk|y+lu+,⬸ %ƚڇBڇɞʊʲˊ+뗖Fגi׎LSq{^,jg m|-{m&.m|4xLUl#ׯ)A50&Z!PAgA !,rCA- ,G&3>?34CGC@,E0`!$\̂3_< B2nڬ&&4Ov;kooUJ߳_&HXKEpR)A/TprCA@g)̂YI1AK5CQ'5FAIRAJ,L]wE^wG1BM6OrN)eeo-'9|!u 7,Q6&Twvnttvz_75M!,w|vxq{Al~,7_o8j7bX: oᚧW8_x;K'+A8xρ́HkEρ0ρ8QA3yy.7_8ssǸ[7qV?S/Ϲezr繞99Oqd۠9Oe:GO:W_:r 0cx ~hxg::٦_g7qkvegC:l%c2PokO5ϖ-O;WzF,:8s9w;3_;;e۱7ϛ3V'C{;z}3:ǽ;:ޟzwƻȏ24~60Ͼiu:${:>O{<ӷ,44@C6D@>_k7e?yyr˙ӌ̓e4^=uQ^ݻع>ƌn>3n׷9j hHz <0&23{0+0 P +C )PD5]]|s+b0} $3R+r#93%l'1crJ)\24g+ȔX030r$x8\n+K;R*tO<4GK.D]Ѹ #?(,L :ApCP=4TRGpDTC4Ys/NDZ72d!1MX8 hYq݆.vHX*j(b1>꫰:_M>nKRWagƂv=zZ_{.zw=d~V7\TDb%Q~gAC:Xb"잏iEٹZfNZ`s[\>5h&@%xR@ێhx8iWUQS-QUKMY&i5E^#,~xa…N!U"N1N@= JB|_1/?(y{R v.G L hPO^* C 3' eĐǀ`J,$9S1Lf Q f{+> +Mw;oԄO @%[JW8u1hPأhI-% D@`@p3@qNE$bK:C[V7moE[z֓C} s0%sYeC?<0Q&K tdBVDB5h .@gL@X`x+ V .0|Ա@n \tS@YR>@UmreP8&0jQMPŽ306pH\D.S9hDWvuiݜ0XX|@DF 4fX| 8ƪ0^ IX$C.A@}bؒ5£ sZ b!D$J8=J ¸7Kc!` +Ё求r48PPgغm!p-pcr_PBpXa$"oX&62wӘ|搉%]F YLb}~JoyJ1@Ef0&a`Z 6y'rqs|(  `(Ƀ wƅǕ2iX% f cV@]ˀ '<5k {`8MJaPJ͖/Y2pwٽnw{en ~C74 rս Ή +ckBh@4ا#sLd<\0Z rmtd0uőql[sѮX),ݶF䘎-<)#oS؉Z=eSn@dBQoF^4^8edf؃ /l0*g"&3 Ipi8^@.bsH`'ц,v1BE* 0oJcN? b`( PtQ+q]nOs+st <}s>@~~D, GqL @.`.-B$@2 "l%LKL$8-`٘᱘ $BF TB\ kRo tjoViBG  m臄H }ȇP&A!&./o>bGH0H 3 JήRO戌 `A@l @qX6a^ k)P">NMAN^ If@`lLhP`bl$|1Pp1>ޯ"Ф 7(7,bX&0>"$X$~L QB`~퍔@O:%X$D  H* +@QB,!da91|k i ! >?>S =%֯zja)a:"LEj" B/2$#~z?;,bbOg`bA0>Ō3_MMF?k9m NԫB3:O+J'.Cchad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2013XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"QiH*\ȰÇ#JHŋ3jȱǏ CII5!Zɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴSթիXjʵV>SÊ;سhӪ]˶۷pʝKݻx˷߿ L.Ra[qclE^ ycʑ-WƌYx&G.x[ͺװc˞M۸sͻ Nȓ+_|6*#aSGuu]N=ޫ.;yuܱO}1|LuϿ("Ňɳ >AN!Jhu1#"$h(,Ȝ%u88#u5G#kLnfHޓܣ6S秒_n羚ߒV*ɚ:֧`ཫ yG/?m}-fc/ip7>wR;G]-ԭ)P˯8R}ZHc$+5Pd3Lΰ"6Aπ ]@Vp!? :G3լ8w@2_( AҨ&U *Z˱GYz*g|r݊>,p 8GcӀGG!q{\"B@&9ɑ*!Fz`Vֽ.vIRpล1ڡa ǸAҔ%X뙭{ 6Qn䈛h7]:{ѰD֨mKZrr%L @5KLabь|9O k"7y%>7x|氟ΤiJk6HŅQ%ђ}@}+j,lbsh юT xkK/ML/%yy|/dQJF+q BWL,3N7Ẍ -& 7 n#L WO 1-*-B1nܴ6J@8Dα- IuoHU C2NM#+'I3+_uC#X%8, G1t2Õ+s`"mN܌ѦК 7E8p< J`Yw&46ށrcEulZh&- Uz5䞚fi3lx5Ů6⭛vKm<pЛq|6Y8e~_ѫ6#=m뗈Ҳ3&ђ ITr) 7Yi#̱M=5Y hh[\ÿɥmCFEEW cX|lWR|4׭]tt 7(mx-.ʋ;y{eEٙLktCڹVoԻ=պ:'$b u0TH y&U+OdLddeg{\l &ok\LRh @2]o]jjmM-hR.9y]j 7˶;D˼lٴ̲|ٝm׭ `:VYFH vY7ݻ ]y}\WNV]j1Q?N >n~"NA?b 3%ō:QQ&V\Z l^`o#ʮϣ<]eo~hu i7PG=Μ nXY@cA<7?.nxyؔ~NkQŎeX}9}C%!sxg|ecq1sȲagG:I_ ;}o6.>i+_ou_. ݎ \AwIgLx;_O'k{ zQ@{,M%,RHB/bH bi%<1?OqIf wDʘ J v̈́)g7!D!M:#Qq"£҂ɭqJZm z V4Wj%2̟4?'EQN@Y82Pckv|yeԩڪIS>ΰvV[66m-\xȑ7wN8ʙ'r &LuBc{glrA,i^m|2і/9:"4, J1" o&0o,0!Zdʯ.C(kТI#?"D*0-v B"Ͽch (D +tL4Ԣ?#42܊5Clxȁ7SN<>sOA#tOCD8Q3o JN*'BH)O3#h*( Jh9Œ5) Z Iҙ($hJ@eɑeOpCOXuVY1 ƲrVTHh CTa:y襧磟~'G{g{fw#wHޟk ei×Z7MAtg[ܯt IQ0,b-$O[# вM29(HkXbUr9]d0<,"p ;Vh%v BB( B;`imAS4ǃtƼ14ioo洨qjL[#491hܩ_a TdY+B)РRv`xB&=prx" s+$RA[\t)HBRܕNR| ʏL>aMLnra'ȅ|aHG*0 1%e!*TrS-0[hDd1k6s<J7Sk-ŵJE[@œBPFXf\^k^CJ8uSF(@y4Q~ZHUF=;:[-h*BxBSf#-/|m|[wljk~ͮv=d?׽r~x'^_%p`ƵIzgguT'}MzV>^x\.x^vMyqt>miiVC}|ţicy>w]$>0Vo!_gϕX{܋_?Rk?@(ÓY+G @=aX0@_;@xX)˻¿@ =`6eЅG/AAAA B!B",B#C?C@ DADB,D?thCHGȰ)$*B6BI̳> 0 ļ{KG5|( VdW|EXEYEZE[E\E]E^E_E` FaF\dE P8EGxĞALliԆ.CNUShE6{XLnFq`xAUPxGq=P,5t8JKKK]HDCGQ* L3JBsIjpH?x.Iivhmd@ CA.L،MٜMJE23d 1.[(3bPNS=K@y5doIkiʓ{ A{CpUF 7OOOOOO PP-P=PMaJ!(LCۃ=pbpb. w6ز Qb(mSF;iN2TaXQ砻i$GDt{,0#@)؂44(+R,R-R.R/R0 S1S2-S3=S4MS5US*M-$@#(J FE. FP UPp#,TB5T@TeQΡ+;+$k<\1 KT-XhK:8*}6U[U\U]U^U_U` VaVb-Vc=VdMVe 7H8EPRW-&pB ֐B1֓xIQSc DX>+4F/KKXP uWU@<3]K^4IXRZ]V]XmX}XXf$^LP@8؃DP00)PT(ٓ5%Mw0Y0YH$@T)M JMW֛FK#kDo=Q; 4A3E`O^J$҉[[[[]-V!438YXp؃Ex(ڕ$T]QBEQD\Nɥ\ PߌZE]]0|0XZ=:Z4H[%GQ OmK) e]4HX^^-8]@3Gňp g%0ZENk ňE)l@x5Z/}M5RC=L~@3{&(E -^a ∍!7XE VIWVx`MXQ_E)q{/x`\ 1C8:h]>t ?LAx[E7{4hE[fX@0U(N"vX&eNNNdQ e b]e\f\peUdZ&VWFXf4J0`戦-]nmU.5cf bKM7T8mG?&56mKIc?~C3]hhU\X/ZՁRnߔF֤轅c Ni]U&4e  bmfZ&/KN+)-'Ixg._opVTB-g `h8tQ`wb̥jQe^\Bf8:%H83Edh(f Me6 ؀hl%"8lU͔feF@^l.V숵iZ&BKj lbHznb`mm^ΫfgyZTxU&Hv_&lj2VٖE XDS(NX5MZEa1yC]!x|,7n/vyEy{ N VQE2mgkL:23$E 0-fr6zSfh{hvl2]+l p zhpi ]MN&b&h+d"M{~hz{酿? /xfϦw u\ݍvM̧i\OZeǏ~o40E56foIodXH[w#O.2@4۷7*öh(Y&`#Qx $ ذŌlx ʊ ~ xـPD"rF3 LH"Qt'W@j2 sZP"E(@-Ja !\&<[ ˘guOG:eCM"Ȓ'K.#̚7kÐТG.m4ԪWn5زgU:c}wo޾{c&8uǝaS' ش…xԳog4` F ((UF>Eb5בg!jEq>2Y]&|[`z}fy'}ٚmƛx^WxL8Ì0̤cwJxwi:0!0Yf7u u9EZ`0WiE-Mpl>988Ԙmņl0kuhV"hK#1#&?)XVbCeM _RDPue`E&0 q BF LzLQH|f2*)2˯ܓO<ē=LLLra9\s:4N=+}=3-Sz$3Pi||6W N_aVY%E4KP4 TaNA4e$VRTcٶ @(`nFYIFn#1!7PdYF : "9P ldQ  Fdv|%;AN ͳ%gy}=k˃v)1:~qjo) 'HQ QTi@ya X2^ K(`qG, `N~G6Df9(0|EPA!Zd+D GH .ϵ,U.(-2(fxp!;dw6)gz(Ɩuo7Nw3>,0ZhsF9Age EA):"jHm5̈a2`W4@j$d )XagQ BU:+UILyW c(`L(&@]9ڀ^il/&;N2^h'⢈+-!l$&ƨus6eG8ēC;$<F~h.WBf~Վc ٿrk!fsǠ00bl- NZ} Uú$RlkdnJ>$9!TiA#- l*崥L\R1 m.ɷ{Z]627nXt{[z"C/ VuF{m,ej3 ӈ8 F FcVvM.02j&Έ [%2tbcza7'H5?og&N~9Ї4ݧD!T 5zP9`s('h4Az,&]rCҸ~3d Ѐ2w;LßUȝFwG{$̐XtG'c-X D ]{Yg߳h>U}^x>u>;; ܶ[m(3\o]'Ե~hX5=MQP`Vi;؈E,!zO-j6x靵uO!/g``sS8Ən-NGC_F*fآ9hfSJ 0-l@:׾ =S40bT2N:ԣ.ubT>M&PшLP{B# 9i]ƹ3]OrQ43M|ZQU)L,("/S<3s@Zߍ<<^_8t_Qaf (`,>$DFDNDC`!j603.r"0!@ԁh72>#< 5&?R&#AhdR=##Q%ѴɃ:UQ$mB⭇A/P[%\ƥ\`/A:_hd`?AF8Ԏ RiC /*E(%Qc"R" @$1BuU60C]`,h@[ "-b1%qq'5tB2ڂ!ujI&uC3^hfjdh|:cgdRfvCCx[!p8` 0H \M) Qm= ,|l -gp"'v¥r%܁\-x&Bd9 PAN?4  8H#5^5 4h?\Ahg@3&aL&9[E` hl)9 mr&@FBޥ~)b(ztdZW@щ#B ~B 2=$At]I6CJ=@QRڃ@gTv mfexs0`bi|}X!,z@p^)%<b l8d:2:b'#iC=`y>dBxM<D%lɜ ?&+iA:kk@kA#rqqVqT#56`mN3Pjȃ80ã zL+B+z+|΃4ͮ"7*<#,+ʦ"̾1,.+,,!*g ,kPcL8L&t*j$J`MSd1,Ŧ-CCq( H-B!? &宀T: zT&n,40A.B:) ?x" YGziujj~y d@d+j^3MȖJlF kCH*+?o캯4`'.J,o# T@>A,<$: `,0/>̃@/*,  N*pHo|f^ /cjwETj+. ,/,0.d1Dr4pz>!C;[mG G8{͖B8qtCij3<=e)@F/1" l@>70A4t-̃@n/A7,CH CL 04|rJg@ ) 'r$+H Ա,'> <;2I˴pT@.2B` uz.ذCCEo]Ɍa~(LUNC:/((Nb=cYyPhkB@ .V@B-$< ;B>A p;tL'HCBG/2F @HuGr''W:[on4mmO/7A W~<ܱ7|q39ܬ,Tr}'C>7L16ƫ#?K#5 ܬ#@s3֦ 3kͻSTmڲ|)'Vwxlhk w'y4;$Ah?p6pw:v3@s4_; ;0R;F+24 #jSAG9 A4<%P>5+nI/>A, 퓵,KS@LƖRVO  ^`'a!:\i0Btj3L 5V'Gf}ڎ6L.,.PyO!3OwیYLP@\Q> mxY-1mRr*%Rlq6<SJQTUNStL7TIa:`7N5 bZYXPb +u~lҁDx'yIqx]3V[uŜ WhJJ|E V$08ctUyd)xZZE~(uvhZf\1+hw'sܐuy^ɸʖwlbX"BC:_KFT?km T4x` rJFm:.{츽&lYMk_O‰56hm| feoooeƛiGgh,Uߜʽ9ac|'|tQ=s;[ƅwIH*?Io8܅w=-\[Z, _"ޫY˞$VbTT ?ا_)V$M 2hC>LH&l Mڐmj#U8&06̡pvh9ip >#nqK<0 %>Q"q)zO #0ØrE/^|G#\d>$肄Y+=g@DsTE Q( r!>ͯ pN!QF>!A06rlB,Ƭp ڸG>|Cd%j^{TjH[ҕe+k9LZ sD=n nIXM$SyMo~a@ޅa 5y40_ fi ;0FB^Hʯ{#9E^0O~şb1y^ >TJYmT%Ԧܰ+eaKwC3J@vd]:TóSΣVF0_ qҽcaB_ 1Z՗^(~~dk jҵ}\'p/YBjRZ7MoT/'6NRxcYin{Í89F5JU׾NZ1"Ӭb B|a(c'PkվP@u/E^QJ`n@Ez\*yJb*Ze*W4 a(Y꾬p W "|E)ؖol`?8NcnzɧJ0H P9 |PQBۨynP2MAG|^4\;n,L62Da k6T843",04 S\׾} %Vž 8<(.%DƯɨ4f0s"g7 ci)xN9G_dl}k0+l寪BN3>|-o>gm)|Bt,F Clۧms}gv39":ހ@PX PAMޠ` M.5M h$0<' 2NcO6:WlnRʏ&7o9o|o3h!Sha& A?pa Ad (  :.pp ͰM Ő ްM  UNA3Q+<+Ro8%,+00֯bWQ4 Qk 'Mp86a ˰@11;1'q p;xqQ!PqAQ1up. 7 Rk4z xג#-!_/f W1"_z"h$q 1-$IR r-!$-r%'C`A'Ir1%[ &a!|-''*y+(wr @ARX.R.3 7""U/r"ޘW>-&@r & .2"s(˰2 .$2C,_D2R-1'W3.\`3h.4 *3-&ـ%  .`3s(a6r1 -".#C`0A<3\bÞl=N0ͫ:ѿ䍽DQUAtO!?@g~1:7 4%T%BӠNpArAN$!!t4&=&SRD4psz2E3sAr04BBˠFpTCߠ %!D34AD;-330~'Jȋ1 0h:nMre/t@1R?j4I*%7M%M8DQ2*٤EEq;hFMzHM$4PeII#\\b~"Z`Vl`V$X1!P!=L)z/PRN+2Op"[ 5UeP%QBAU;C2R'R'31ESC?MDTIML$Q5]t;;#!S~/ah1J3V$:6Xy+Y5..Z lfiP[O"g"O MֵPەMC5;^%MR55_#S_rSU`Ir6;``VMV;[u1zUc~ze 2Vd9,p# n f,+bRZ㎧/r}Hb!Wx6\ost4Ba##miői#u!UlCjjj5kckw_`ۄl9lK58tmncV`wғ//Y׀~VdV:p`zWV }DHR|6JD`Z1JP!kxAR?}O9W&TaNI0xu uu9auv^e^Mr&wMu(6`l#mxG:0L!A!@BzbL4~ @/l`Ed%Hfe8vu!cW`E6d-*ǀBEPkO@cH>͐SC<P /yNc~jttW8;Ra?tvjvCcÆ_}WGBaj//}ڠP #~!¢/U!阓yAjPP R6~KG./. 2R*)U~̞OE8BiK҄DOءK$jSy'W;Z*__wC vxv50korfd"p$ #pi~@x~doaJ˝8}9|rY[lc񭂋plC9]Q-)Gk+iWO4_YwIGwX;za-v᭚5~hWM}뇳"p9*eXX~~FWe/'9YR?q>>kR*BV s=).&zM:69:8qK|  4gJl,>v8Y1AC6Jm0g/;~oAhA`e[~^o ]~bO@; Oe5F{'Wcz79Wp aFdJ.dN`HeV͕E\re\oy9&eYx^}T`B䓕`gzɧ" h`Q9Gwj"~R(**iNTJiy:ZgQ⤓*\ne9xj+j^뮷W}) ,< mNʦl`ʴnn n*&.,lH(΢-,JD$m!ۯ.j /lܪ5v^*qS|1{?1kLN_i`tw(r.\V*ls:5rLtt& ]t9WJ"IKN7}/HDWDWcBE-@6ؾĽ_0  Nx*^Ţ6C^>h89?KOoHɩ~NN{"~JoSN|S$+>| OoMO_Ԉ/?ck=*Ԅ/oi؞?N_4T(N7 a,ֱBЁX+CuGa pc$, OyO%$1 o4 g H2o&~?," }(Ј>, D&щ&') wXr \"8a wC܍)Ob@)P6%0 цM()j8u"hEN dnTH [ړԥ+MI[׹U10 -g0<|fkՇ'l9v!h8sz/~G۷fq}]+D% m^8&rIC8G 0d>k C vB @ 6pl&m6A 8m $2o7$q|$ez ȁ|w xwfwC +`K[#y1p /z$qF!0gDpzi<<0gd#{Uϐ|VD0WC @LPjd0VDWt.V58 ` P`"p"p ~=!@F J0ʐ 0 p' )@Zd(HHdIVKHe'SIV6/p`ɨˈ+3x,`a AuJD#PpTD`gpQ >xgP gdpMz J P A${Ywf `S>fȌ+cb* &B;d; Ut  `x@@q=P <&. ;VC]j]VYℕX]ATw8ei3՘iF(P O iI0DpuiIא Ki-ǐSMdo/hD\K 0j5|giYkIG%=~;a Pu~`0)2P@-6pi!~W0ɔ7<~'93ZW,WyWꉞW1W|5̘lЖmvOovo`avOG7x8hHi<0xSUBi+H7fsp "6{v py] +@@jGɌ TeI%@Yb0<? 1c06Y}=!>?\=7W (!e cyv՞q sRS@^mc/o`tB՟gzYa A0>p %IuprQp> &{ag  i1w rOp丙7tpXCɖ jiCɈ=|C8t*  `N`0)) bYcca0"$q  x mJ][ ] ۰ %]8Ǫ|QYOnfǰ#j3@ xFg3@Ěy9C?bg,ʄ"(0zxū#Rsl`ڌ|جt 5fc6pf L~: ;;6ƈpc Pc$R  `+ȦZMxdh븽ҋR6sik8Vis pqWaYMP .GiD[8kC"&0EF_FFDK{ae(g蹟,ѷmu۫@kZ{mk m;1۶)wZ|`|wTJ2`ѣ_2Kj@+[S=YK^kBʽ%\8ޫevdv+,v* dEVȅ<Ȇ|m Fu,Tc뷎*="@gH'J03gG.lvivn~ ApT딡DlNăA>BJ~-7>^Q^( Oon/0<ҀPF s?A F "Х\Jd꫞ ( ^B "I8s4>/|M@s?8  I "bp&/Z@}o b9Jeml6@c> ͑'˞@zPV ~ mƝDRq ĤK4IFfM( EE׶=}dSIU5MiV U&j(jVk^ %a^Y SH!/כ'O!8Bbc pbƍ?Y@l?1"i3Y4Тw?sD4aM,g=.gf5WM'$- Ay\V4irs"0/@Q \SXd9 b0p(9ֲ0*DY Ӈ2-iK\"ŧ:9TRѨ;U@CXEtKO)hI\ph3C` 7Ի ro\jL" Dek`Wt& (&z!9O)F!@Xfj"\#n$xJa&&,%OIQǦdcTU6iOA &7OΠ]W.XƺCA&"b3 HA-ɮ P(9YnȥLY|rE e!!g3dIۮx J%<J."0 $Oֶ`qc`Ji]e:Ќ5\" W/XAൂ[ yxrkd> eH\fݎ2 3@@v_fƷ+|+[`x853Atcx@g<8HmkS+Amw~-odQ"2?CXJ">FP ^%9Kd8ga9`C; qry D>17}i h ('9A `z%RsZ'7_M"VXdHhuu3^xsPeHCsޗaX.B/l /(qW׏mW 66{v6I |müD%A8IF@T@|I2ʆfX:i'\&s ʁ;=#!=X+A[x;=[-ǣ3,\hhnHASs,6T .ȣ&s8qK.1r`=5Jx,53̀{ \hUcC|NhۈBDohӠ>IDӾFMJDMĔLHPN#{ $3F2 %Xȁ8Ԉ\уEN؄a$b`lf#D8RBAq:-\]]S2UAՈ[ 6xlUKC/vUΨF̀3; %q| LTM4-[;2-=4ˤ ~HUX@IФBkCH1Z0]8l-nVsP=$NMKTn"E-S%~ S@%Ȓ4Q#죝N/)>/f}JdQ\jnʨO`ѐ:䘓EYP3뻈gز.#0D`H-McdQhyhy< emh.@X+YN6is}ܡi@#W W(=i@ˈ*N0Il-9*j 4J^ֆ^JSrO}9 mj pIF;k@&-ay6 kLX.T^p9%:6~/c"uq 0pkm6b6p`4B#?f@ )ƀ7 jNW g8ӦHfn;Www\2ufV!]۱$L( __Dؐ.#7fm}khd89s-7 <8kVoHxY؀O69wq\݉c+fȎ < Gj B&D N > WpJ=t;?<d#d% `ZOV'ɚo69n+r'i0UP.ObsU(mPgI LH:"xy3Jsv}ʎ==`ȨC 7$  wwv>a hX/#@I@IFy>6WS}h \gOlfUHID 7i/  "y uW aZ!X9mogz ")XaOz(2HW9Ow 0z08YXp؃Ep 0) 9Ր( ip)|9ˈϒҟlB#' $Xyն%\5[o?ܲ /@8nZ&{_kpjP0sxm6LU;lڵB|8 $qca$)*_8VWDo&4r6 ”̍q"LKcψ+Ct)SZ*ujTYfxAĄ[T#)S9Dn?'w.ݺԝey_r9 +  Å+_0~8 x2ljň <&- 1Ə#kkL:c#'=t.۱w{ ,9]Zܺ54dET;(F%fZ{Jg{-F?)!a65iؙWy@]=269goї\] 'ږn\sE=׉)i|j*ꨧJ#0b ȧ92 #-eP[Z+rZحՂи;ܦƺ~Kض+SP+Հx̀PY(u%Zb?20_Fgj9y*'u٤riXʅ=*@07h:]8#]8LN OG'DQ# R;]?0e0_xoqG{ݷ|߱]x߁E#.ދ3.8CwLQ^Njy柧dg7uMcXϥt%5EmFU̠ A 8c7.%;~P3KhBIh0! = m)B'-l!+WX̐8Ѝ(?4a(v!0C`e l`+bB(aJlYX\'|@ Bd@rEtFu@F.p@ ШFQ1Ab8Ae0 DXLcbU=D{沆aO^?H9JQ+(mWcAh$5t@-Y<%4xm#\&3 _$F2iMfnc$\(Qk&PDN|MpZs`_\g4Ao۠F(*da3PC&-Hy!XEQ`^`Ĵ[IxLf"=?fAei BQL*-PDcRT.1Te|"`1x^c1y&7ٰNNjZZӨU~iVkVUqk< 1k/~"'`Ԃ Š1ˁf㙒4foE5 XmӜŦ/,Nv ATA./ljˆPh1 dech?_lcbֶ?Iw[ҒY ~7y.9bCxi՚fl3zԳVϖ  ?^+nr,B0pt"&Fh`CaUlZ(GPptXlj FC>ᯁ Y ?\yr,V_s,[}8B AX!QzRN5G رG_LI+wm[,q"'~O;ǟ1 qkΟ;Nkw;_t Tޱ%'b(C'}R*"*))+ %GյEeIM$^0BKSI V; A;4@4 $@5 B@#h!6B 97]-:4@.C9 @ rۡ7@#3`fC@d4< dȡҡ2J$ A9| D T2bj@v=Cc31"B؟4h_`&'bP %Б"+*bR"R%",[.F\I1T!,BAXX`, c9  5>3,r@.|B48&`^9@`#j@2 D3<$t)c'h$jV9xh>Bir<j)<pHE29< B.TR]֢<,`|i]\4B#؁hr(Az$S9d 0LgqPB$f6ll.dd8>a.c)7@ld23x=^g3&jDÔh6Φ1d@%%lC;B!$(( gFu\qj@ ХKޚ<£BjFj#L',VQD?HÃΧo穢*$]meޫv^a`m ɂxm@޳X&l$0k:kZjZ930cy&>t9A }k )$dhr 25(`@p^e)I!:j)a5m&Jh@.p؝V6vn6|BЩ *R t.bSrBlN̓ U\`E)A1:mnGz"r"((B\\֍EA#l#'2&u@;Z#^9T&_*AЀ,a܀ ,@E6n( T$Z* l9:vf8,j9,j#c"_`9 H@2Su9n%pC@9B2A؁ /'4z_7U mէ-RNۺa/n[o[~[ üݙN^/Azon!ڎm9hܒ]3mC-h2‘kҨZBd'. :ub2$d8qBiepJJ!/ aBC2<{d{E,oޯA؟U@U>EVo26qqK&7'Fq//N+Ja.S 'i;C@#_!&'/&)CC-k;dC&aqt S綰u.mbg._ ffT#A If&rCp@1b1̓Lj#Pq:BGʪj<¸&G!n T@IsI.:p4 T@mN+N' +iiJ 6 X~3/ ,A̓#ԕB&& B;541Ȯ53,v`l$$8dm;2.6 w%(6woD"7X gNw4#w.@u_E:u2@u3A4q)kٮzw2?1YuHD>+D4U̵׵> \"RT_48abQb*6x)Qz+Uq\-b1ofOŒAqBH_B ?C20k `]C. l ry5]Cy*)pr&S -2}x=!v'ӗ[y5yr"./סnÚ8< &5a7%ÝuYg^KŁ}.FDLJ53BhO 5]s [ f: }+|ݭ!Xc{/A!O/fTƘQ^< <['Gb;6c;d;CXgϞ:K>|A-Bm#BD ,nkoyBAyeBAzR4euie0ztV-^g̓Kfn*?@ <tƹ!LB_>I`@ A0A8C{?goYqT[e tF<,CL@[P G AX[E|Y>@8`ABaW9! ,W-4FHZE42nܸ'Q4I2(/7f}d}7<5lGJGOviE:jUWbcƌ?1:k@\N;z=@,u jl*C$!G<1mhv/_nD`&] >akQFˤ yJleR k` K-Ɗ⋩:T8?Ecr>6CFyY&eYSk4TD=QLYWjY;LѴg0YZꩯe]6BhꭹU%њ뱣%[PDmn^mQkhsZ}$RO{j^Cs B0Cr!yGm7ƀџWN,@R**RD9$bfv|1B0D՘pgp䄞ɡ>מ髏> ar&nUmRȺk@Pxx _ g-Ё6 B$ 0,@ -B*@B'Q" 8„l˃3(D`~;Ao,q4wAy5½:k`p.`# F9,-j! vUcp:OL!D6%򐋔S#ݤHBҐoA(8<JA1m0)QJU**aD-OY"leJxKX%&,gi`3%3WWqа91EܜSGqle6&HFZ|$;NIӑ#ɁIMN!I@oPAP&\CPA}DiYQb4m(G ш"%"e(GM: J`S\*;݄7gNs=i ɇ{+Pڽ)5L%j'(J~\i@)]WU aMNYճceY֯UsEN]Ҕ4;D rj):S.e^Ye1Yd)+3JvO5 Ψz(OAY#PZhD$[ΖO`ok[6J^[\V% rm{\`BEIrK[y 礫aXC't'6){|A<շ?^iF(` NdNk4_ 1|az*1\a0%F5@bj?US< aΖYA|fq?k8pD\aKnPe)KCgxM9]i as0f6b.3m6c o|O)_ii !D4#i, qN64zۀEj0pFh jpo*VEӕ|F;dE5zX]m {M^Z*%l-{'iw~_lXΑ3)~{dIźٽ TR&BW-H쭑 #ʄLX(X> 1wRV涉M ep:%/;ٙU+l7ٔwfdUO3*x n`0,Xh`paB@`1O-PBo f@;E V Q"Q ?: i 2 q S 31e F@ ItI `2.n& 3=P3 t-DaTs*`tL5]1L64NլΌNl3v7~C=2M3(a Q!RG%O1&4 8 r!T'?aA lV UApR UUUlB@*[O !b5 !`>AfU !t <nSs U A "` HFMAEQ L a\p+Z9#T!\a\` WM@)p@$vb)vb6QV @ ` ,+* KGdM*`s6es|6y7V7, f蒓 &a9jv %MV+4  Ȓ(Q<t tnju@AHu owAos a|  Xsb+Fa)Wf T!Tu < @;U U"!TFn@R4GbGuqA~t6C`7ywy'&AE `jD`Fdi+j`*/TK d c*j(@*z` ~7*fap6g!e!)r838W OLB6yEnNqr <4<}  ]n`TouRX  * x`:?! rA 8A!Tsk f|`oR7^!>aG!b_u`Y8C @Yx9aa7x_A q&gwb(-A~A_R@$w |w-` -A @ry{w& x*1Q!ѩ9z" * -jPG!y7Vqah ̡؀SPm@ > z7>ٟsto4 @ ya@s7xfTg` VZs )S8X WWZ2h`-!dwAwYw,WcVzE! ZD`E ^I+GTM v,3 *}k `w!JؗØ9*JQUp/`77?rPC AeOR!# N!@x!^ОG ^oiU Πi]*V{!u͐x(AsKvҎA@ (YYt+:= Bw½U>ۨ!JAr6/P KSVK Gt^KK y:xh*! fdKtڋV|Za;;Oqu<܉ch5Q :`|v@!yE[:FAǡG 4 µ; U0GVvw[>_mr@s@;и%G:l?Y72uTdYC["f L ;UOx?;ۦ8wY ?]]E Mܑ3n'V0` 6dZCZ%teTH`` *baģ4wZ*2o6"ͽ}6r:|PM!a'y\; abg=Aψs̕R K & FӘS+@`WO' +ai1#WZBE 1WCs;{C;;Csc3 1EL^t@| T/h +!{Uv3we7 L=ģ  f@,وz` _!Ź'R%ai7'8OԠ2|~BBoiR? x ,T.e8_31ɀm 햷kRo pzP>?)@-T Bŵ`bi=B? _}+mw{_ D7!æa}#Jmo1NЛ C(S~8寥˗0cʜI%0$WlkNԖ#(_BYYSTA'Kٳ2mSgLx•;\xwKW]{W/9aGBʔFHJܐƍ`b]N98mĺ֛iHJҶqˍKƻdrUӍx9_h)Zowμܰ䄘+ydۈk.Pu=|۳\n* UU{^ۅbZ*{sp Wlh `5ivAH 1^onCHCv$ bȦv$Fma]nrʙjjwٹjÔw冡nk# j lu^ʜ Ƿ+!Cc9C8ڎdCZ/MHpm@)lXa ֗\91Džg dR>xD-Vz"A4\!u4bHq.a74ѹѶk~L7}lQlUj-f!1c-2}GaKvc|p/SH`߀e /l=p)N-xO~7N90,c =ȗy #+@".7!J6Hl Fo\һjP+o:gLs6ua# 6t,n1XU$6o ^[ĕO\K?r0,@|H@2p@9c# &2$T4XfFp]vbCH;;F3Bjx)|Q6,xwCb2 D=$^k^`soRi[)*P}6Q ^T9~x\x=⥏tc i0rgL,P 0Ր;6M_qCQVlDlb3IbRX48TvRjb C(1[ڪ#0.#v6TQЍ9UNsn"w]rn>wEkg M^ _o~Y! PB 4C E"f"XpH\ -|`+!|x|CH,JЁolRp[Tǀhe &1;>J > aPPDhN3 1_2onsLg3Alx"\g6Ds泚<7 zw.;|$f4b'JYE x6]s[YPa@.| Z08e]N~Ӝpk]yr'> d'^#x 4򥲶n{[,eHU++7bS$PڤQFb քc1&3|{ddEO9qtI]$9y"$qsm;"@%H uFca +ĤS@ f0S [T!oqͮwwa@Ae<2^G@hY㦈RG/z_$0HzC^WWc &DlS/>{>M=?z>$P=]gHs!DIe=\'K8/; 8"( =@}G`wxN{pUZ\5V[V]V (V eAB.1\%M[[ Z[q ;*ZP}O[#Z5X QpZ>"?x pAZ KL7 ,pc"gTF]7u0_>$ 4Qn$REUtNDH|OtOVbBSStX8>RO+^ps !h0 MXJRRPS5PxJ{yv)5 @V7\WMvF7d{`{j/q203xwW:zHnظ71$0qA/hT'O0AA8GZ Gԇ ӐH^Ay8h: XdbuXZ6X {0wi(wHJ-ѕ-a ^3qwgA8Gw8q$Ќi+wDUŀ 而 ) 8XORlxq3C?tJճ*EtD@DO15:IṞ5hБãE `n]nH~Tfv1q ;!QmN"@@ `-p)wVv  0 o Q1-q icPxwUxU J:lxcIW/D3BDȣĚ²CI1c <9\ 9"+ i!d e(0Y]P WJ!MɖDZrȌkؐ)mQ1ßsPМHZ GHVMfGq3S(6j)Zҡ2+R+*%+"|U<*XrjST$-s2R5qCbF(%\a782Ro0z61!0a*Q07#`tUIlk&'z6l%{'⧚Yy!|h"Q۰ +-`2񨦒:`+ =& pP Rmڻ*- :`p\ ܐc>0`-Rb 1C||ܫS `1Y[k 'B؋؏=7$`vwIL]JX g$ =* 9~ؐ] iOiq5qwj"|P}|p;BО׀ # J,m K4ѫ+ܱ݂@ pP [z - p   K 0N! @Lm]} _| ځֹ (pѐ 7Z=c Bע .PP@VlJzXۺ-7)* !㮱 ,=0Pm#T+ܰ#`L+E#rK ng-03tq0paۼ@6AQ0վ2e.: Rj{.6^`^ל[s `gFz+ϏܾNiVlIp> 6 4 0.`-Pl #º Հ "C{~7 @H-(ӔnŪWl$/P r t\) fDP#~:pѠE} r^ ®/^.<J s Ty`?z jxdf 5Z^NB.[ ."^.Ѱ נ(p30}" JPǼf-oʹJ= /}<ҹP ``PK]rӭ|O gZ~=q !"7 7>$B>к\9n;a?c柧߷ۧSNUbz؏5~m<2T ¦];%cȰ/4l4tAsJBlQ,7,"!=. P3H9$Qjc( rSVhGr\4lTT]aR$CarVi򊁍 dRaϘƔ.mM*1Wfu6Ñna-ʨ–`Fx-6)Mb*,[E:ҟ(B5)S&ր:7 psld7>KqjSYU܈u; 6H ō$QX%3/dKۑ"QEz I̷m _3wkY暴$m5fL {XņWhc,e7u[o,—ֵV8Jв>!a U`5b0ɢՐD!߆Bj ը[m$nkxxSOR,aS7)VqơW5AݚH-18D>!+>|C+[Y?ZSeH@N?_ЈG+|_IB{es8VqhD۹0i:oxs"TqiLG80h,PYсTTQ&F3FV9!l8.AaAX'c'ʽz"kmնM-k9f?*bChsv PIbwmwDd > _8=) Fva! E 4w wF 8q"$/9^kG/^@]_ǠV2 @AXRle~Yc6,ʧhE/~v]fo\kE$Bs{kE(v{ q&}' ݼA(<Λ7%?yK<{qx۽Gs!ʾvԯn~7 r鄡 ks/]MuԣEUP'KM} sA~}_'~gz@ZCtXvq h/X6=(#+h`@h^@@;5X_[6@6(ea `. \,'u'v''My'Jʧi$;BL`&lB'|&Ą$\B(B')\*B.Bި. C/ % C+B3J`5\C&B7C(DC$V??{P˿ .X+L: :Qh5Ѓ%?DX6@:9# L4TX`@:gȁ6@Q Q,SDA{Ab'egѧ",7㧰~ @w9PeC YTY"ER=RmҩeR&/qsrW4C![[-[-$pWz(ҥ(R+ A#M )-\ -m/mܶ)Nܬ\\ԬʼnNNВݲEb1 3Q*J" I׽(/u"ܡۍ"]Zmݒr]᝞%%+pXҍ^vͯG\Zy\UIID_o57pB^eV ur r2U#_M+)wن~Uwa1ܹ+OUk`/spō>+o`_-F}aFdca'!$B(SMɃ:%Aڢ&@3%2+#& I(Wc1fsx(Ff(a(7^&ȅ%7&@vhhx3~>.E!a>`! 轀.ދe\l`TNaaYeZ6F|r 6¨Q^u<0%n}y'@ v ps;%ȉ&hs@ AU]G%p 8^%%c=" ygX))1XFf'% \&`@)8#OP%esne~I>$ 2Jb._.8i΃Ib)'؅sm6ށ f\Ȓjxpfn>s8\)%c(kN[ hc/ c6L+ #igH0\؆O |V V f]dx1`~^6lؔChf%i\mqi5&_>^nfn;GfbH@'@v"fgb~^hUp >kFfs>4Ɨn&V \`&=['/% AnVg )9sR(,ۅ@xuN.n]>eF,KrڮZnZ%$g97_@!FXM؄E!-g5hjjnp)؅$(%V msp7jVf  3xs knx,yec0H"8~lgK$=)B w173g3`;')W[h5 o.)M:6)7.(>rHj/rrVllVj}Vpp1rmvsrvowwIAp5 4|}fV/g6.L~sy ig3Ȁsfmݾ07KK؅j8 6=.l0D_$0RxH_U{ v͖d~j(]Pvqаrt@v:XPFwgv j*%oro/'p3wPؐ:k(g*wiM_%BOgX `g r0 /Ё  ;g`xNc%XhIg ]}hp9dkȊ2QH^kpV 3yYgfq 4meCHi@@x 2l!Ĉ'RЖ1cTG㽏=$$Q)ɗ&YΔ D8q+MАmr CjeڼB&޽ ҳ'Tr \n0[*bA3.,PbêK7 :Piب]n(g*eFv276|.w- 1إ\A3nVkCyGC =lprħ61"WARA>uAo,!]|0P #z,? 0 $1s,11"A 8$QJFqO8:S8'eIΖt\zYgi&kfc~Y# 0B!J5e!SYV * "dU8< wǗ_E&6`9T D sQ&_@6ϬApiiÜsr IlYnR]@+MIGgM{.#АWyJZ W}N A5(KdEh::|aJ̆}2 ? (#Ǜd@ 1b#w11Mq9CUnTHhsGADDE3tH?-IM/4̞Aāj $p XxhV[% V#zQY.rp7b/m:W9{{\dQe^9믪E694Gx߻ n>3^uLy;](~^z59W^v ql |46X G8!eba~'jx5rdmk]ҷQE S*lK2 D́ tu/Krj$ɐK^!eF|e|pc6,)Ow;pYf^%kSC{ʜL=HTLH2D#z KШp@ B([D a+s)Le2UF};b&MejJjԘL&NMrk HB¨< ,_TL*lC *׹εxcGI_AN}' D9˞}Ntp؁ѱc& -|k ђ(U"`Il'SDVT}'u?p@f֐AT0V$Qaħo;b 8;*vԢdKӉKH3/wӛ^w'ZWA8[(o7/0 C(_;MvQeX.:8-^abvÁuؿF!TV0@Ŏ|)VRP.s|j`>xhA,d"+EǑl%'yNnrq"jPM_׽֭SH Tt Ys^f <"DqVl>Ϸe1SQrE,^wݴ9 ~Zuo+eR" jͭhC " $I+k6ÇE"{"7p5oaxPK&n658k8 C4:њBMjwE0m%VuT*TabՀK8Ntbz٫nXS, dWT<)E;_EG 76BAU<0# s[2V{PG"d!9`qDT0L Fbn%6dﵳ]!/q&2˼w qB= g5@J!mHI)ɻsHQ$ዧS*$>_Ngtj߆/$7bwayLEG=D(/=% aL~;!+úTơƛgo;ӏ30W3~ ǚRMU#|AZUVj/ . 6"`!E( VBH,P~ &yV/ `"9& ^ ʞu y `>Ա\aT( 5Wl1]^ᔰUPQPnPD44Za&aڡ"X!"b*""!.b""""Fb!" V%f"$n6"%~"(VH[E@i6T".]b/ ŗY=P5^#6f6n#7vR H<@@A "S-Va.#<..\!\գGh UP"M b~UcC4$D D6DN$C>EXEfCrGF~$]#824M9~)#_<$.ΣWרѤdZN*{)c!Q%B*S6S>%TQ`B(@U^%*"Ej\%W:e(HQ|\Wve*!%WXNZ^%,`$%Ve"%TZVI> Bʢ_K¤aJQٓEY-Yb:fc2&=cj0pP$ťQQM[&5j&kJJ5 5&m5l&o^H:o&5rpgkԅeͦrmzstFmZ)%'vJ5P*TfhMa*03@BxɤMM$NxZg!2 J9 `%%2A_Qbh Ya?Tn^둨' YhQTB(UBͽ(5^6Q5|eyR .@YM;$L?|)BgEu'ͣ>>#=ք?rU$0h a3Y4MӺfM }*/I) iZHB5[5L](<|dB \J8Es8@. ߻DLȔ*YD3 guD0+0+129gP"2!%"15YY\ÜůٵlY.j(Y%!X5kj5_gRӃFŻɁЪتA\ڌEdji!!~!Ɩ~!߁DRkph!U)ط:N"AYZXd+V֜u؜ ZXz]"YBVZ@*9)=A`m+`BA $DBp0@?A4ڢ?$YlƊ&.Qz2$vQVS٬mQrᙟabЎ>Pl0ºŸcvOv ';t?os , 4I-@$((CMBLKAnĶ."/&+ޯ"--k&j`>̶ARh8:0CS7;?l[f0;I|. S b:J`vBQ0{6030&sb&0؃ @h)C1(ح?4D&1H&1<|KՀ1hq陪U) i)"/2#7rt#Lkh0`p)pZ\C.lr.tr95''r)9䂸62=C.9ue )/ .oNdrip2ҕ +#+{k`032|r2Š. +%Sln'A#3#:;'@VAق@~' {) :(8 pLČH&&Xi@ n(HH'II>.#KKw}s)!&S@5/PC٦P 5riS@ ,SdHuRWr05h5FF;S.pZ@q95*'Vgϖ705d9` 'Td5S 3,P@pRd7sH HɮL4g?+!iCB@ԛ ?X)p\?=@q*Qp&$,4@ 244ҭZ+ ĉGdZdb&vCuG&wkwgMJL7{zKJ MCk#Аl^ D@4pC,(XF'tE%p@%AGxn^$8ahB%^@F.*bn_ E\$8\s9n9p ܀E9xϖ2v@[9B, Ta3OC( \B;A(\R!4B yhk${z.2!tik3DH*oɤ)(m/o[W4DC:<trx0HtMzOwDQ8ǷPcU\́;Xw,E?Cl9 4/818Y8T p; x8S{..Ƹspc9d@/䂷XY9H; ||4xB2l{*dQ4#v^'dg D!d[ 4$AF8)9v@GBrQk6JBri;،l‹ 7˴π4As$q cc!39+{wMW]C>8Bi? h(9@5ƛFMpb@(T'S;7 u4 sc@{YGuP6 ZwK\T ,9 T<5A g[<{1`@D66sV$&DD#a#EH * B 0hB7ldH a+?fL3iִygN;yDX@4z+-ch/9rU*UrVNz5׭`~kXdŦ*N0aFiYXF./>p`Q/ D6U|2'N%%i8} AQhV k]DMڣY$hCOVrp=\6v\-h+&Pݡ?ARu2ʝå#lBav2@#Ji0a 6Rv> N%6p!.9Ƀ 4!vRF0 L`C\mk@D 2\@3:"q!D/8D3̤l:8($`-3F:NJ.ZB 8ӨfCϞb)rGAЫ*x%PAQJ'UTRH1Utiq.k/4Ts0-2T},4Zg]d6>qsim' `#PX<(Nr{N\r  `@λ5GmQhJԎO< wiBX%IOdrIffFbgr9RdAk+jE(r0#UG|iH1m6?CqD&6@n0ږԋEl*DHy695 X _yXa{cI4G9ff_{pQ3 s~6C\s2'$r xb#5@Tp!v6N0 ]6,jaYƂ:І&䀇TQѿJ/kGS)n 5uQVܤU#2l#ezr|rqJ@tpjkl:WA.f>&؅#0Ё@'0!Ƅ Zݨ.W:l4gAZ*\/S +dOh G$O3&glU+qPq 0 P#lF?6UjϹsoBeyqEu'pQ< b(M"W/ ixq=^ ôUו^´ʑ7JzTIx!]BϾ:qB^`C'́`/\y+4ϸԵW1|WQe&V9 aFû7QYx3KISsjLeg M5y\P= >(=|CSre*[Z.ar<)..OR0 &B?pyĔF}6hTmz:r}T8:eЂN|kPUmWi%1&605kHҮuy'^#xzvzc܅`;xƺC$0@ 'al]؜b bCL_lW?! h~ْ;Yu-ؖ">"Q..,HKN*&)0(NJ/8u>KYnJ9ڡPPVk^0&D*#oijP𝲦 "da A @d A jΩ&P æ P nn&noQoPo<%tb"#17:NU@V$4PW^м@$MS V0ϼ #CoZA0aPb$ A[LB2n'p #$41|" KtT!=#O<|LK4BRM=Ir&NQ KCa;!5Q-94l.T 'A'Ȇ*UA @qRCTDRMmmWMWmހMk4H$ q8 OP+P& $A[5\Y\]5ғ?}2]C!>^ŕ\)[U_TJ_`t`u] ]T"H*Zө]Q%Q C galR` %! 0?VfZڰ V'smlLvg}g hЌg ΄AΎ#4p4G("aK*iba5kl`a"l6lslӶks 6mmͶ^'0nmlqmk K "b#bgqu"݂WWrrK6p~}o4  1o Zu_vawlY7vkv# lWw]Wwww{wmwv /xkwxsxcwWyWsLUBhiGAkaA-Q{&FDW4E׮EEŷD FnFy0i"ǜ" b@uV=SxeThiw< k}tu1AOS K{MN:z[X?􌯃`$TJI6!sa&OX5괍XK[  nux[2yؒ/R)r'ғ39#~ #Tq=ъ6-=U7X/=SI|AtyX/24f9&*!SI`+za f9<٘ǁ$aKvvJ 1&"boXP+V39y-e4puO""V7&qr&':9U!ZQyj璑BaaK6|ArS/ D+/WwmtΝ噧χ;3D3H!VZqF?P*_)Mu-C+:< ?pҬ`9avM &mt{گ |WɗYQ;ĉ `U6&%VѴd0^7wQQqYPݽg<fJYf|L@~|jI# e =$ҡ`  3:E^]a LG|7sV;c]ij&ae0Ɗ`C'&24Xz3z4# ,WRi˽1 /(D.@aŠEʫdW7nd:aHVᔠJ Val XÓFW~Vafu*wba,fj j cF4F V5G*F apؠ^*fdj~# Ab[+!Msbah&0 @6F,:|1ĉ2_ reK|J˘1ZSx^ƜYsL9oo'MB{I)iӄ9;Ta5֭\vi6l<A, -]bМ$mxܹZH6njB*p9_h,L\m˶Gcش\nXeBǙSc"j ex,h$Q c#\{&r:0Piبݧxyٕ-.uM*_i7wHdiu \l1Gs| j1I#zWdQ3PI9"NVeC9J=j .hK;ܼ@9L@@[8Y<2"2@$T$ RDOF TØ`F'9G ?2 @PSS>5Mo=w|}-8㭷݈ 8#̡S%(Ec  t#:l^ld<_9zpkZ,7+p 0K Q oIedxJnuI'7f\SξJ~7ErQsωt##15f:6 #d !R0*J Nnk`Ls{Ђ#hJZI8$1I}In@؄CT! "+%Y,j^R 66SH&uqgW׼tP!N@rPIm'a[KhB{dP$O:ILC$)99JOLe)QaRrG(Lt"&` s$#IE4 =Ceך\b0 n\">@V\ O@A4.5>|BQ0|tq}$0bژl6qq mf#5lq4S&%1̐*pִe,O9:! /q$[2HE,RILhb""r 55!O#p DldGVDWBd0h-X@r\`i2-@"YDȆ(d,%VM$CTN8І%g7{ZO(Ժ䲨 /{S.T Xr=~0+5u%n|GVm&,FYyraށby nI0n0\}߻"]Txd^Gˮ}n,!PHR rqUaLUu!l )fZFAjV+DOb 8RL! bf_-?HxlQ E#"XJD!Q1T1XG2$:[i|!-d8d HF"]YY\VQqm2|<9s<<> Њ.4|1UzlbE}: D5G'h`eףȁ 0jB5MZϘMlK|znP ZEu Wc}!گNt5]OT&#!f  E5x:pK=&™2`~%&*iLCrI^4@B 8Z6d*m ak\* ܐVii3"d/E ts"xpdDF<-ΖUIFZͪV:Sgꫥ4T,m‡  x{C]"q[c1U,74*ǘg`Rp&FjFD 1I:k769%G*`b&$`X<~D a&pIYEȌ(i:m8ÁԖٚBPYo*J*p*X1j\Ax*儐*u zZzCY9עfEY d5VEP dxX4K KvZivyFPBLrj\A:ڨ I/0n "Ư"˩pp"8`Q]0Lbɋ 5;fUeJ)TuSgZZu(ku)u\'R[HЭu:Y! ۠ * pX 򨩒 Z : J{a뮾Y1c`tjlc 8BJmhhhfh{i0i3˖yz*RTiC k]K?#o ; : *+ېQ"MDZ1]\٣ ߧ^."">Ҏ;utθy~{N6 [m|PujvֈP"VLnz`>:eΉ[>~ ``u`^L0(ߠ#^%"^n~q=&N*{׮8|uj4S]ZŔv`Mt`opw>[N*>(]sYQkD_o>e"]1w_L1 1 [5D;llq7;߀}jy< Mn?-L#eu=SE?Li`L4OLC{WVbYХ%5'q6_X4On،؄?ϿA_h?\/LpDh7M^+&>EuJ>+/Bd*_%fF~k%O%$v -0uV9R=SяT](N.~-@Ur\uhZ2e>'?ӴϱfsY5O@ & ̴DxPЕ-cP!O6ȅYdI"Wt2^K2iҦNӦ sQ - 5FRM>UTUU+Iy: dhkM|e 'P6A˴̤꜍& ݶueCkG^ATQb6sXdO|8z))Q;@DR}AؒF`ջiPjy.)$yA W R-a1Ts.o1^Nɞ{r׷}?O>cOa323*&0갰* bkĿ$K1sl,*1Ǩ. 9#L@ rRC - C~!fN& r'1j#Q4 tN 2#<$ L1Rr8L]MGTR?ExN&VPBo­2lö Dľ"|rmR__WuawowsO}9#3Ul::z+`VDb2idz+z+Uk n9+!d Bad4%(   B Ր 8K4Ps2!RL-YLV(C,yYRCD+Т8q-M{M3>E><_^5}EaɦP(1@0 tq7P7SJA$/,zPž(~Min4fp HR.q]8ţT .L*U<*Xɪgb^U;ezh!m{qX&fʼn͂"2U*W*(uq!&&I݆P pm˒?-xPNs\@pT (nʜ9%rY p&< (4*h? D4 >APfգ)3lsCWyUN:+!„Jd:#R2ZdG9&>CSMQM7CUqSbr2FMc4ub6լ|խv&5+]{ՔOџUl}"1Tl)8X+: Tlج|:$iSu1D P9 ղ|0'!:uC "t*r vְbV/n G;ށί|kN޿k^Jސ"9u7A=U#L;|vsЃ!1T."50UD,-X"\Kfc: 6i7!:4QyT=´.ʉ߸Îil>ӚKV6<᠛j"ԢeڟnUhԠSrb"`>h'B8/2$#C ˊM渤t<< ~^\CҼ-4-EgV]*:#ȟZP~rl%; JNU"GȬ!B8%pg+ HQJ@xe=x.x+T򖳜x/w<1('@C+t4EGt7Iwzԥ+}W7zձuoUץvOK'Qd38y>yۅ~w-|n lAE/x7|%?yW|5ywEo4=*A zյv_MkX^ֶ6uWG SQ@~|7χ~?}W~wT@G5WxZD\Q5FFA~!!ypؾ$4DT@{-MP b?j< {[3{@(d>p v+ t2lx`](Z@r *B")A*4 ];7$243DC0|4`; \*N,q#[Y @2+[2G.3&= :2(4OPbDM 4&*XL<:Q62('D=3bDFbTFR5S D=S4DQlF3$Ej-X10RK1Xz'0@&'㷽û~ԷG[|MPE6?kƆtȇ4nđ5`FX HXd25 A <SC&@ Ďub{I{ 06p^ 0uɟ.H(k`CIPJ1JP 2999DKLK9 MAScH˼K a7X86L9`8I6Hdqöc@UE`/0SK:'2ɠI&Ć5:$#0A)zJ#BDZ0HPl͋ <ѓ&y8'ƻ ̷N7;Ѕp Fol>~@>~`Ot>A>|OPψ,E I2L9PL|L6 Ȍ

oX[.yO~W SbO[oA-yXZ sS/M;S2dS3 aPGЁo%Ёm W8k>i1%W5;~mVHX>~n'5Yq]}P0W$xx;@It0TXԏ 8.Hd(Yo#'@]TT<ՀS%JC0$ЙՀMѝ P JUL«ڬ/4Cp̰VRS o0U 3 35 V5@1&x-0uOԇ$`\ŀ'pM>~ }PSÅOyOOu>Y0WP8}x%%]Syʈ7X88T]{֣0x73"@I8ux, '0X"@Q0*4 +hGDR$F >߀ɄR`[}_UUsӤ B,ڭ/0񬕺<7Z3N}0c3= P7P9ЌXT h{7 @_ȇWPX P@=)`b(d(pG''zqFZ$hC`YzZ` {4 QDd2cMkf0cI?0h,EF>f[]OY3XSpF>5Ёeh]SS(WXZSX~́݇/>N +ܺ̕ ցOXc1S{5 `sSGW~i܍Fh3P3PW%x}5WFX1 ^ym \+Ȏ\H\L\̲K A7 hHǻɰXl $mddZd$7M4Cgoe.6.y{H%x<֖O'xG|\Y\X Gxx懖偼iuƀ^O怇1xm% 2U<>>oU6Ea~0pl ԭ xYxև[.oV!_k2P𵦌7h0Pk}T4]0ɟ`{ll h o[H\xuxypait[X[ 8}򾅝rbH$[xgV>}Xv1XWM.3e8a]>lnå] (b['6e(7 yl6}H8߇6ZXGR7< tᘲ`ڶ4uD;K9U/.= [lށ1RYL 4%mWPg|]n&G>ƅgOhXۥaJ[b0on}3xo5ǀ.Y0m soPS'ȀoXxv q%tCj|A}魖1Hk5 &T3(}GΛqCPh`e[etU>57vdun~hWzj?k Syv~p\f tVxx \GHsjG]ixx3iuFv wW{gxaxYx}[5~1u%X gLƏ (0HY;Bow#]R'ew (f'[/g?g3]}x qv1zfaG!g־oh\xkr?l)Wn<'oX w&qHq>~N2|3Uɷo'k-~%""?i"!VCY:>'ļI{1۾6oY > *4(5dze5CBVZa׮^ +v,ٲaMvmWK\vسpuE-T#G.p &L0vrǍ3́ & &XOøu5֮)s6m5y #̼R#bBĜ'a } \ O^MZ+l5O3Dcp$hWJ&)B3fU+@aD-jD-N @sPBzMTEmd4Rw"B裒%6 m?ִkCAFN)q$!QiWYЅ-a%XxĠb9Q^X\~^|Eah`oy؜ϝp&} v'vN30hS2`ORZk#,|3Og6#:PK9` KOX;萃̣ *.HlJT: 917uۚLD)˔:1 A΂,nnX`F;|b7'j >I fFed=šdnd0X)5M+&/2jg8 j<:ρ$B <5ȘFL7γ̵zs+@Sֵ\24]}S6`K',#K8MYx^{s5A]{[ O}5eNm2]S{}y9M,5Q˱kl:F<:̜+WpuO1Dhʄ#o#Ip5!_ ɿ"/8/^9 Wk8/t!7.,5e,Vf2 )\3gB3-vTj[!mZAYj.![qC!RĔX,cG@`0FB!8? @9Am@6 `@ F.y%@ 8ʑA0\@@.axGg(az?h@.l(0*eq9AK;ɖze-o^-Kc ӗ䀇`L/AM+hը0&z5\k'9i!H#&Aj[0LF!dXW28AW0<3+x C AA6!,?щ;.Kv!=DaR+djBBt %JHYG`Iԡ ըI%RT ,5ܪPiίjXjֳJ j4R@&Ŏ)QKCDx)§b D{舩?_~]+6B>d%{b@]c"k( X@]cbE'3+Wd{O6~8 .q\7u.rBhUU-AMW5]/{ktI=iY%VJd8Oކ ˴ȝrKa=v 3+CpWTmԶ`):ZըBOqcJ*0 BNi-d'C9Ra/dӮX)`7լ Sp %രMNazyX afKg{y X$e9bIv*<6В0`:a_q.Y`$Ycl3 5&2Њ);Žr+k"P4xƷ(o1 Lk1sWZx}Ƿ'nv[8[jGPu9Tb! .| o8;(iBT*᱉( ';"6!]w< o#>_<C>|1zqrGm Qj>@xeѾAw3OYf_8Hv Q">o#?_>C?ҟ>E!B^HP4p҃?6'VOe~=1s^4pm2I=?ʑx5"O٘1U5Տ51`6S&}`Le"|^=lYرXuIuEt jW!vQW*w w UZk(M ) UjbahZI ~`\aA F`aaUL-Ӂ!TT"/&la(&B"ߐ!&D8"/QL%d&'rx#(V$B%~ >!Y@!E͑Wc1ƚ2ş3}rbPx"&B6C?5fC"nkb3Xc64(#&<#*F<)!~c6bB4G-#3 @a4V@^Kc%^B?R7d;bLd5$;&NF)Kc7c*̂&BW>&Btg;Buv&%X#uP`K&=΂wvҦxgnAAYr^ 4 '2xtneO$UR)%3@AqE-[]>hɱ,#c%, _ !;mK082$`Tr*) d'.B&kZв^6_5{yЗrl,#--$>I.CS-6*:Gm"A 3G NsWprASiC7iR8O^9 퓨s63"-nkH˳ҳs11 ,v| + B{&CCO]DPŜE xn38sGt 4k4U!"3 `hNu'W665=kiޕ55u6<5;/`<s=p b.`L@GV> {[럾|~]A xBj2+)m&L$W0;hIE3(EE!~C5Pu uQfɟo4= ?|?Tt?@8P&Ta4[Q1fPB[@9dI'Q$'Hb:65۸]_1g ^yfHb>YjUWtE-TMl~}>|O=qF!JM6 )4P+A QI,7PKCE4X3>6 k CtiPǬd `IP f~GL@ &DLH%hD<0ˬ{B+N8t:|3Ns=ē;%I4M1hLNȤp 48OA UQI-SQMUUYmWa-Vk&@H% Wp[TXkd(J )jbCiE(ΞZ,)*.@*vus[| ޺\k^zyW^E UIK8a\!X)1X9AYIbUaJM` Yi^]*'Gw&ug&:xFZ裋~ZsA*] he)[.N[n[nP1F\nxΗ8.o\G`g[9A]I/}|Yof >$OTm]wCUn0ӑO^oި&~y^ {0?#@rTPY ^_;ĝjc[X}4n/p @EDr?%4,: a%KohC p a#H RN0 ,a'tХUd!\H"n>0o?#c%hA]I ⪊uG=}HA4!HBWWĢ-FINuiӟx;w]!ZюͦA;PvJQI[Ζpd\-W6+Mi\w'mSX*V&fU#H\NsPJUn7 ^WX.Pq/y_Ը1/r+w@h|VwmEۊV v0yMZŲ`]o•lU2Nͻ^-P1ae63HU: mvg9ϙs*:}hAЅ6hE/эv!iIO'ʥ1iMoӝA*ԡ6QjUխvakYϚֵqk]׽>la6le/vmiOնmmonq;MCE-1.608/images/10_Scaling_Pings.gif0000644000076400007640000020146212511671246016132 0ustar mariomarioGIF89a"=;DO}   'Ywcpq{s50h "H'FnO 2" $<5a/4>=  D .$.?090P1 1?\5&67>?7MO9dG:T/=$?: ?R@<@VqCf'KLyQMY1Nv+O?/OJOUOR=SqTXLV#Yg2[)[g\]R]fq^X&^1_Z`v(`~Pc5'k=/kmx$mJnd ny5oXo:p~p/p4pqrRuL?yxw||~[O~i ~)~X~s0`5:Gy&ɇh^7Ydfdj@*jj7-q|tcevmGt]a3<5qAxncxSz{zr]:5if᧵ħ[>]bBdYvU;NɎVNRL̟?E_C𸫨HHȺDFGóźɼܴ˧~̼¿ťʩγϿԶ̣Ӭص֮ۻڲح޶!!ICCRGBG1012applmntrRGB XYZ  acspAPPL-appldescPbdscmBcprtwtptrXYZgXYZbXYZrTRC aarg $ vcgt DndinX>chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2012XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"SH*\ȰÇ#JHŋ3jȱǏ CIIEYZɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴSZBdIիXjʵWSKV읳eӪ]˶۷pʝKݻx˷߿ LỖZ61cǏE6ʐ-O|Yf왳G];{Y^װc˞M۸sͻ Nȓ+_μګ:;zYק:v{Ͼu/?5n/}>;(h&J#u}םxU^R(bf}=,0(4:TCώw>$v?Y!Xb>60L0"L\v`)/8(p:jfl] 'w8y:L5\IWBC梌6裐Fʢ)$KZJIbz|&WJ6ޤjH꫰*묍R# *"J< 3@LlhȓN;TKfgku㾷dK(4ިN:l,>f{Rhgy*098B7ண8Vi6l(鰛qœ0131l1i!,7$lH'?߶'nꡧn|Nw38BD:g˯Kltp=,~T,c-b\3)e?f<7krZyK#C 8T;.zФ+觟۽-lu'ZfucZ 2BHiXux4C5 aZ-,f&?PfلҌ|"̡w% a'}<4 < 5蒚I5 ' \Ofzmlڨ6q9ohsrQnpIB&z^2u=]$#ёot*!7NƇSF";u U< xEO;D$Y=ױ^"Kt%.1{_C1H ~tܘvp 8Ӈ@]x+F}0l4#<دq! 8?7O>gMY CvC|:mӧ'*}hmÊ?&thEHGjrjO>2I[(S4K$ͩNLlʱB:0d7B\MAP.$盧:\*yu|Q Xf0)SPQ)|+ڏVU^J<"UIJ%ɗL:5iQ?N61Rp hWdv.v+^jriS͆)G;JÏ6M.st9's}tɘ%L(HzVdZrSc'8Ϛw\wK_HC6ԡWX ]Yo~NlU _cjI~ pA7pp[_&R|iepoCH3#4j=+JvOt0 Z"[ٶU:iDӪBLa;f1ԃWNshoF"lYqg'T̀ޫR9XҮqzn'ct8.4zؒm=1jB͐~IG&WJr*`^7ʭH_0`w)yǡbI% $YUٽOuvQlWrvn[q@$/rv#JJ{rY]8..pLekj37]GImtv)%Aں }pv\|&swf}* .% sZ9!01!5AuX:.QZ=# fxԙ9.@*̡U. *]lAP gU׍A \=Zz*D]U4JF;@/{9r̵].pқI8ޝ QX# Gk Sco TŐwi}w#B7WoU'ٚm8k;yHhw=4a+gzS{7&7է|@|b1wqs{hqXP}sPG0t@zG+f=aqu"=I5&m=oVm7t{7qx!}Ir bpL7NR&\v'؂Nv砅Gt7utTIx^M*(ThX8nȅWxsggX[cn*&WnbǃW՗p |PDZ'wڰ @ 7pwrxc} x {ĠxzSwЋ s}X(((X@zI y2LH$@Lm@ dv0{ P @uhXzNx'X Xzs@yQ7#%' ( yTP倄S yq n8B9:y)" &IQ˜NH@h~ՁY;P't.k~ Ww`AĠЗ: Yy ╪ח~yf`zcҍsxP7ـ_x P) Q80 : p ǘ01[i) YzH@}GBqm7Tqqq'rǁ _虠9QRə؍'w#exyPؕ( 5Yiz YpR89WyjzHp*OkYy2D7sEg1w2]q`zh9ٕ}Y*Ȍ*JI:&zP z@f@UXzb?Y|hhڕ"Y hNZzS"0 n0XpA6tz>0(~@ 'fpjNu: @Nwq׀ hxڤxvʪtva@s '^j)Zjo[XSʇͰ} :úoQZ0|0 tquWکz tf t:JK[!DHjѝAaO`yɳٳre|jb){T *{X7p{JL`zI hwηW+{/ڪ[+{][{ :ЀG Yjn S)}aX9Ѵׯ&7hGW٨+I{Kwpkzr{{g|x0%vY3e;C6retpWM {+{ʵrji[zwvga׵Ho:( Ȅי[{zھGqJsXФj&؀l:#{{י5IܐЀQ{z׋mG\T}71̹W0}7y3Hf`pwH*xunw˞yy qڕٓB, @G`hJH@P>ƭp?@/Dozވ*b[VD퀳 %zeO5 P HP ieR>ԌWz&IPk~_纍0Qڛ\dl(`զ|nz[8=,S Թ*._쾁귳qdm`:b/eZzW\w^FO_i_Ƿt7\zzGS,x#3#0kpMC!x=(C2 B(P 7-bH糕I* S鿜dRDMCK"J =4ك^BK0[,x "1 Q8gґ%O[b, Xs C5gPF eYnhѲ ^޼PMN+Dqguݻ4DD!c/]vg917\y;Ǔ/ozvܫo=|ym܅#)0ÎWj{D .iꈬޢ Jk𻉡rA(p9I:䯤JPAh>pB 0{‘MHH{Y,K 1k gB1K&*lK"KJp7/Lb +#8 7iF |1FhlN M7s:OL%uQ#Ξv(UR։JSO+RQ;%9S;ERU)mѧF)w) Eێ8$@mG*ĄJ9f>gE.EL5Avjriv!x7G-ɶ|mt&u+N0Gɲ"k̶תCMr5)IpJ6GLwvg*,GuمhYCO>9bn數_Z5CSP5GS})QiJ魳jaWfq:$K ,[PħIH g^Vم'搮cPsކ&[נ ?oȒSf-[WjTJY_%6Y sHM3.Ԣ(5hPȰAHFa38A'KX&v6 0]B& (G[5E/'0!ɖB)d;C4â?ITz^ϥOVjH8D\&Tm zݚYҮQFkҾl*k{BxG<07uBH }D3O 511;.҈" ',rAB` !r|T\Zž0lV#H>a>d|$A{ 3 7 T0CVQ.njC6X:2ixwi^4UϬR 9rx1rTi4ncxHZ )/q@-қv rKۼ9T'CE(pA u4vxzP\u6D]]OݐBܹL,D]`oPݜ)}\'.B! C˦>tN:`x\\؏! Q$>Kg<~ iW mjz|3*S#/싵tbsj|-GEJ;w/ ٙ_wc/>_3_!L֕8HKq |ַ9aiOXW,Ι<@9 >'!*ޛ$[A>!K 5@Z:t"ơ 4" D@ wha @|9I"@ @D'B##2A1Ґ"SC D>6A8v ?RXq+r9YqxGTHo`┻Y+pryZWsbOPy51qE9,B,$s!^ZC?d]¯y%]%|󠘃DuqPuHtDJqXO H092sE1 đGɗI{4tIxC ; ZXJ ҟNuʘ{wH\HȊJbJh3kԋ$˲? LLT{*,L%qʆܚq%JmĮLʉ MT˟`bL48[dJ\(l3+IMMdL pL@|+{Y2ijr+|ϼN5mPD҉CMM]T*ԆbՔ,Β^I`M2\MVe?O}?DTNV>5ҋ KopDLBh0e]pFFqJtWz-PV,V2h%u(ȟ RX%X98Wm$HDWXXmQJ { S>KΑUXeQEٕmLԊYYٰ?K Hlt ؁edjsW1ӔΌ Up@czQf_C>fcFC\nfgfb2຺_VG8~NiM|fqg v,FRPmx'Ly.&g{" Xv|ȇB a"X~ rK/gh5gxfmPAh͇xyޚzh>i>۲QVkvY of>o:FiedePXf/%]jDUj^R>@HhZYHXj>kVԪeLn= oЯk{kNNtlPH:נ.d>eV俞l㥃TmUlFc雮i9pl^ǼUteQdm헼l!f [%K!qEmfhFh#vhnc-%_P+?h^Ym^`mn~oqm8NOm|Dde̞gpWcoSMU}|0U^vl?.p pRnFUkHZfQdkpT kJjYuqSqq$?MwCNUo x$2nh9U*o4?mJm.ߖ'0n>xކsA_mg DKfn#tMj9#W|mQBq} fLJ~xuXuYuZu[u\u]u^u_u`vavb/_;^7oK$f> }PGmzqxkr/ws?wtOwu_wvowwwxwywzw{w|w}wkqpzd7^^vߎLҟ\/[]kv}h Jowh[HB(=(y?yOy_yoyyyyyyyyy?[hwxRv_NBZ`kXv9[Ho4p}oP-wx^y?y?{O{_{o{GHhD2Azs_t򐇦`thuχszI~w[y{ʯ|˿||ͯy?{oeoPY9.?4Jp /}}}gB/7|Df2iڧܟ/Nw0󫁆HtX?_ǜWюE4jFAg „ 2\ON~ h"ƌ7r#Ȑ"G,i$ʔ*W Y(wҬi&Μ:w'Р !d s3go9LmXZ_ԥM Uسbuj/]m۾ YH[/.O'o2n1Ȓy5^̘5on۶貪C7zܶt3{5k{9ǎu5&0‡/n\e$^59ҧSn:ڷs=G`onVۭo}y_?h%Ghmјqb0>,r,}' :(z(*(z 1D֢n5V9b]ljf\O"{ejldIR!l\!ZǯZTHɊds~⪬fZH{W ;-RX{yOyޢL2// <0|0 /H bZpyөVX7JLlRcƪnF`0AA\Ѓ~THE0 d$FQo433 хZ YL@Y0=PDk5}v V 0w71`5K, ~8+86c$A6hjGNc/6OcK1%0A:Q'@)cTHLP 4(Gv~@BC@QYp+P E]5  X|+n 2žtaA E7[!-jyS@M=$>,?^NlhV0'B4 ͚ٱ QT ag '6Uvqq`]k[ʏ,ia혛=6 6IYS<`u\|]нh)8əN Sx&?'N1M]er<_k*!{d%zэ픹]B:FrSLO%M$7dtE+R:f8 ɸCJ&a4ڰQo D7-b tXM^ zMø7 gC:zTAúF}f%b f4_XdC֘zjCɆV`\ޓcb >0jՌpHۼ z GatB4,YŜ:!L.LF` R]cHɐE>-pj儎~`8lE O ܀x@t[(DJ@Mi )a!aܗb]T@ U@5@9FҼҴ 0ayO}Zr4|!0P ! H)P tt_E\T^U(A̍. 8cQH1+H0P }1&#!c16  -Đ]#16N#ANiǜ Ȇ7@7 k4>LȔ}LdC "M̔ΐ](n @@Q TA%&Z(Mdl H  E8 [a!쁁Ed`QO# DN Op@ X!d\Q ! xiYդy!QZ}um%Tbɒ0.܂_%`.LS_aPa&`"b.(`b6-tI_V.X_VY.hKgjfEfcPӝcɀLSH ;8Fǘ<&9L;XE5<dgqv\UB%)x@W \E<Rj\%H% 6F8\- Qbh̀% P%l!) {aHUT%Ut^|Z-O FKO]IR'X^.B,h,a)3&C.)i.( K>i0M)5Ri–r-pi,iR;ff  &CT6 錨80C0cDC> !l!mr"[U*@4l $V#6_"C@veA a'DaࢀmD8T\D#u &~^Vn0AT XX-^O@ M_pGd- )q)镦)hګ)7~)7]ّ]f0C{>lbale7TC506>_L=*F4꣊ @0A,BW LFBGw <\Se&̏iMR} 'V#~.E`%,4EdAABܻ철,F\uh~ 8xzBC@eM.V 2%!Z@ >9C:#mnmЦǂc2TnU8$D {C)PU!B(UyU1*Ek.8p*^UH]TFհ APR!@}(`Yb6%q}A'%X!0Ļ:g, &ӎrc* 鑶fP  6)icjgujjy~kDUtjiQ9tǦNcn[IZfdDOFW x jN )Î.Ad-UMW!n!@K) " \J9`Xr!#TnAhv.GX۸2`bE|WՀd l:qDB^+ E5P34S)ts885vS:\\;P:s;en]luq98{FŝRi>mr194t:cXC'._Mw"Cra`M0rE"JwtSrÁ nAM睲dqC}OLKQ Tެ@!/eg0+_̧ݍ@Zx%ėlh̀_!/,FM@V1aaa/`.0a&ddO6adZe;f7uve[vhO4ٳPX)sکĖ4ŀUmVsPq Ȏq3X[, fA !FLMN X3o՜e1 F2F,VϏy)@xxVALwGA SG9L(OKn%߸PK @Њ Y{˛" t+Q)ĸx.88 B 2Ǹ6kБxܒ3Ӓ9cӓs8vPx`01@@]T@tAvCnCw wPqpIV#,Rf<&<3@C9K@;m#A7y:`]C ~RKʆq* PJAWBu`W3>)  ;v+0_0 s)fi̇i8&nml;{K1멼sqq] ?}_@E__շ+M+47+#7sKc %:ó3s66S7ޓylC0A)tG߻o+COԃy=囄rL,W>_֔vk>w5`w f6d>cbcg>gvݷi¦6gفn\i>#?o{۹C9cás;._JpTqFAh:G}5:x99:E#P:7}S) [ 60/aUxcF!d {H,iJ{*IdIr7fնy3wt9sI@Zg2uɏSVzkV[v,W[`\w'Sɗ7}zٷc+.ӹu ׷~P";@%^r\mI&x*J((oQFY A餽]|eFދo슅}3vXnl"<\_|Y4^Ln8`)`(a3x!*׊ %(,W2٤SnymG&ffR]tI7BkJf "y=餕^ڪ}#UHWjC]5]k:MlL&])3nᖛM(N7P^$ocoyQW]Imvp!ܡh5ߜQ#2XcH;VxS/}b_G=v X4/oiYev߱xjEN撿vvAj3׊ߞ~>*DA?͗Aē }_h޷>菀_ "|W#T՚V%IQ:84cT5}5;= P6oO>ܡG2|zJ /=G"a (^`(xQ\B*)DO B*bt -J%l' EPr)D=R)Q `$>nJr#AapMLU a`A2W^ݪU,0A4*#tVNN,^yX]Gx 0>c&edv iezD&QMz7:$'B>_"qLobg-1T"ȇ8 6 UBo 2ȅ QMD6FM&'KaLz҄Wa߆&8iNcS8; *PoJN\$/҇w3$1eV5 4lnSTYUsk#g;ԪBg5>fVJ!Z=3Wh;KX\c3#Y63 baYS!,g=KZ>!2]jYtPkiZ\"E!cUZUڑPek UJjYձgcZ:ܧ\Y׶w܊- p|k+%|MG.Ű;cn`j!1Pa%` 0jĭ-&w9\bLn5B9+>^ٕkUK\*C(QH}|e7;H|)VPvXX<&#_/¬ZwXH6CCȚ޻.۪".oz}VbSqZOx< \B *:xA }z-Οw`m.ov@# F @ O F ,? pI@ &0?:>h <ՏN` 2!4ʌvAn\ݦl$3m&بKn .0" + ' !dÐH>꣛H! :O`p H=Ю#NZ Q ?O? v@.?zA1XY v &`0*>C'@@: l @Rh!@: E`) Y `\SKϱMБj1Q1Cxn|. k+\+!rlKhB,i?F/6o?P%z1/@@@?$1?/1 P (N h2()*!"JZ F@S_ An2!r`Z@ Qj!(nMMD#N 31sF2 "`F253!zP  @ʧzJj5OS5Qsa"ߤ"r?o## C`ep*Q`K8QFqtjh>lM'6$as'(QIHAp N٭:6/+0*$V@Ȳ*o Ah*O>qDP!-!H~ jO@H΢QH`N RnE?JU åhnFXFeF AR!r!!!rHH"=L+"p0DF ?A %` A S::4'K X  ?G~(`) TRSRgS!! B/uU/WU] A oPU#M!>'`M]LX4>`\`XX)>h?XX*Ud4M3݀e%RmjWxx7?moFS舎ڮyznl{?ж#tl`>+HpQa#Q^>$5;hAHP0n5r {Y?lbk*'v d`e?&d;u|EvG6`T&"?v!^c8^td؆1.lFc8a\_'&88F8Y/A|Tzz؊yzlUM\M{Y+4n  X}j(q W_0/Z qmO4/U-oӀMKnS[Oq@Ғ@@\;98L%bKyןtk0 mhqidžVflip٩ hFkkVk,ll07x?HsM ^:{QNM[ ?=߫=]4T>H=~//c7XOP>;p `Rv_tוه`MCa]b`zizbpvt`pC&apZP ZWӖ2YK@a(ɟ9v}+8-BQ#'VYd5sm +z!b;/r%9?AY4Wn@,>.+UP){tR9su!wruIDVHV-jr/ËOIHX_{XċD5~^z^ʼn5Sa4e5c6k3Enl9=Mߕ|P]'zaf' !"oi;PJ8Լ9Ԏ&?l C'C>=m=X-A$= DLFCJEuT8*EOkt4rI4LX 2[a+amizCu 4]؉ /MA 4s-}aX~A[!!|1м0ڛ"Lξ@ qA|qqCpU=3Nv\~}Wg0% vָjͳpM J+37k3M~O՗Փ:Ôc> e˜Rty]JRWGP= &s`]ѹ?8?ܺ>ww'[M%qq#:;w!@(:`q  a%3~՛)Q@D+QTH? ۱qezy' W^?_kLlq@@}^dvag!OtT ?@˷= @^M vV[b<8{ny,P {?rGGq $;?[چg5(hoYmX\<-_銕0b] FLxaYxSQ$I"fKؽ4kڼ3gfw!㦝k'QFeiR{NN ҫ[zαcu҃6Z=}i| #VpzSHϢL% 80(u OiAj]3]B(E {%8 2)LdH8'ۅm @A3+A),\@'v˙FuX% qɜuV\سuy|`Axֲtx]ۿ_5\2 H}`}, CRᄱ`Xa_1DVcP\ITWQY"/fՊ0ΘT;_ebZmGd2wuE2f[iYfK&iA)k5|]ƛ^]YCrI\tQuEIw=$מ{}fJh>.J?aO:HT:CIX餕^馠fijbꥒM50JA&F_RfGicl&[o&rIu|*)ᄷ`"a 5Tƛy66(=: QGQ¤6,9.SC|[Q 6e-G*'j {(k(ڬaZ©܂A˕DBʃQm5AT`APK "ӆ$6KP_GvKhcML$0HE VȔwキvw݁ r5b}ɷ2I2[Έqn'^z뮳 hżN.Rۮ~|ONqO?ZUo}߇=݋=ާOӤ҃ʏ#LbnPM.=sYjQyE7yms=R;۳C?\2s!.AB93Lg\1v-`ȅ? .;NԷSO-Wղ{k ZZׯ/f3c,'ypۜ,:58lk{ۧ^u2ЁPfk!f8]++w<㱱2|!^%JFHh+ jRVI_N<R>/+_{^= M(_a)(jr.8(?+tEA>P4]plú,j|3MYUWOŔ{_UNdgܡ^= 9{wB|c4+t+x` cʓLKɔ LTLH/Ԥ4.$$MTXU~"rl1N2l3hl"sd"{Hs(9@^{.)c(S4R|IcwWD2tBD0]WC]5DG$CӢ.І*Ć[Bey״eUfYd^ևc&gUj&s8 (UR{b zsQx(SU؀q]ΣG6<;;X;H;;ʓ A qp%70j( ` љ*V 2z۫h   p g9+?*j;;]Y:OAVqYd]ӿ ;-Q67eOf!Z 퉇>>zJI+y#eyBH JB0wۺLp+iؘ-L `J@\iwI qL0-˹4:@H {U [,I7 -غ]z ǫ-ۺ#R D 9죡I .p]bɝLG* PZl@iѳ>k镊cx˼˯Ƴ,ٵ \+(77e{h B] KafৡkK H3ȏ =&yWn `d.@Pӏ R ayMėx<V-0#B-J蹁 ^nTzu0| %_a} 2gYHk/e.? OKZC^%Z8`D`G#~ ^q~֊˽Ў`J 䡙5 4I[Ɲ ѭgN|@nnսm ZpK B٬NV,p$v sK#<6wox_ _^^o^uo^?^=Z+*P)R2T꫊IYn[ϽJ_̭H|bd@`AZ.&xQVЕ =zQP#-h\@ T#Xka vfD_(U,4HWKR'<\HJ8!rdR $)&#xC8&GDj SQ܊ÛW/>krXv%-` u)&̸ǐK.XÙ7θgWCE6]p'INK=mD.x?ƒ/nx;Ǘ77^rɟ[ݼvm[h_ ;^IcuAeTL@ Rr8`i %j[48K@6HL.h@:HF:8 & `"J&A*$5~848kFDHvJ@ZRD7r"(8$.w8K#bbiދ'=O@d=Zl ZP#R(@`zz+K<:rO?ӳ| TO525\@L&UPuaU]uY]EOs\o-׿lu!h/v{6:h8h[jm[je-ҹk  )3>Ϥ PSJT)oۑHc| fS,(Q-sb5clh\sַejeKuE=q\r#nrw|{O_E钀:r,X!2>`] g XşF2@! >ϐ:ڎp V`̆*2lK2D 4,A0)K4; Y>Y.t!r9e$)<"JR D4,&IKbRQPF/@"Q*?8,#&ED%:шDhFcEbTmG?ѐTII]4`KaTa\ Gd':P:FNPx<: UHAH j6"KĶq@-,Y;gOr6kG؆Mbِ/ 0|*V=oM~N+H$Y=/ )7+? 7١Dx/gmͻГwU V%Vtk#]]7.Qt67ѳ*|MOѣNtصI6uig[֭usvrĊ - XH4X6 ;û%oϵ41D'^5"dyX1KY&Ob뼉1`'|~ùO⫷}];OHw;y?|Q-C6O x>+>?Н;kƃ >擳 ; <; 麅YYH?d  Bܨ*d?L$A""Ԣd/(+B20= <#?0@  ľ ܾ[ tAAAx(]дSPEdDP FE|̈DH4-ZJ\KX |SeÛS:;eþӾcA.oAĔs(k]1Е]Px4x RqGGZ cGG]GG/L3C4,C0ȅ4hYdeT@xOȎtQ(GW;1Q`IS1tX`IW9 R)9TQ@XSbGGLzJ lGbHZL[\l@,tt* 7 <J˽˾˿KGDLJȄ郼L@pȴ̽BLLG|LG1LLGG@<|JX%ӏU?\?b֑(g=;5#~ޫޞ ܆a5$u#E $qLiT߉Q_C9_? Dݺ^C b(6ES"&``-b2$Ue'6hmVٚ],V*b-}6=ccp%Oq-CVD>4We#c58 Tcasëaiaųc ѹ1ZmVFX-(U8Zee4Z.`?RA-bFfbVfHcI3$VxE#9}{1N7f[_%gifd`fwyH8CuNz9727u.9k9#J怆8k@王m~RSOE܅S dӚei>TcB&` "78RI[((PG3@43~+Әj,5^F`,;k+h%.jBg8f2k4>뻮~&j}릮14KlH@kl,4&DѦ[S :@mym{ծmmֆiAFKCN>EOOFvf H0F8  (  h8h+ ^.胄 '84XY"n&o(I[F`n.^@EYN%npnP^W Р5So ooqvnlH-DodbN%Oh.NG="p"ی"\xNs*Z.?s+m䜽Y,b/V8g* s}f z: QJ(Qz.,99i ar&+@1_Iq D! !twH,/Ft4p]$YFZ = `t4 9%d^~î_PK!M_\o?hR߁r:'8dH'rdvtxd'%>da#2m`3dM>?ф(;p50ʥ2ܒaCfC̜?T *(]zbԤ9=/@+ (e^rPZ-jV!T/It-H $6LIjehTl.xR6ٟ9#9V t 8}V\ '#uIZ߅Ǟ.S\1d"W[. W,2'w,'r0ria;8b'b?/(4>t 4MKCNϣ6x3G2lݒBL#} 0k )r՗aYf2tF'Vcw֕K<ծh0A 愨Vۙѥ )'P]eRvu-  ' ]~U nQJJѥb敓/}3ǔY%]A;ˎ8F,<ћ nx8]($xVOk",' [u"'A.,D ȁ .ļ >P@|ςSt0B P2PIrng&BQ:T4v#F5uhC;aD?X#Hۀ!Fh6D0 9-M(s(*,6ҶV=[pWW-9P'r"*&l}YLhSb2uIh'`r00=Ht, ,9j.#8n0|#,Byc&< M3WH @ r6abp ^]X8 x$JN(%l:WԃA@"\g:o!]3@.b!OuB=IYسd @\ET'BųP6?A:{ͅT OD#{#F,iH}ђ4F(mHEҕԥ15m iFBR!OHE%VnĂyOR(@CH.%}d1O09:^S5 HGVH TmjrWq zE$~`6#̑UU`A]8yn,VPGДB!K2M7b9?tNF"7].sB7ҍFuJa)m)MU*S~׻va æ8H@A@T?59IjgZ!| ,i " J?V<Vj#Gүd&\%C-^7j`b Q2XUY$ ;pV`EMFVNkAfy%YÅ^_ ͺm0'lLG<4"%J1:$v"z޳=?4\z!LY m'}JGPjQiU=# D$G4uYj#2J<(EQwǽar`D*Ȉd@"}bVZhٮ$ + WB.1 B4)[ &#w@cNjER4}2+i,~iA&3i ,I `3)X>vEC' Zy~Kf|fX 3Sҳ mF;t CWtDtG+t6kajS{֓ii^ع5q5|>Y*>܊2b&xxy;X#0Á Lx .y@nL/9hDPonP(uz|q?V%dI(Xpgj6dIqs|sfIe/Jy-)E2r)*z1^/P-;oR0c.ڍ1oiy5:n3/pJAz lO1tBa0Rrp[-sE '+0Ws%'Hsp=pWov-n6k2"Ґ+ks1kcp2h眪qIXjOoװpu?M!2CW"#2 ;r!Cr$"Sr#_$G&;2'w2paZq%i)3:钒)HnMT~,ðmrqq7Lޜ[G.oF"|H-./#7"QHz`3 !7c(2)dr`ds<<7d,,G,,   #s)(bk^4FEYzGYFHyLIKZJc^݈;9~8cMc948z#OMN;c:z>>e+C?㱙2+@qA01W(2jd5?4vD.YWԩ['=uu>h]:KEuFQ(t;׳OB;~ ;=>2¦:/1~rA*TŻz%s|cC߳Wz;vdC?N&S'I% C6tC˿3pڗ:@[A : aCJR@ЖM8Rc?THrcR):C^LCAuDgC?JԉlPQ:*>keպkW_;lYgѦK=oƕ;wܐ1ׯ=so~aƅpbʍ#U͛osAǍɩJȔtƩ4CA Dө)WEE;Zs~|f9&w=+ ͔8CbU;|yѧOmhqk]}oo}o?P@m=F>Z傒xBx*m7|k-8Dj͸)( uiCC. ʺֲc!n+#L+lR">ѯ@*L)lJ.J/RL-sI"$nBXk F4tC1R6K%I b(#k*ȨJOA UT|=(K*I?UlUsXYZgUWZeX]UݬmL+NQE3MOU'?uэPښĭM4Ju)LӔ8!"r{׼R U2C,?;[?vljKNGըCjw3l5# 7g;6Q Um\]ݢQ^9S}NZiR_)ajzv,6~?v܏F.u [-Jfif9tei=Nzw!߱WX_{5s^/׼sw 6_1Xl҈8ΐivnبɻqۙA]%g qg.#z#'WI Lr3^|45tSr,TiO^?݃һnfCn))Rfh[`d0 Vx/ (:@-T mH+{ڳd@B 8 " EdSY7|bXtmGi&hqG+0=Q ^xEVz%BBv RXBP`2,$4B O`x!CM⫇d,JTfdTVҕLg4)ք;#~-"v1 %n(Ly#2 RR̗%6}+_ Q$iiL(dbCxT6en3*I Bj|*0 tgԖV)7C+> `^@!-:`5@'h< Hd(#x rpA60{&MR))gmNt"0<7t8ݱ,K;d7 Or ܈z@5XbL Thn6U=PABB*L9҈l%\X+x|Aެ ,g2X+ H$)@f3'c[FCd {< h:t7=ۿhM k-,"M- D@>8ҙ4!:T8Bԡͥ7ж4xhX5smjMTu_MW"ǧoMjekZɎ`:QC-XAp" l:3۩d<A +ta%:?M`Z Tp:PÚqv;rp;xr"דQ$=GB@0Gu "i5:VqJ*VR &:7GTB q7vUY;]_=)~vڜY2؎woBqG;رR~egӞUt!w_k{=w>C8{pG>p}Jh' N *ov0p&XZ.r/q[ '_VvF_ ^%|2;.Ɂ ž`..GJ l t"Rrv4FJ!>"a86!<H"LSC(!Cpyy?kUiW!0!* epp gp ǐOP 3ozp@0 P w`ynBޤ̃rɮZ``~o,+@J" N@ `ꠞOj,Q⠐P=.c~ȱ +ԄMG/% Aqdp~1!@.1 7a !2H!!/2#" #c##Q$M%#[r"!ˣ@'J8 6>Dq֠Δ & lvtN @p`\.+4 hi&hHQ6꣖H0=j0 0Ar)c6"@nFE1$5m$.a2K0 _2[2W3+Sa>Q. %(3Q5!Z54S3g6KOs!q#2675; s34#8)-S6Y93s[7KS36{K0\++t ?,G vQ?je ij ?7ҳC6vql5O@.Y_5v#Ij!jմ!6kIܺvjlk6mjkmϖlv#+m6lR[<˃3< KB+ʸR= * q J+JSKJqlϰl-7@SC5 fG@ (m8x8x8x}sonj'7/ ꀻi=ʑIK uo Ҍ@   ߾xr` ֠ nz/=wq.IylNXiՠ]a9eyimq9uyy}9ya'HT&ɑTʏީ+PtsˍW5>.G)NL㙝F9:`J`o8|ׇP,z-xWy_t# (-1:5z9=A:EzIMQ:UzYڢaoPu<ґ=L v@̑텫ޓL* HAf5 PL_х zz hq{|%`:z;{ ;{@#qxwr,4{9B  ظ+Nʳ$MWwUWhmqۉBJMX R {;{;{;2 A5"u[-++ֻ,ڻ߻ +D[Ei)EM)EyM|! <|!<%|)-1<5|9 ܻ)(;'-zLDsZ0u[1#"~;<{EP@InH΋ʖ|C-G{Uƙh!5<h h 3tq?wC=w~?_| )@n?q~% sc˼ϜFgd&@DRA H;wܐ1ׯ=scD#0Ŏ7b ɱƒE6T͛ocʜI0Z'=8sSL @ JѣH*U .5( ,XX * 0jA -^ \8 BQ.wkj]W([Ph%iԳ Ԯ@lP ۸oTho[h gmVȓ+_μsel\reՏco O^!rRhֈξvO "Th؎߀x " x`;T~N=ۇ TSD^A@Y`A TlU Pp8PZŦ@ ,Vs%9# L#c*.PW-Fd5FTU5 ԥ (%Pp$ Ac>RЦU X ⢌[opg\|2 -tzMJ|+ r 0ɴʦjw߅w\0jꮽ&C"*rz k*7_}V;~ _ J۶-ޞᖫ;۴d})N6K9A`@7ilY cj50܀Ss'W6TIX[{bwaE@l2Hfo2D蠅[uA='FK B 8Dt6xj+xAo7„V0v; 8$kygvۏ B -X ?w/h׍ec:b_:uN:n;{O;Klpo\go HЀќ@a*:%vE@t!b+K @ SCPi2<; e Yh_Yqo1uUU ,gK!U8T/⠂9v FNpv$x\09<@ 'l #X)4@i@3@r@, qF0S~dGeDB" tH1R,$HJN2TKq!AƤzTHpemapk&~*z@W pJl%d5%D0%I&(y QvFA VO! Jh 32`5KNܰ0RhB?D u >< +`?xEdmI(Q_}>e.eFcـa o+$ g\1+dԝMN q܇fv:c':肯x/ouD0LюV]L"(ۢ8;yWh8܋qmOɘd,q4@2yɽr,O9,#[%  k\;~K2a&4C@-sk4-e0>l,?= U$BgC͛Nxn_*T^@i['꛷+tFAF ɠqaƉW'F?z%8 9#&}JPB׫$=EX+@z5b7fO3:1& F%zx݀H`L~.a ,s̎(z.GW')2|$n9Opn0wDwSV| y4DxfxfwGuQ7,*2R P?+&[*U &G5W"-/!D8Fr2rHsBs `$|XJ ~Jj|jJ!kg&@8s12*20t(Ve2s2/"u8~0yre%%&2B40 `v(4cvNRN}Q섀` LƀOw3$ݥ9Eh`DՁoQ^D uy8yA 90qyp]a+P V Jnz^Vi/iiiB!rހsSsBwa Їbr`kl lX8#J 3 bLayJ2``(u4DoBe`qJLr '  p >`5# 9)䉓nz9k,J0xLGTIQuPx~y8*Q $kt |z<؃!b_U_͓_)____h}`S]yk_AVhYlQk6lQ`|!}XAKY$\W`iYp2HAz񙆑{a%9> Z ȃ3,FueX7px9gSx\:"uqg'5DpFp6Djk6C'zA@DuTRTNUMuI UTIV P(!`ؙ=KGDq~F00.+K5j'}2qC[ ”zw6A'@,6@ L0B`:ʀ:Q:m#;Ee9d8`Se8Hzʱ9@'pEB8TGGDzʧ*)SuTrUi !TFFʉ wפae*wR~ Sb*P*&S,bhPZS15,NJj+>بں٘ŭ*_5W^ꪢҷs'nKd9ZzB%YHX6+!X/Шb9lKw]wK9 Zұ馯Eu AXKȲ1sJ.M{f [<۳DqJ<곤df &˱뉨N '{i{z W{[\{/ѨP= *l۶nۉu~ՂR{е|k WwTVhG({r-{5k 9/>{۹;[{ۺkG[6(I;rZi kP[{؛ڻ۽;[{蛾껾[`[h+H{ڿ鿎 *=ڨU <\| "<rO[{,®35ZZ4|ÔJ!:f 5Bֆ9GnIJLX!G ` \ ۜ>^~ꨞ^ԽҪ>N~ x]B'>؆-&iێ` P>^~؞ھ 6 ^~n2[uھ ?_ q  -Ɲ"!5W 4_0.:#>M@?F/l 1/׫ Ȁ p pXZ^~ [_P P 0`&M @ҰfzK|Z R- xo!?E. LYL 0{@_ϵmS /s @8ʿ?.<͠ 7_(^߯/O]o`oJ $XA .dC%NXE5n17w!Is'3R%K)Ot9=+o椉Ο3yQI.eSQNZ*C"I3hM^ɓkX- -z[qΥ[]$c/~,ӝ.p~ &qcÌG~9˕;rmۼ͉wŏ'_yկgJq_~ݽ$u&ɒZ+P@PA<38cB +B 3pC;CCq?BSTq0~|ќcLieFuF{ps$GklWKBrJ* MtrK.M/1 LLL1lA/K:N<+2N>ܲE㈓NP C.9CEϫRL3=)LQ++3PLU,#53PtS\sUNkWZ<|X%XBk-[wZlBà,6$:J5l@4dm݄nƐ p0GCOa3:`;8!w^;$o\#ϐ 9X8l8=w .8?֍M[ͺA(< R1! r4p0Caȁ6 ?π+TԲ e0K٢`CMA`H a U P!,8hP5!`BB0 5A J,@rb'B $HA*QA&9zH;$d\0jU"H`*BzHjD8@ncbF acL"6 horZĐx|grpJu@ Blѫ32 GY ,Ѷ*8]"itZm'B.9!}`!8!SFOM( cQ_4 Ҳ+A#:ׁu.sO"@ 63)3^A>?`0?4v;L':I.ZgsNM,`jSF@rh.y.! fˡaA} ѡhF1Jap>đC(9i֕K )xSoT9d$XG:6$  Uy\X@60V+@`фR(C0<.@k8, V+K,f˛Cpe3 Z;:Ĭf7!QNg]|^#b A@v03{PmOp BnT;hM>  JuM/+Vqb Ш45tN#]F@նEn=V,X€s+a,'^3!8wk uW;wq\gYx!uk rev#qc CtBV9X˲:.7\'8/f),1] Xn1ul6~Q&1deAb1VZqk+d%X72o3M,o"循4y&] bf7T{CƻaC^]'ൕhFXxfzk껀hWz´y:JNN у_@pV[ni,e/7操ey "EԳlз]ǾVp bY?XzC7M:,x3GA?I%`A tkks OZ]2cpj !6p:?4 wrPˌ )uPPCgO $A^mz;C{冴my{wq7m׃ 8`Ƅ^cv} 9Ag,oN&SJpz@s5'& k5[h99|9 .! +{c@w7{ /rA|[ xI 0lA#X5%5&5a15 $B*}$[)Ea|ZE^L]ax9<4FI4 dEmkXAi IsZLVh凭4LW5LDMbpzQK(gaWQHvXMXcצq(؄?pWN`l0k RYEExM-}Eb=5YaׁbXX-dXX7H8YN  RzQeGMH U{'0ZM0XY$[[Y\Et@>[#[9&[@[[h0dpq8[(5p)۱}:0܇X8]|ȇP%X8ϕ]INU Nm2~P& 3@݄x{9h=&EaPrp ^/iH. #%|(`)݁`P%0qp1|u9ZP%p8 5؀(Nsip^40]3851ww=TTLz51z{H%sPPpM p[xP9@˦HM_>ɇzNԅr(Bv"P"@dz =8{-~8Ld3.VM $݆ }l8'@EWf"H8#vVqQSN뢇C09 ~ゅ 0]8!4_2=P%:pzD`fH1 ;g0X*{F:\Xg7UZPzTuVZ-kHh2@RƈsfPA^O9fs u8z jX qd8qPhfYfp8gDW]S.M`\͇`XgDݳ%j=ل]`|upz.TsHZvafjUlRmSrІ}i>f18f0XDž3dՇ$&mnF(gF@mQ0^^miN&uq8i@idTpnir8jTȇ=dȇ3H20N.j @oPj0sI>)@@sP]9nu`- ~2|j#(n-[]lbf/%dmVTJ>;Ĝq\h*o5fcwtv1"(F\XP~hgIރ bKfNgj8 D8wGQtXdF؀ Ё6J⒵ iqP\T>.ToH&azQ`H=fa x=TІ'{0}`KV@5a8Fp`Mh|86hi=^,Vkӿ56ja|8-.}FcD6\H@h ddy~TKl]ue:~s:"zn'`r߀@?}Php޺c"IQX}/cRIlXuYztST%0OigmqFJz0{`4H'`#p]`C{w^Fu?+j,ngg0Ʀv^0w\^0͆z9`~_,cNfZEpUۆ0Z#GX_ݹeꝆ 9~ J>(z_x}l 8m^.铮TPX0@-t5D&`3vĈ'R^QAwċTڴq ljT޴ &PM1Pb: Azq9 K Kr2V8qT8 k ΍rB2Pw "cĕ6:w*M',J[o 1"?f0BLƔwmS"1j4ԪWn5cnqC^vNgn|wnÑ?^\8Й+[ݶj`s;x…cFaI  #q O,>0 @7TDbDUE0AJs: TaB$ԅT.CT Cd RWE'I*$23F90A4"(E+LW {EO=&RJ1TR`O>lc1]fH6Q=C sTAٜ3U 2/ŒFO9S}rC*%Y:{pAi+Z$m=ol۬VKFx7;.3 w{aE)=[律 ppq0pA(!c!8 V%NCxHr(#?ۘqpAQN |3 hQ&16=s*K1 6*jRbeOKTE4u3hNZkbR\ PF7x3DjDs3} 8@L0- JW‰iTr6X>t{9ЖN>:خnm3ێ~~;sٓA{XE*lQ5"P0:G%R ;+p#lxZ˿Dړ6q#|cL(r B(@2"؃ vJBт"9FOPKvv3$ H攪8\0XB <)EЀi&2Qw#U808N 3^q+殖115s#56g9it̑:u2[$;b!P>6 ay@B@Eǽ! PסO-zAɡ! Y*VS0!T&DO"<&2qEp`hরt D|JRDl0QDb%)Zk Ogs.qboyu [RC)=&a2a:6@CX1rY 3F~vlU0kJ+L ?1qߠ$>'*(.ri rӰI؛̳o ΘĬg|`I8YA)``J:tw6rrWAZ4Ùb * @t{Go~7m/Vۄ|j΁ #D C*8!'<(@70qd1l#!ffG(0?hB)`Uz>7B1ূ04_Cw8d|c 2xɫq?);_Cs0GOI<m"*:4Q=x=RP<&,5,;=<ޝW\0IA= 8(_?7@?=C8"iMTO͔O)!:aM UVaE$ yPVEyAD2;1YG912}JĈ19 ydi0. ^D[FDC8hC8!E$bq6haj8QЀ!ݠ'҃a &"J"""C/D'b2(?^Qu 1Qi0F4^LB (5n#7rJ븎N88cηX^7##}998@@>K>&BF`XDc=BN$ENz3&c3*1.ci$H3QEI67PI$Kd^JAO=aL"$Q$PP%Q>}9}I!^!%%UVU^%VM[UWJe}WeYY>۶yBf[%\eQRB%S&S֥^*RR^[%``7Nd֤LM6&cPQ!aN&eVe0~dFnfv&2rge&iiL$:$kdkkd;&m֦mDcl&odo#pA&qqff~gzr6's>qN'uVP bvf'w*wdOJu'y>%_ez:%{6{Je噧|'}%LZ[Wb۵X'Yg~Yg?_'&z'^BhTN{^(fEKL~gbdw$xFOj(FrB9gG苺uv@樎h0k&? ?p(&oN:)&@(^iyrnF~iHJ')'v(b臮ihƩfJ]R]^楅Ω)r (g*%j[Q%>*e'i_Nen* 噊(icjgxhRvr))h.*<2)*jԑ*k4jhl+Fjrzꑴvan'jj귦ѤF()bW+l&*Z+k6,^ҫ"N,k|>+R~,EP^뫎,:(Ȯ"^ ,l,άv˳ޫf+цiʒ6ZblFm!:-vkVz:lú+|l֎-+*hڂ-rzkmنԒԎk*.b▬("ʩ,.klVvnV.jnZ.l֬*.Vk㲮f*B-V--*lm-ijmmYN/ /v/}. nv^|ɺ"mt.-oln k/',//&m뺯$0&ŎoOFn/0e&/.*6o, p nj0݊0 0\NS튯Ka.2*1Soco6nFqqf.1qI00qǪ - c%1 0>/!3*1"'d7so"G6qjK&Oc#q1&fk2+)K1,ok+ur۲/%k [m&22#"103$'4 ,r J4w35ss3qƧ7s[2;d3< .r<3jrs*C1W>t?2@*4C<30={2sCWkn[Gz8;2#94JQ5_ [3 c6pJ1Ö4N?I 4M74F:W;sOC2D'5tQ7utBG5B#\?rHVf**Sx*q+*30,1e3GY5;:8;;Yy;x;FIIKJHK[fNe{NwO*QC.TTW#WW[[*b3&cdece*fg;.g@hilF6mstvtL@tpguP)uv~wx1yXJy];{}H}^S~~Ղc[؄ń円w9lcٌnMtiIUh;{u`)ɗ՗ߘ{Y䤪zP[vl)׬x;Ų絧lI绱üUz»»ĶóøĉFĔ\ƌSǜdɷʻ˷˻âɬЬѽҪwƢƪˬͰԢeʥгŞںšӴۼĐͪɢȞͰҭ׹ƥȝṢ̂ӲرΩܻ仇ЦҪخÌ͠յѦԪֺزٮڹիݼٮܰ!!ICCRGBG1012applmntrRGB XYZ  acspAPPL-appldescPbdscmBcprtwtptrXYZgXYZbXYZrTRC aarg $ vcgt DndinX>chad,mmod(bTRC gTRC aabg $ aagg $ descDisplaymluc nlNLdaDKplPLenUS,nbNO>frFRPptBRfptPT~zhCN esESjaJPruRU$svSEzhTWdeDEfiFIitIT"koKR 6Kleuren-LCDLCD-farveskrmKolor LCDColor LCDFarge-LCDLCD couleurLCD ColoridoLCD a Cores_ir LCDLCD color000 LCD&25B=>9 -48A?;59Frg-LCD_irmfvoy:VhFarb-LCDVri-LCDLCD colori LCDtextCopyright Apple, Inc., 2012XYZ RXYZ o19cXYZ `jXYZ &2ɗcurv #(-26;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmparaff Y vcgtJ*ntIGm|>  K\a !d"$N%'/()+I,-/C013 4B5s6789;<+=9>E?R@cAsBCDEFGHJK,LCM[NiOvPQRSTUVWXYZ[\]^_`abcdzeffQg;h$iijklmnpoSp7qqrstuvfwIx+y yz{|}~wqrz߇ >?@ABCDEFGHwIhJYKHL8M'NOOPQRSTUVsW^XIY3Z[[\]^_`oaTb4c cdeffgj?2?@AB]C*CDEF[G'GHIJVK!KLMNLOOPQsR;SSTUZV VWXuY;ZZ[\S]]^_]``ab]ccdeVffghEiijvk1klmbnnopNqqrzs3stu^vvwx?xyzi{ {|}T~~yf\Y[euƍՎ"0:CJQYboˠ%yЦ'~֪-ۮ1޲6߶3ܺ3 ?@@ABCDEoFXGBH/IJKKLMNOPQRSTV WX'Y9ZM[c\{]^_`bc3dYefghikl!m*n.o-p)q"rs stvwMxyz|E}~M4I㉇!@X=IwŞ/DYiv~Ā}vl`΋и!]ٟ1+G{fp.Qu 2[:`-Y <k1dL M%r  l # J +  "@mA-0HF !"s#F$$%&'()*+,-./01245:6c789;%<_=>@AdBCEEFGIJ7KbLMNOPRST*U@VYWsXYZ[]^=_f`abd#eXfghj4kilmop2q`rstvw7x`yz{}~I}#]؈Y-xƑo̕0 'ǡi Z_ðx/赧i-򼺾LŰ|JUz֋S&rS'>_Ap2f"R!X>z5vO;D  p L 7 B Y ~O>S LrA k!T"A#2$'%&'( )+*9+K,a-|./023<4r56859:<(=>@LAC+DF!GI)JLDMOpQ RT[V WYv[4\^`b^d6f gikIlnyoqtrtPuwxry{:|~ xX̅B4=ɎY~/><Lߤ*wŨfdp˴(QW+Õj@ʫshҬ#Wׂب+5:>@@>:71*"w5Y f\BU'^/dsf32 B&lmmod,"H*\ȰÇ#JHŋ3jȱǏ CIIєYɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴSh2FիXjʵWdŠ2lY%hӪUb۷pBݻx݋W߿S0LÈ#^˸1Z:vlˊ3oȠO{Ӧs̚mh[kF:m֑{ Mx[t)#K]УKmX뿨_KKoEhpY[_g`2%K_]|(8`> ߃r̈́Vhf!7t ~<$h(;!_90¨z}ڌ<1)Db %HHTiXf%7 #bif=dly`=^%~{0FiN8Ξc::cϠ|}NJz)>:79L6ٰM6̐" )sڟ &(},Hkk!Gα&$2 Kcֳ"f{M9cڦHJkeJe[ ioWx =>‡yV') <o|3 :0 3H$l򧫽.6!sG>s: <'y:RM\sϿͯd[s~K矠½0 Jsclqz7߀#)j3L:fu^.U#8^#tҤ++z.݄7kժ55ݦc]6}mߙ'>>{^oO8\=*)3~ ;4x8??' UvE.xSx׻dYF)d.h#<sr4*k'4x^3^D/Fx71p_QO,x\ QJ"( F/V_ `H@iB!Msh. kt~t@w >+!ڡ1w-,^|da T"M" d˘B JrpJ)ELbE(1Zh0ZQI‹29(`O:d8HeY-p\aHGD@!m]k4D^y2S##)vj (EPZ4HYTBX$Bb.[f0% beՓ Ql[ P8Mk18Q:BFLϴNveF-9 bs4Fu|*zGT~(BghzcX\+.)H ԓ6sY QKw:"qY4iҢv 'cԮQ2*jRSӧ1EI@jW7ZJ+(,Z9T5XZף =p+%Z'WƤ(Sz)%uږa8ɮ?Oznd3Npx SQe(xK%%ըz곾‡9ONӿ,/:V\Ǭ/ :a %Fiq#i;&xȂNJ]&kuD1v&dԱ" }aʫ PQYyָlE{)KU>uO9eV~LJ0i)\SwRHgf~-3 bc-RSg&+mm#w%&)=(dhwÎdZ5xrd)iUǏ,w##"-Xyze3ait\tVX\FjE6Ƞ 9WFșO_H5xs|BF}G0FHi jG9:<*$Fڤ'3N3?g ٛ8Fٞq!EpJT3UD1!s _[Tjk[e8k(+5J9 8lj!E[-䧥2zwrƟ؇f'r?2j 7sI+fs:4Z=UjP`ЮEeax+GB Y!ɪzzy?CZZ=C;׈ %_cL:I0:_a{/e( >2{Oç) 6x}w*+}dZ3Z B ;4Ԍ9èj9N]4j.RRu(/XIFv mG桄BBeCef`E;'VH[fwԣqY~YK5_khI"A%h;M<,11'AD )IkDx@QD'&IHg]%kMIF  !`B{˵>{dw2ARc$7:+<в=P`c 2 oo\'K+Zr!HT׻ ~ v g K,K!^ jlil;BW׶;9i0{! sMUśVDټYɸ[{R)#tcw4{85 5,@;S4 3HUF,IH<F֡Էd`.xG/`Lcv{5/4:q_[2|4k,iq\:'Vo Ѻi;ThC)YHH)xrOtOC[t:Qy#]2CiaʧLZtL%U!,3X[qZ9 ݛi|a_5 "^X(jWF)zZq}fҼ:gdIkTH›[&q|4򌱃δ,WÛ0Ңhkm MRȉݮzkiZYѦa$HHެ)͹T;oT;s^bǨ\5|;4~,ʕ.vxH+ٶ}M 5 .[_wͬϱ'eJOmwπ} =&Շ͸1-4ArR;Xvtlt'0{=ܴy=B34b>  ؝ؗȑ09 ɏlh _ -0cֆ57u~B׀׹=hiYw.sel.]ӂl{*L#]JۗMR=_+T|7O Ck!.V P P ᙼF ]H 9zηۯ {Նe$Y| %Td >%ˤ:]*wk' 6Z)UCޝ݁)} on(ѷo@ 0 0pyfz+-n$;g> dܭgN%"kH>s ֖M.:!R Z\.$HEM).! j+U| 2/HYKy-h+=!Wڴ"{(a^N>׌Ȃmܛih`cBfwE֎ʌ9ˎm޾ދ::nޖPv6ܢ{#9#g!JsZxxٙ` z;:( =.M va?ZSz9=ANk#M..mTW9 DEnhPe(ՙ b *Ё.->dݫ U [$q߾h/]-guۚۆk] P,my4h=?ռ; )_[Փ [R>%0TGŒ9BsҠFnU]9تӣ/:%$XAu.d!B%FWE5n .i[Is+y2\~3R%˚Rl2.gAu6nѡA"%(B:Z*nWv׬Ď%[Xvحe[m<[]`=6[xۖDT' W1. ℉r>f,cziҎvF0*oq'ǐJ÷\kduϯ;d]:7~Gϓ7Ԛj&I5}x_K_o j@H2kkA߂z0PBF1p(źfͲ0-8? G G<)_)4IypLI)Ifj: >. j.ҙaRttFNv$Q3L3 ++m'6&+-+0тDP5,m@?TNzSTS=Պr|\2 p©(a[kuWg&sq9b}6iuZi=k@y%V$h7=KԲHų0 ;Uݻ4S}4.xvPELG#hqP.t@z?]w [-]V5Se uk%*lʟGv$SL02L3FO.#^:߽}ahė- NkOD6^!xQ탨Nޗ߇cjJ:|{87&zǼwH$nϮqA*@ hv=S2G툑1mж5.v0˸ƨv&FKS$A Coz1_p:It(&rJ_vgq̤z)Gәp' Ţ|zWQ^WT:OAc˼F ^VfFQac0XHt\ۉg)o,͋4e,otW'pP Ž03C?O!C18a/aN"dq\# kX P6Ղ":q'Sq Vm,$7Ńʑ$:ڑ3:agؕ^lWsC3.+7Hý%* tvPv'4yxEgH]QE[zL`L+1)*p.$A g!\UAPc"4<~ }&)87j`dĐ' ƃ6䬍Nb5uH5/Ą$:Jf 9IO 9D:xR~dN] cArsJ7 暊"3 ;iOi RH'ێecI<n-BK`B8 졵Ok!'ɟ;R$8-UѬ 4`-C!l/ rq/K9@LXa &n WXKôqٸ))P/ W(K CT[ { '䩭ZlAP䭵lȅA):ІYB>'LK(+doR<¬n?1*@4L5%Vg]~ɚ=7!ۺM?7eqa0}˷0ݷINL ET<`;-(ɱN$M]H%MOSʓ&i:OL`9#&/ۃ9dYuT15E90R*ʆRh2 >xd_9OX/9;RPc5ͤřKM8`Pb}v߆ړ]_194>?q؂<)=.34 n*?A)hNc:o: U/VV#oeQx|Ρ$X~W2U=@<ƻ^vb%Ri Cȹ+(0%Xtc8vO2EC8C6HTA0SW=ZLe3RX+dj)gE. ٓ=+inpLiDgUljiXKx_xjCR HVhGHnCn*HDGF99?RX_?@lŦ=.Ҕ4mC]^ms^JhENrlrÅoĿ;4˖zK?K5U+0=@)`DU4-&Ōb2Bv`E&oS%?xdri޻TT 5 GԆ֭CAS<|WP2 /f91# ^U-H58xR&)X΍no]WpK1!xĽE%Ref{\?pߝ!l݊ZdH\ K`pZ|-Fh$K`I<{-8TqSSk+OEHe`+r``rKYj<9D[/%oVzt<"/@V=%MOWQq-#m*L?U@)Ip%uީ$ `E?X\oErRBkvc.V4W ;O5(dd( LC>ZSh5$ռ-؂+0QK`vvW֎=fk7@;THܭZ8*EZR0|vO"υd`_OlBoF)vԐC@xރA=`pChT81WLN`g `z)Ќ?zT? Hm+qHpMG@- y|%%^Bu/3ނ~nnUxk][c\/ ]qP xPSKvh~v"e[Қ3}dX#mgK'<)#(n[PvG!tzmx̂~_O%xm]rR2J\|d2/~M W{#O)HEkC(U8рofMzъ Vz,X„ 2dx 4]sfqEg9E9-'N$NjUTieF(IIg09J%uB-j(RmJq'Qj*VD"j9nZ@}!\9uҸBL)\mpڨRW-)^+^!Q[JZ_D5:lSHQhn\ ~y 8. eG+u6nxSQS\[Y25%L3Gڸ Q:1tr۩jf꣬e!Ђ $#+Ql{s G#,svK7jwPq[ծR*U૸F0}nN6 5;Hgśޣϡj pp=ATPr@c2-PAf$i&& yc؁BwmCtd\Rpa+/fW 9“*Q\A0c A ZNU`XfW QABxd )l _k9lcІ)Xg! ;X Du{CVk* 3er)]@TP3UIlyBԥ6LS"nT19\Rt P"%IK .P^JӊwSR)n|J385.B.5-`)CpR8C~J):E[4_vl&]'1U̡X/B>4R#&v?, ~AΩXA&d%,1\Ⱥj󈲠?g4l3T׿Xd8qw_7Ci(ԆQ{"}5֠ۤB-A$w9Py|WLXUA@ZO_W]ZUFa嵀A.dбFM ]^ H*]gF3uŅR*\x^0` %$PPQ6!#C<@:tA0MKYD/C:PJPbq"Ey$b.. ](34C ZJ!2:d$!F&pbER-\xa ~݆q XiNİ T"@jHA]H\2$B OIۍ%T2IC0@9U!S # 4J `c~Ad=!B h"AaH lzֵ4b!V"PC>0#> $JtE8 C+= K4WRRt.h)/L%UN.\%*^eHeHq-`ISnA  Bz 㧍".}QS~ ^ ]5F^袇W(w$A-!#ΣdN-B'͓]!\ qlA@J*Hx <<ԛRP$b!hԢe϶xq!&^Ђ!A&cBHʐA 8LƤͤMd@N>D$JL$=XC5T0 EڂREbTB1TeUN(U . ZH Ü|@%V_Al[^BĨ+`""u_+9#$:Nܩ!4!)X)d\PgLUl H%@B)h"XmPa/VEb'lKc-̞r RR\%职A)%iGܩ@Ε \.*dhZ0{F%ZM(^>ư$I:Aw6K'}֧}V'R'$OgŐ(:+9X% +B+`h14/<N%>FlRl4d..xo(p\Nƨ" %*܂%lZ-`U`̊!* [X)D\呕҂%)N-d6h!DB.\EϾ6 \\.5ҊApR@l͆2H&-4"Z0*< аeNJLwնD-zJ fdtpt Bֺ0kf$CQU^ !xnniA-"n]ђzkp+Vk ΫOV:%2&0E!TEX)s0d`C2C2xm$+6hdK0N A%iB  rL.c qR a74*<11h.Wg%pK m[gE'j0+1 qg1k1- ږ}S~NT/ $:RC(;X5t9(RN3>C12F+2>>C4`e((PBQyz1VNzkH!R2>2F#ðJ`K%@3292niпiP3ܺ)mHf`5nka=^kЌ#{$SrJ33lCYJ2Ţ4UJ?5tU$( /7 0LYQ9 C=(u;Wd)sΔ_x R ř "up=LC5Q}EEFOꟿʂ;0+B6O\P*,JNJ,,[,(4t(l#%OUPu6^o<(Rㆷ&D5 0Ayl5^LZã>d6Y>ԃlX7345t06ܝ pulwh5`k`vv$X)v;|/(d%KE]8xnuwS1SU(bw%+{%9$t3BH'}0XvŞWLsvKS%(W%,T04%LBiSB8: Wun̸Y¯|, +H"ǽ$)ʹ&KCM5\na1GW^1wP`18^~)fM6LEbN;W@Z!%ݛu :2Gi.x$$ Q+*)H C*IPDRLEU%],ҤBFk ҡZ12JY̎TzajOKm*qhӒ6,MEt|{e3c9.94YmgJ|yt_𼬎 D-3FҿvT6?KkH,'M;t*JK4SRpD>֦\,dŐ֍t?]_`9ckغBH1ܦ٤BFsjp/t3DM7CD?|_, 'yGaB%U.u CԮ(}pRܡ= c*GQeE2a+lYu8.GA=h%P9;"jE(&RO-ԒaF>iL{ͅ<ųt?x#T}gkmo0Yo g8*I#\G]&GBfeUwf ,7ٽvcPHNH-&7mr8gLDqҴ_MЕf[")w (C#J\9'JF(>q\8"rD7⢯9wyo5TD[3Ms E'9+&6gadBG :9L2y<ɱ PYuTt-?AjP#1퓠kg*JҔW.OY lS( 6e5 a,2LfR2LEDĝ3Ѫn֊x?G@qjZaD¸ThܭZjFϒHB(P6amx^hbL;tFny@h-ajϪ宍TZGXu0ҏcL@]!xu9Ki[A7*_$#>.HUqHqd) U`8'ft\ʴϏ14cP66l-J[1,LQ !qjC_ T66(xZ^sI,۵.`~9Eh9xCl+ph y3\+@*K7l3 ڢ3#-f5GmzӦLm K*"N cm5> YzV38./67r`~v jLq;͈FV!5mM2#_F)i 1]lX?tB]ny7in'ݸ<16baL=KH BjZ Kv`>)+k)>\x3_Ȧsmu tOHNhFe sCXyugvF?:veN7͍ka R%&5F(RcEkVoc)O"r7\E~vF;h;3٭{96o9#р7C>u)̣HܮJxN/LOx$j\/d$M`>abbAD/3 < 8֪vt2Θ$(@%Rb0#&nIÌo&]HFh(R ZFp!&9(azVʢdjLP 0pj_Tjq~j:?NXn:9Y"NL0Ѷ0PVB  Wھ9ǸDuHEXO 0θ =%bp;PcMMN zJPAPQA-kHi*26y#0qvjoF%) bw"x.Ox }'a⁡q"pc\Mlo?A \С6N27Za&/'/辎KY&̀4Hr(D䴏 @&+CTrE"*ߑ,"r|EFxA/#E/uq<2x$"An378JM: !LЁ!n2ʰ$}h(_e,6"G*>4oC:o+2 *)0o"0X|m|R)п&"HCj6ଡ7 'g `1j4 >;p>'n$Hr,OL)513x3Q71+7e70.W"V?d,nҒqnS8!#!UK.!sC /W;J43<6S<'aei]cmȅ$(33,>TGJ4 Q1AlAX"AT% &fz061TvBk$MόV23`VGREGDfFFF%.S0050(DMc&|&92T53C50AN4M4BrĨ$5 ˍt¸,*sL +/ sLU8'i"`aN)bPNPݣl ȓ$[`]mA`!hݐU HhtQt tp (aX`v"l­`*UtތCiS+mbW-4Nҝ<< (GwNo3PIp"Xp wNkf .g.gc|u *h^viñ;KYUHk hw6ad4*b##x-vxͪFQ@kv=GyAvm$2/䲀Ub)p $C8e7"fɵ$U\L]Uqoh6pWiy'&5_8 `kuWdW~2BmWlXb6a1SnrwcV$¸%~ww 촌 RbʄozSY\CM"n0Qm|5>aN22C}s!AT)TG2KU3jqxv_ F–:)u6Y.Ԍ )?"-'YXSy7StX8wt^98Eۍ{1f<5ng|2.G/(nsJ>չIYzyKܦHl5e"lN0|7c-X!m)vN- Zdq#\dX*PJf3rY]qY!@'YJIߙzZ*VyL?[9AeFHڲpsYtxwc38#d*MOnoX Paq.m!B2yP8~>ٓCP*> x@t` A@:35sH۴%ڟ""F%\;1"8n*p{-:_ZY)Z1y5:'<:Z;$co Z}N\A^ͮ(8ur; aC`|[zU\EL$⩇|[Ŵ8'ᶷڠrmNEiqӹeyf7yo;V'5~6ʣ;:)FȊo" <1iͅ(@d-fXsNc'tt@LѺs[G0Yxuz`{6 fQ3 sE:~ava- aN!uQwtM!uCM!*3)R~&; L|ͩbœl^Fb:e5d!\5l3<7MX!"%',W8$t &vk\״X:??AªUZEﴱ8)*a{)…ʞzlу~:dr2L!F 5^-E~]bD0f3F$c/ :|-ĉܺY.j2Hmۇ,[c7r$ƙ4g2'˛kfIvϠB_d&ȞY68> )ׇ)=k&SlE:$+ع-7޽z l|| Xݥ.et2̚d\ <ڤVbsI$2˘Zx;&1[x;ژP(\c̰?YdUum"D;^,g P] d*W0ɞզJ- vHK_WTe5t# T1XPeT<+I5 W DUvSM_%Z*8VMאJpg\vAhXa(a9_5fP&eQ g/bjҊjƜ"-Ƌ`Yo,[pȢqL.b/T=wAD\f|`G&m|4M IC̠@a,HԁQ}aJT-h@+|25|'䔊@Aj5qaNE#~QylH\يT!;n{?hߎ׊%ԩW5 W.4X ɘcSy9!KÀgġK+@3qcj1k4`ycB %Gy,"g$)/ AB@:' 6_y2*@y! @K,TߝjucM!M"@+^3Kd!!J,T0qwS굮6zw÷9O>nnvK]LSDn.P=OI)K:Q 3(]w`1bQ 9όhTc%ۼibaQ`#!5s?Ґ./|(@ )N (lDxi#;,"l> ϥmmmnqV| `;a(G3FH 2r ,)0얆fHM*8ԡ7uMlh!B\CI"䐇J]R(./bsT'.pO~ZŠ$B04g,D:Sb)$Pc,ǝ魈(j;THk v&ْXr%e:Y5Ko Je԰AN@J|C=&_9&Η>qD$䩵^g$&3B,YNa7u|i=#g9t:Hg;sU=E,j1YLE.#0X5³9Ƃd%jeA ®Y|Ӡ9|˪MoJ#D'tz?cC-*@dcG@K#܈j-萯``.ׇy;\Xآ#z3$",dA)$bLٕp:3TUEd,\ptgcU /dUx]f$}v xsNJ7&ű0٘5:#1[".?yin3P\j qI'q{Z2w('܍]3w x79ś4v<zd5; Z k<õrρ( 6 ` ub,K 6,ʼbc8JָIjzc[r1=JT*Ev/ t _<;vtc@V4g'U~qUp B5}Fcea>$ &ngw?vewIeqht~U^ P^|%VMrxX8Bvay#AKWba'QNA6yZ&Dxzu]4Y')YS E1ÀG bZ5Zm;Z5 & %4VqAOÅH4}(fw~64wGo@2#1xB/nY ǐ ȗwC&iH r֑xpuOOO4`P$3I `Q!Q EQQ%0 #u ӐQJtI).%X {a !ITR^|nl7dpj~l4 5 zp w ymAxp#9bLRUy{%/Ii20tJdY:CyYV[4 3{pG v{Z9ٓ<@I3[H)U!`pS+#p]JpI+Q =($ EJQn,`eA 561L!@+Jٵ*㖊 w.j&P"iYw_im:ql=z>DZ1bh;H@GPócc2v<lLw"Xx8Y#W"*- :3yFF:q&*Ưj3 {d&6X9xk;x%E8F`M{5M4 woITivip٭]z9eEa@±BriDNfEsI0W`0 iG-G{>wC'|H|KF.jQGβu7U;W" @-N1f3eO!z 8qLkxruʧ:򧉉/CmEzXO櫇5#KN䴩+A0{'Cu{`{'@giK[mo 79)?kW2""O@*Wڲz¸ AWE&ȊE(Rȼ~c}[zM[@ jjj(!k k&:k 7$Ӱ @=t|6kKCo[mq2.8dWP߱^gK[xKk9\t4{ +DĨqD;=sÚ::ΰ 3ڊ3bÀ𡵈%j [k{*c0<&+kx;q8)l"*OI-7p5 Xł'J/2_ `P(Qȋ qŕ _<p_|UY Xr!2zG(JjZ_CG9ī7-N!H#C9G|yۂȉOs>V۸l\w44׹ɇg R/3+˳l 00p@ u lk: UGc{T .slBx FM(!7@  xX"ܘL8Li*_ I|֑\ :L`@Dϩˀ˷\ ˽\؀] L{TP.2(Z(ZxcٱErWw=Wx-L W $^-\*ڠLk=1 ,wx>t#t "1i|\ ݃ڽܝm pƎ1m1G2@2: s~|>}͠ٶȡK߯m˄-KeQLOb Ag qfMbW-qʯ qԽXRqJ"G R̼rmxNz_$N-}>jxPS20 D~HKnQTN3I."T#>p%۔پ~@Wz{Y>bLoA܂Nq8rڷ>r8-tc#0H B C>4^ꣀ% H> A{P ^RłpBm(ΏQnM)_`=͎daW-!NRڍ[-!:M.^w/36nE4nG0> g< 鯐 CRKNU>]yčʆqMaԶj;;w,t.r>Ⱦ1_Bija "I_I+QC7GE^p﯐N0 `F[ WnW-Bq!_3V1+t/\ gОq qäLt3Ϡ d zKw>%؆MYġ0 :  I^zB S5TDX'HPA<~Rd9$ED$7m-]TSL1ջ N=} O(P<]DZRR/dTU!MV]bUw*Vܺ:lYiNn+ӐOF-S]J`Qu+tbƋ"0L-%[Ν.R2eL%o8\#F֯cfZWEKm0.?dTZŪx*VjịɟiE|8TeGyYo̫ԪD- +D+Ͷ! O*?˰0 뉾ND 2Q+iҪPgfg#<ҙav&']&hI!lP hGaD2CeDC3LE;zs)38?o?C%l&QG]F@kv 17e4:TsbC;#oΘt4M #H"wu]Hkpqr*qdq%K-'r9B(^EKA [FXCV5UU2=kn($,,BP7®ʭx`Ii$dBq/4CEՖ0Nx] 3S{guKWqFYZWv :vK1{I YdEBLA=W,]uSfUw9=C\bڗfMQht*i2pa8aUW߃+֋C2=RGVUCD0XeKUL]Hbyvugvi֙W!YklކkEo骯V-CV}s8=%V' Lz |_ީR թPDAig0Ueُ*aH?. >"k["u)ެ79.ux]T'_PvrW;Kw+&1`>Lh.@$#rlRJ$DXpz ? Ts.;c"x=LA#O3|ĠOeL6jP* 醄$P.)I4+1%)AJP*,)6JuF+ZQ_B)-^Fk)o@-F[ U@i4/V̕.huΦ"U<Nj#:qꖩ~LWz)b &Hp+*&s$UuꔚM+@T<2Z0e^D;>ER4d@vax^b$APPzÁt*J@S1 x^@%Y&jt|RbILx_Yv%xP`JURhu=+sVҖ%/Jեɿd` Ĥ,q5`+p4"(|O! - aNXJ"O:CbX;/Ҋع)Zo<0<$ r(Ⱦ2Xt{.w?V {ʷp ̓+J!Pr!Y!%ySBB&\c_Q!z" _0 ` @92)Hxks;K+X-iJ3:vXC)b.8lDh7"l>Ѓ1"K Np1p>)" DzPTEDpD'"d@v໪ pAعn)r2)ҿr#h; :D kY#ªYk +)0#)[讟AqAҼ{AA[("BG'$RB $)\Bݛ xG8.Ȁ2 9p) 0p Fmp?|)=>:?do m8O`mHɑI"L<:{[F"PI>z(@? …F ؊+X@sQ0.D*W tYKjscڛ # mzG,"GU(h8%ߛG[.G]M%ֈ֣{=֔Y)K]ƒ|7xC(+΀@Os Mô`8h?lg>>âc[[dNL{N˫dDЄ4PNT@zEMcKJ'ҪS.l4aPNC:8, FËj3 AL #/xLs, .o x M](88 R8ZȐzMzˀ/ CD;pJ|fXf(?">N& hmSSnx**?)zL2%u8En8IRT8=95@N&*Pv WsEr]W .z>@*P@J=Iտ3,sMuaTJHŇS=W fcЯT٣ȇP.P-&#jY39U H[LU] ^ MNvvZeڐ۰*0ӯⰆ[0Xr0j5ڂba(6@r"$p 7:?pT&HT5 E{eS)Z7"HD SS4)"\ ||z.tn*7PG 4uhjM˨]$"}(] z[Aiy ZĠ>Qr^LZ{Yښ*U]pvp")ME_5Bqd,X:5KVdmV8[3Ί[oAѼl?fTK O@[8Źˊ`E8].h]zx9X c WUϪ]: +^'eޖH1ޟ^,BBX^k+ v q ac:;c^9 Iaمa8Ҍ8%e6`~0)m+j\p0 0pixI\@@OsL>OemeR aȌ^taқ  .O f04])Ъhz: HIedX RQ]=.4 922/c1[y3V[5F^pZcg:9^sp`"!%gcS(IdU[J=[+#= LTN)0D0 k Udd?q߮mƯwm\Oh`dD].Q P(XXUx l"԰VH@ [\@&>E`r'rmXXa)IBx s*:X6PNbwCp )[ +oYbzLR Q.VQp͑였^cqquhughh`avcaPpP]SJ"aXU'RP `pWടX;aW0ZMuWkaQuTd5iAKB!޷\5a{mHK^ QlOe7~~Ul2iuoVXWqZ_cy<.?,Wdz"]w1  YW򣰏RcU윢p$V9uKwxtwox'4Ǐ>u1NyZyoyuy,0+}_5#ߤjQnPPO W ^*yzrOގ_#.ŷY?LKDN~}ߪ7T<ӳǤu//hfZZښYWs8{DžwHmܸ^:*'v:'q"Dڴ˨"ǎ?q$ɒ긡L2e._Œ)%7$C~Xq'~4)t%Ѣ*jozJv4ӪNZ4iVYr5QEZ/،+g…YgN(y'Oުi># _7PiUT[iגwszu]-xuו.1؆١$YVSilsB,&O3Oj36 Axc5>=$Ez6NqD͓À2 %eQ ^[>VSw <&ug!Ŕ~ѝdXJ#Y6SUuHeeVj7wa^\VD_Z[ؑ`t؛jZ}lVfJ6o֢35<2383 .k+kɒF.QB\][r9!fwfiVS6J)}*wod(%[5fOJaDqkӅoاj91ǥslH5X,L-2üPFB,pEvbzu;XF נC&dj^T&־WYI(9JB_;i=` q} 2)]jgj#LI>8 s͙ls:ϡwR&ߗzMN8cucJ[]$^T%UiPb㋨X oD!?d;mY׵4q)m#{3"J9 KX߲ȣ]<+Ŵ=`{z]ߦ>utvKcڶ j&2Of1iMxc;[V<@-\!K64CauBBONb>m%#J> pqV"+a # H`\#IGNT%>hBsNEB>lC\T@85PHR6yTyD*i T#md;H VXDQI쐆R&(zhB3~?F7f0}3Lc8INȺNñ2&4C;*%潰lD!9KJ}b ''@Ly+Kb78WB f= yW9AsDW(Fq2kF*qs'+JR'MJeҕ B"JtMeӓ:ށ(II{ɧJY ɶOZ* &dFFĬ<$h⊒fJ˓%h2M5^10%1#c>MpFP; <ΠLZF:Be脦 #NI22zJUCJ=r"hh)׹q1nY*^׬DSbcTٜCW,[Y?/bTR8fQJá0L^ n痎!z1eYV6Pu9Eْ6'n51 BQ`4V|<@P6|e'4>vSz<'@)#΂G 11HDz*٦IDRk}pՐ^um$?o*DPK:t+*SsXCv @F9yGy3b]g^Dt́Nsiwf5rs&#++⑈P-E@j"YmY%_{Y^Dr@X;(RIuc{5+GyGDkT >AtrA>(Aߠtbɧ~Z倏hY w:x]K}&dĭo3#6w[xϻІ]`Ea(O2 0iLP8`yTܥ"`L0F:|,dD% B-:@!B˫ J&iS tY%SV!`ehRkzHDs<ߜI݉Y<)mu}CeDUE]<.I H}@NH HN!O-DiZ[ hK" KtH:BX`7pB@XxI<"@K P=lR}" إ `X9ib!]7-L}[,ȅ\^EE!'Z--24R2{NZ졵HD)Z%c9B#4|!"b)"# `ʞxZIh< I) bWC)@H@T " "iiAxV/GRPYW^ⵘ᭤Jwdv +3( ` 4=PZdoF!Ac#B@<TFtiSdB(v?$XJ!@B\DR$\$'q$a%0>Fj}.9CĜ-cc8BH/.5&5OFߚs=AJ~7dR&"6( a|WbWʖXbYA%S{T9YEK8A/HvaTN[ќۢy~[4eZ/ ( Rh6i]*ltD26$$"G!tL!F"-\4e-DD.܁8\K0$Ch0;$C0h Ń4HFC0H… 2!XB0lU):A=ÒBI)7X?`4H:\:T!!H0̇m!\R;S6Ör܁#hPd)&0LHT蒎iViV%ȋ6`G0b$GD!vH2ŠJL0h\YAE ivYxn[ڷgz'{{٧|e08!'8Jr)d@f@pCL 0p6J$pĆ  Mh7z@K X ^0C@! .|x#Ū7` C/<@XG*":7'4Z`FX r ,J@[.EfD X,}4; ,n$=L @FF4~Hü(TR 7t-E+7x"v:VmK* ^f"kL`ʣقUwR`Hfb˽cXdBfcf.Ҝ5Tb+:k$G(83g'.E C~@so< `K<"H,IÆAi+ƚb="A[\"'"/lFVEYFdA :F,=co:VT>˪;a$?@btB.@'4#K" #__G\HOIKF⹤G1L0l1+$%޼.n#d8:d n; \"|ȇ" ABo lb@$`R@` \YCFϢ2/R@\A/$C$@%&srݮbF<0("U:p<`O? @8.p 2cr !C&-!~! 2(-#&(%xrT*:A (@t2B $8 ^b㎜HHJa=#33&#t4B-B-B+LQ 5Qn+L"BdfMo#?A]q/CBIl4$d64GN2<&pr tC6<ܢD]s*!Rr=h,TEB b˭e)l p1K3 dSj_;3C@r5KsF,ҚǺ:n7BIK%;r (ZSyCfD/75:j[`\ @q_CD4{b\q1tmNMTC~P*f_v"L$gIIe!Da!a;8C5B(-e[CL#tĹ6&]5V'd@ӱ_LA8;5HuĪ'G,\/ CGD3u#Z^K`f k^6j :4A?X=<-`~:`r@ip:s opX}>g=̂&@703pn";Vl*ol<yF8M(tDk7xKXb\W0dC[ &˷TpC;^4<x`J5OChyECe -mm4tt PQ5/ueSGuN xhRs;+C$(O yC=""0"A\/@sY396To_K\"" C]y4SE1A: ;>zWչSPÝ3)H\c,I¾~|VD'ìr+Y믢"$sgu/e7<Tl`{yN7H|x'"꽷 !I{}ѐdאx 5T1KR9>wcLq!;N Uhhr+2n8_SĿ&?#ز:  *96|G@goW24D@0; y,@ A p #j"gn ©\(T\Ľ,QLLq;EaglRgr@ 1D.V"* @٩GĺHGIo01d)Pj&- O9S&VӼ>TPtP怃EuVI!K S”2O75&QKm`UVmhA͵_ts_E5a')؎Jao8hrXH[hN0c`ܓnA`=w&^xIOOk" W>E+o8$IEUŊfY(Au9E =8|(CDhH=ǐ٬0"pq{a9G}1hdc6 KhښV=y#C#DJM'\Z\XpbQhd5ڊİZq+6FX+趺֑p#5E,;^.dbV@4"hBT丽m2vϐb7^:-⢋gFuX!$Pڢ9G|%+`Y&Jo3!T` =3vQD $r܂.}nD$I9"U*!fmB,Љl8|@Knc :m f!AEhl 3"aIXkjqpnj sVطj cx)L 8 pWf5RQ9k" 9qwœDD.+lQcxoQn4W|-9W.7rrR:Ȉr-SBH4fP0(Ծ^ u24E ^ܩȅzZ[%9_.n#Q2^iޓS7![Dx1yh#І:R\`vKʛ.@jEPj5 [p= bX)j6b^^Qg7v72J,F,gjh$HEU >wE{ofGnON&E.i2!0*ʯw-hoHA"8Я20f. ŢjJ$#Z7hSTlڊj i (+`jN d%>D@!{{%.0 n( pnaMI$%P>P.0Bp?0hz g.Ep/#4C`L2LE㨫&э,11AbAr<'66lƴ07p7fN80IApzi g$e 0RpaB`@IҎ.!QNXXqƈ -q83XG'GTq 2r r*xx!b!WFt0tXnqPݐp " Ǣl[bq.O.TkIӸ ѐhfP?$- O>1/1R1SqqH !- #"%"% !)t#Qn1$M"Emoq#@!BB)MOE'n K A Ԁ L4Q4@r1q..R LK2*0rLǐ&e1+rp,6Q-H:K%lal.';"A6<' 0D0*=P%2\3 aa77/ZfR(d(wR])t}a? ?Q7//˯7]7 .++X8++jJee0_ctfTjx!RGG%RGaT^Ps>`8_>5P2g)k3430ATAmRg*,T&1ԈwD" Q*JDBhjE`b0ÎoQQ/d*!1R7US+R3@1IOtm1t8Pk&>*3%O@q@3kMAqA(L!N*5IGJO=ôPQ2".\\N.]MaLN]U^^5^^!N0=S5QU;nT-RRQ[VqK! T,qPu M͐A'2dNc{X)sc#P*I>o嶈nkϧ3BP2iQAG?SSCuSAGuRe!10g6+,IcUlb M2ue3tec7VY7p3¶nvv9zZf'-qboV P@[WhFeatmTt'Htw4u+ru}taD&lo p[muW*bJ#v,ضmx_3b3oI6duLA eSh{Nu&sp'71Z|"xj|{6h(ֶx. 9:7S1#"w;X;;u)2vv[veufWul{TA*Vcxyփo.g}/ƒAdc0@b~Q{/{I*= }}xr78vg-hA8EPV-r ҉)U"2"ӊ2S"mkS#NB} woXG`X*zKaVXhpD\%7YTs-8%Zq-EuS;Wux؁5ԃo`*ۢ88p$s 5=XdLn@C8yn-'$UgB%y9O>ɷX yRwa WK/¢ 1Mzyt3nr"gGtO3 ysVRjSmk+%5#Sv1[S\S5[8{r+H#|yi! <N)($`ُE/KTF](H0FPx J +gG~ 7!{Xު¹k-uH2T8GyYy_Q]Yv=>fA>N*7$W=A# ']؅"][ οD":Idak/."|A~# nص4bK2 ʼ"b"(ާS>$b+|G׾"^2?"{4E!EC/U# ح*\hÇ#J(Epp@ܦIm8i0cʜI Wϟ *ѣ=cQ̣A^Q 7BI#C}Cg=,4.FS9 tgӝg}`˹̹ϟ3WӨ/{^԰MÛ ,%.cZƷx N4#CTK*JkZZxjU+ZZ3˟O;Xb;+iϯ?|=" sт DуNԠE)qPMJ r L)STS=ДF+4>a7T>cFaU8]?T eYDDFPŎSM_q*:cbS<Ҵ#g0@Xj<{ Z?=6g)-QLxqV:ɉusAg0՘W/򋨤wwꨥ /rk+}#zF,DڨC sz8rPrG0t`ArKC`ʹb!4N2HMKQ@r!􄢦78hg,$R=X5T=SՑG$Q[w`p4h= 3|T<r"T&~0YO. _"Ldt0hKW46;ܡg*!ӚghcSݝ%3s\夏!p+X Sa*:LiP(Q0bu"{,ǎ۾آ-C?<|X VͲ;DJg7 rsE9R65@'aĸݰ oYŋo QK|&wEK2BClc;Y= 81|*kj&6I=4y,@1-cXO2!,(*jB)酀4b t2h'x@%^XKSBΨC&j$'f.#¦ 4%:"T,EJQ!H>؆8G%n 4 '`TNdt "Խ(gπ1VWrt%,eIK]rӘ)Ncf1LbNØ3yJT"Գޱ\p zPApl`lK` })I'~˂Fra/c[KBuHqlH;Ι1EՈ 0aB˨F P*D@O BY r"o'̐29q&- )KX"҉%'#^Ќ-26@*jЌـhF8`tbK GW_[pFckc91!⋖:U(iS`ÅM'G1hSDX7nve5y3g тy?m6Dfd>5Ҁ2p64BW<7O|!VhJ??n4u#RV6;a-KpA $0+IxCSbj hÔ EK@T>Jz(EPz 1XK仔{+_di\2P xL̄S#T0` Fx ы'U\CY_ ;@DXyB@pb.Bԩ) xZpØW`.aO6(֓dFYjhU-|ū]*e,NBۂo!h b 00w}-r5,7B0^H]!tx֮@ل DF<ߎ/ Q;`BZQQ-&(6azSD'Nц:L{QYϊLM@|r-كT(=\edGI|a2I4"gQ*'esfX쐂0p;$eשP_ 4)~F_ l 2p B8PG8 Jwa x$x BxMw=|@!xvg&\k5?y"ʒ /2_^>#V3aַ4#z"soIpRdn>%ER l#WFaܕHWaz_BSpQ b 6m&|r ~0x68s{9 9/(h,6d3a3~W@'8ufЈ U?A&qfPf(*81.0(?>ȃ=H؄iNPX RHUh =صW!hayđc ql*@8IӠbMHpcDBm !ٜ69.uQhyJlӀq.Ieo": e0Sa_a`@OT0ɠH2`+H"*g``-ԉOx !E"ODZx6\R\6:`@b$]PM1H]u|Rp a3\f:?@`a/yJ:Cz8IQ^n%%GmJ98u% lץ/A @O@qtߨPPX@ &V7):@ !JpJ dKKT$ L$MM MLdMSHfę &:\ 0yOՠ>_%zyݰ]e1jC7KP %u4""lJjb M!X&FrBdn/ycß8AgHyq %Zru l#Ԫ:[p+Xd:<"Gj긼Qg'TGl!@pclu@Nlt<0g(EJ÷k+Pѽ [hگ`;} ZZ# #Mܛ>Âs|V滲-61;)?!̩,ElTJ[<1 v lH"nVn;62궩Is\kT+@W]! F6ظ~V9 C?HIGcGȸa, `k{YWr9*IM]aP)Tv<yh:" M +B8isi²iڻq:ȹ vkȜ Ti`'2 w t$9=`HV Od"/r>=a 4_@u0u%6`B`cTAgvu/S1LmO@OA HMm]%6xy' "ϝ G!F 4<0 JB60Q X1 oF$S\<=2њ wn'9wxyw",}uQ : 2! L؃ pݠ648acC2\˩VA!rB Aޠ&x  5J /43 )N@ 4d qAPPu @:ALA  A#Q0x,..Q=o"3|9YW{!X*H۠"pNkT1>1XpD(?> @ )P~"^#1ti~~[>aJvUQvn_ ):^k !,Q3I5l 2mܯi.Ԏbց ^ P}3$ٰ(z&0"]꯮B R&"g_[,-Nnh\ * *Mbb*Ѽ * 5o3B?tEi| L&/H12[_&=&ŁV 緽e8+,@4J~yl :Y|;==R;Ӯl7/JDp{{r :JM o1dA=3&!21&2[V_]sk ` p ʛK${   1 Οo 0 0 .:L /,O"/Y ,VA .4C%NE8XǏE$IDPTrڰaά9WZiN~i&Lâ f+Z95ԪPobIi#[ɲiծevYq㺥[\yz &\aĄccȎ'>0c[`Lj;G& yrd=¦muw={7l'Ma/%fXЄ25N;%LL|!&&sqI&Rl{ʲ.2&bMf+dv\gYo 7p6w,p+;ݣ+D3Ҷy%CQ hu3$ԱZꂨ`#ۻ}tlig'<.Qy&iz=~>[?pxwyx'<|W}xq}%Wڿaf12:ӡz\1 9&Q~dUĂ]!)kG564e)MS4! il*[ѡVrXK,ށ>03_ÉS$# qD}gdF3sif;]xEtu# AlACzG1sF4RqL`p$$%i J>2Xd%79h';Ev~_8e*A c]Qn}櫇 38*b0-Fr)`x49:Й#b-et &E=q&Z 'MQ w`xGLu(dBhBg( m:3?"^%<"XHD|H4&=}_HV/p}2L8== L%T/ΥkX ~ɜijREdvC0RTΝ#;VA O3QZ~$mZֶբM=r{ۮm/\8pˋDLd9vB,lK%3@~YGC8%܈l"8oYrгCWhG;6+^(:a C~%Y1]y0%Qqԣn2`ý:A Z&IaKBh(@AhM@Xw'U{!4.::EccU7qff:[,oF @@6PA:E"F OH_~ ?Ň\!Sv5eBP'x=r%VvduH|EA6H;2P[z@# yH :ʑzA*$\8DZ$*ĤK$K$.NҤ-Kb%TTCH^~Pp?'⨎?v(Cz;.B(tmhKzX P* q< 惸r s q\5.ǐW{<\ti``i`Pds#AB!TlHFC^ Ew:Y*`C(|*2𻡀tDG CgP?Qk?*úcC{~FvCCCpmsx@tm(7\Hd;0ĴWH`xMDXFaTU0 fCU¸EX=@@4H91<Ȁ@ qW;EǀDS \T)D$#u`_4\0D6u =؃ f0C8U{?؃\%\K5WpbT`8ĵ`84B*HC9p`LW̑lLb̆"0AUМƲtAKr˅ V5i, ˪mƥ•-r+󚯳*);ۅqPyhhqhNlaY&R A ( ix&ȽQkUb#UJ- 5 IKeS $ p9S@3np5)+5ɐj51ͅ #ư tR S"8 k,l۴][E[L.a Q@ȐI6jcŶb\\epCt%*\l{:pl5?X(Q[GLʯ%*+hS G Qߝ@|pjTJ`*>aJl^<jŚ6 T fdCP \ D%`J]StPe |5ltp6f J$58kQ)pWH+M,Kz@10 R|6 Ǩk.Hl u0`SG>`6 &j"7]MhF\ Y‘`][m@yO,gFdT0:mY~f^]vE@u:("mS֛#a䧨*:|p*zyy(=C?DỲ/y8 HcRifi@:9HR*cAPg, 佦i0ܰ&=]ƈj >)ѐ:{h=/ d,=KCP#HPK48S=Éd6A/}oyqvhQ\2liìd[.(SP&lha'|BMp> }b!|"AGҪAzxdzGcbcR0;,dmmD@f&PWSHs_(Wx-dH31 ^ zcF JmN qH9H0JWHJ *$(x_HS]u SStӋu:`ƓpXRh%13>j +,@ {xqX7Aid^5Cwug* ?Y7vFdFCO7B?rty\t{܅Hg+J'8=S>zVxOzme( f(„ rk6pt )*H@ȔOHo&Μȭ=i<[K BG`A$MSW LTDՑ0^GTq4S0FD*X@C4v3\ v@@xvcċ7̘(pK3Y۲[wm5L^ eUfӝE}/C+ӦW*ږk@ҧS:r!Ǔ/HI*Js^{wՆ73_w` M488À2 %y!;3>x"UVY(zU#r-lh#j, P(6 @_wM7lmč`qS=/AA ;np&L#1S=Lq W#O܌5gQIU8X0R)gH*i:ƁHљ/WDN*3tiP 18aSVҕr$G7d8TJf|5f"<Ճ.jS͒)@!:R<` tًuݑ/z<wp~83 .23Sl1W<Oq .ʸ@.*aH5 Ὧ c0  n72.pbG2GU#W QU6T^G1%:BA€<&Ky2=^ !ǰB," a9%Ԑh(&3Ӌ|E>(ub$*Ef $|pcA"nM}CRv Pvz Y_KüCAa8! G&YF1m %Yj72l.m@1u UΠœF)\ ބP0(*X5&[aVv|e ?*sF4&$!mLAieD1uWxҍs9Y&*-W}o0σtHɐˆd0Q3^pAeNloب0!__z>So 魒L o%ߦaFqèqIe<KYGM]lS35h-Y} yqY j 6`5 ن֧`fxYrt]_9dvA 4a-đ^%)߅ݠX߽w]CH7yP: JHȋPA Irj2F538i5h=VɨkV%RbjVtbnV 2_ ٥ۻ]!ϤĈD>`x]!/:} .>\2AD )ćx!* `9"""DS3=03A=f6mS7Q7YY8P  bY+,}^<4ʛ/!HVeI5cy_9#ߜ Ya5ZHo Έ56_Y#eVc?^?cUd ݖ A~< ~"dE+EV,v=v.v$W^J1GT"5fXLN!c#$p eeN,biE"AhuiYb&k&b}p"nm3V($X\!/ew%Ede[Y\c7eA]dY|v9_%Mpn٥E6b6&x>&dZwZ eNrV|C5>ږ`}bGy22g %YQqfubvZPmuB[!grW2gs%c/$`eLjgYB&9&x2x6_Xeɝ]dzjW]){*2 uV4L X 9FNF5 ʐ.00+$( N h*(s0:躹[C\xx0GnۈXd$]^ƩoϜġ ͌gO S<^.t<Ȍ<19jŤꪶ5`7+\e5**/X5B(/Bkf)L#Hjgz Ʃw^ւ/옶B™)ʭ)ehjr+D뷖^ZeZWqQijZr("*.橉FXPY(NCt,f*NJf ꨚ&^!G{G{,lM8ƭ-5x+/B0 n&n-LB!hC\-Ni+mВ.H +G.žm@QboVkYxZ#8zmm˚q[2tVð5Q^R^oZL*j+|+4.@n֪}2jndZ-Je"I q0b®eP+ZZnaV*O^ b+*2=Б6 j: _p s/ ˰—$HJ:Bį)n:pnnYĹrן-U!G>\D)/.Oc(c1MQG2,<2=P2A7ݰ p  '2/0q/nedպc -J*J^ndd'#(k-1N2XdL2'ã6/)1`/No6kom/ʖ&O.oqQq;D*c*w1sN -3?O^q5nj s2a<5ںmGmޞݦtKs L33tAs3>2@"u@R+a*aXC7!CX0svpFHo G£J,*\{Zl]slƂ^ߵɢ,ɶ_,)fr:b 6F9c7?/mOZ7e ~@ q#744I-KIt[CttL/M?CVy\ ,xyND v\plhC9*.N9&4Tg9S!%8U1ωHx/դPW47BC8zt-/~<2yV~5ks7ss6kP3$9/:Epi(XQXzwĦ֚v=7[9?pY _Ng8m8d5o,xJ / .2 ;r#.p7$1|%%OySG宥09a82| [,; ;jR \߭]u4v4B`n{ Yމ8Gz| ț|ʗ<#ʳ<%K2K,=|?:G\X (yXHT-v1z[}t3_փyk[$OVKJYokǧ64ǵ06O W{sj{3t ~H|?D/0*BcG.$Wӂ*0Ђ+6Br5xCDX vt.NB.I[<0`իaB 64ϡ :̗/"EĄazH<[J*u[ȍLT gN;srkrC5ziKn<2fxUzkVYZokXbIyԧ aL[*UQ|Է xM C!pb@zY\ rp\UEЅq)њ*wm\-G%2}*ɂ^ʍ=a/q 0sN<4L=s3AXxŖXdA*!Za FI%&VFy@v5X`Er@Nꅅ @D5Xn*6|49qBK4*$EOa !@`pbB YV$C N"X.qrʜ0אd]"P"K]k]Z^pzN#7 }wH*~!ų^җ8x c$F:f1$e$k-a ^_,lF }DacRUj MB'[ SPW4NAQܱ*(&xءL+HQ]*'k|*,T!P4E\XLL|Cu*Px &)qEqTqVŭQ t2J xƦdQp8q wDZ @)9c`*b &2h$N ӛtiȬ uRQ9Y>Cel2Qr,}C5;\H.tUjNB1hULx"A+!q:Zӕɿ?%䔮 W!xԱmP B,d? jġ"x_*J/~T]&դHcJg4rYNK$򈡼Y80$W8㦉.xWD#1{Y+~a c)8/TE%*!4VժjX?Y!Zr 8 7 e`8mÈ{En39+iMZ10 7kj['MJ+ecޱ-X@oz B>E)6tbi[$[[F0E$xdGEф쨷[oedvKr㹫5tkQ׺wkh7{ݻ'}}[6:o_ކ'*\%$M JiMqN #c]g 8Y16j@k 7Z ML;S.DͿ DE.9/ D-cFˢôlL愘YWf7+掮\Қ2ufa@SqC3/Iַ*)8Yn|:!N&M[UX@5c]RZ#V\}CXc-h \+;mB(C0W"(n۴5rsM}D~;Dd0r -?a}F7ݐ nl!@ JWblxH7t*NrNr.Q$EQ&Hnvz UuBE_Jf$PU ,QLF1qqLN1aˑ*-G Y $b$! q"Z@J}@bک*X*j%9r̯ؔ"nO*0*r c $`A*kÇd2%,!8] >%o6δ$N) wZQvM5b}2)A%m$JFpz$.$X$PA_0PyR.0  P g},(bp"f!  *ڐ"sXpW N,N$ꈥ/!rW=ԡ&Jހ(2s*`P!$xR+OuV/*]VI+)H&",Ps,-[pZ01>osboH?9.nOt8/ 1O'4)b oW=4*=n6CbʪYSclQhUj3VF7kBk83BC~H7[ 6S*:+(+(Œ)R3>S>NU?SHtl@I~{#dre| wqāWJ)LMusق\0#/h7K4JJOw(EFr#SPk)"8xޒ-cp-a.0/2.0 |GWW$[}IPNbY$Rݘ Š7Y-ZX-C9ՈQȆ+ǵ XjgApwywkw.˜AFqQYQy ui˲xg<|Hu}&B~9~t`=wYbY9TW9+5xh8]L- cݲrMZQZ//| Ȟ]mݠbKa}'<z<gCˠ0Ddk[gmx:w ޼-zu5|kqEƃE>s>GӄK?:i qO7px7!al3yIj9JRDՒ HԪCJVGKY"jZYQª@uqG;ڢ+њ]z7ytyy4㈻N{W;{K)P#{kG "±+`X1uW&Pw*1~ :Vd{9rY_M2dصdw`;ޜx5zs$v{mז^`nVo#Ma#o푦kZ:9ۙ,+Iw)+vs)Ge{ذړ5C&,*mI̿:֒om5nv+\o!DnÍ/oȋkl+0,cߦ)ƍ:;#@ݻli6G֜Ev}k5qfY|Sx4yէ׸7OO[y$YKӧ7CqQo~m+( ~j $9[ajЀzU~*ݧUae~y-+wq_DZx QY8|^}J%q<偣؝}VH0YO}ۗ!cO<ŭ#>]y XaP}OzsPIɥxseٻ̪;^M_Vi}iݽ"$H@W/cC)zƑ-j;Rĕ,[l-̙౻3zz T͛3S9' ]DpRKDփYG%!\iA%)lX/r1"^fB*bz //^fK%<hObeEkvOFK ҮNMA̤GQ׭UI} H#!܆wj_M +xKQ2'gYFW2lͥd:1!* Ʊ`jwb뫴ڊ+U % D|d p7ؔ"ecF44ZwiCtT+>iDs6nUE-W#L 60 OXwy[,b$hxd.zHL9\HϪ62K3/DU jO4#EݑG^R7E_y;@Y\jۘ-L 9Xk:.=:Oo0ZnS3$fKߢ**4X`&؆odwYh.t˻ lŹB?"D( ˵$5wRjy6IW\%y*//`hU.ݚMMɆ@uE^"vEH*4eVF% OX7ȅvfI*$FIy>e>~P 4w1Bea@Kw]q# W%{s 7 <<\m>V3M{VwԦFgd#sBEu X|)'C$Hъ#gJ1*`f/l6/ MꅧdxNx3+a!1yICٺݫ\"#aoB2Tc?z]U2-/h#iav]m8'I֛e =A0ADq[dȅ! $Ə q$/ ̦2@T! ]2\u@0q?8)aH>)pG|~j|6A_J2q=H%fțf@Pq U~H}Ja;`Rg.wiC;j\F ??&H#gG WA~W~wOLJv|Jw0vk(&pE0Pw 9 y |p H1sSs:g 9Wsp*BtrwtNu6R7]$D@UaP]\Uۤ) $^cM1 0K4Un `xVpE_Ma&iJ AQ C@Wpn@Wtur.agg M`J$WtEd MBJt%@gD`5w6ذŧ vЋD€$jڐ Ÿfiݗq%$a YEW 9z|5Pxt&Lh¨ @+hHWkxa@M@:x,IWKbt!okkk(7Zv"ʖl)4y82i8H =Y 6mKJ t@s/V$P_*ke=ׂ Ub4ьxr`fbi^uި PVh:WHu0g- HKqy`.pWXouHpO8hTB׈MЖf``|5nIW p 7V) q-!'rILp[u@_7'`К](!ykxgC LbgI+4kUyFUJ3c6l =?@F?Jd"( ?dB)愴Aun|p(Z]X9f  uPTDT10&vS`t8bPDex_p x?g9ApU `YO PqyD@dd*: VMa hGUOPC`dЄJ J> JEaM. FVb Py$QG`gu s$LJvE`WPA:д6pJ = Hpu D 'qJ*- /0` @b ?tG>jA)` ʩk"1PP?@>GsTGjGHձ){4 xq! p!A uHT7!Z&:v@3*ڕ_9 nhW KpO8A9uᐆ~'Ѭ GzIo  ejqkoYNDVZ]aF|Uܐoj.W]Tl ᩮT:Sj}x6 `p-p V @tb,1 afPWѭa8q퇵tU7q[B y V×,9pq + +lr`ɧ 2ꊴk@Q` P '%S+R*S kGk \屺-&[G0 0 ), . E%@RSqwP =S@{Xu{ЕOLe6Uښ:AQ]1X' LإO,.!kp WoJSUoJ+]bFQ WY9ݧtɍ&ȜPjlT֙Yb WL@m,ƒ_m]\V 0 =46L3|E%:& ެK*'VšT*P9 ;l̝f-1f ԪExU~uXK P~ @de}hXsNA"Lp.S^,J) X( ?lKE!a*f2ˎ9Yi5|K'W٪,( n ;9X)45 #))9B8t??Gրs `c:Jx@ȭ.wӆQ| 6WZL_:-Uf޶_uɮ샶+.d` W~ޞOlJȋ.Yv3.r٤~~) 8pyaD-u/r&10>lX @ZLٽnJʜ  3zaw N-O0z/6<_)1@Wt=IHp0+< DP`} TQ!Dvڴèna:,6I>8q[@0c 9KԆ'!8.YGR\G59a-즉h-@6kZ8XU^qFO,}]yEB =P vG :RL1=iZƲ; Ua`tn:JGk-'@iwZa'| #[ IG t7_ q/k5>L a #x]p~ Bp;$x,#9nk ؃- K_vflDf+K?W,AdC1:KF ߡ0NĔhS7H$͍MzR~`8YCt3hjqF-j cB yDpsVa@Rà%VLB]|;Rv+eAx;p 0y0 3v /(\/eI I@6R6 CK.@Ee=py\]RxWFh| 2xPL@."#P%FMPG\$La0: `Gl`n3h;׃Kadb\Wd0c!NdA`}0u5̼3 8 `Ea^$=$͕ Vi$:!T^Aا)*f lz)l2>&9VSooD,F'Wx5k'BVc#;Y\@1 S hKkD#d#;V֠kc MxD(ڥrQ4*C،͹]*Vq7vt* W"!{QRb:ԟ eG\j1C-pE.\1|փҐƬ `,ɾnoj Țx$CY0+PQuR=3LUf$z# Z7^@0]!7~B '\όc.%/7nAݙ- 1ɍdBxn_U#Eb;Jz,3h8E$!Mbd&|g=GK$(ea1 f-7:=T]R׺p kԽf5e-oiJPә.eiJf=~4RQԎ#\]y5F7ٝ<%%q6 UrvF:*t4ILBлNt}c.*N\>b]l*;WZ'VUSj&ʂg0.n`C=\%'HGn݊>uhqa:)Lo4VNyD%U$%Z cY{5u؝C0A> (ELըNuFVW80D!p,cr={cx"1e={i+N["驄z+LG}0_BTߒ3lvԇ PLeȽ~\N~c}dO֕oHEi(Cwcϊ A"7?0_h*~G0G9 ^𸝆 ˼<ÿ'>=ҋ#{;š@SWۛ:A0A\3>u勔>Uz>|Msq> D@c7E:BTD{][6nH{MΫ08[#?D?~!\?s85ıK {°d?@BKB·2fDp%Cj66ljm+qCtFl<7n$*:RcDRrDh H̻JFD .j5+FEs?w\laTSDvX!B\GDE4&)<3CaD>uEt4dled3#34SIl=?I3MI=@3>ruQTHP0U +DSkǾ,IaMh%4ͿHaPQ.XQ0P|)NeӴRaH]s/{@>Vܩ`T )nVq -IWsԟq, E!C%hh k[%Y7U[^%Vȓ5t67RD4*E ,L+Fe.\gԡTͥTu]WOCM/Ԟ$ըeYJVCeDxpXRZL9 ∢ے3Sax^(* W9] $C>$F-ѳ>=h5\ȭDoVŢQj4=]Qu_gF5RoDsHϦM&֍ZZ[MǓc^?C1cБyh J1 `%&UL9H۵L^h9M.m/sN[_TdO=HB3O3>3>uL4Xi-Tx'`àM j_|Mٹa9^Ćtc[mՒA Tݷ۵֜_,_?>FX_oEZsF翩gu^xa^Un^av\guĦ}n~}eO_%mN$F&fO5H `'E+f5l(bTHu@2uiA "*UGu(o"BI)hcngmHon(p-QbP0ljNgV\A75Tvl-d )\#܄]\\xN5,I]1L]Hb&_^d[pLYn(ˆt8'Lpx"p(X x|ؽ'lX yO <ikbbr Y󸛓p(pGƉ 7_YǴڃ4`Hq]Y+9WUbt=WεuН[_ݒhpLnz|bNVF?;ˈr`& + vRhn2Ln7 eJ@xgـ'`:(sn R%h/HHW)K@wނt0 87 ;Qs8KX'Bp@ ^a!n2bֻ.%qeR/u\&i6ZqNnqp\zZu]Z^T2ZbgګגbwcGr:`u0hwΠ!"uv( ^vXp4f Qp0sQ6I7 ް{XxnXv_d< |'pp t_n6L(|ɫ H'˯w0 [y6u~Mavq sڸθkwo0ڝJ(Xgp ZUcYbTT,Wh@ABAF/Z@JUK7d99S 0(cR BՖD8@aW^ ci3_G$["Ћ CR3~&dMX7 j曟)8q: V%oBJR:]qbr*ui88C3ՌZ{Jݩ:0ٹ:z*$Xl=QJaJX@ktiP=tbEPI#JVŽȄLfO璩M%E2>fDIДfYi8EHO}\ ԁ63V#ͽݠJVpI[(c SUJvZj OX-g>( 9i1mf[4M+*nnmH# / I:Ǭ\ƊU: je@YjtRY5x}2gB0IDsV<mlwdq㻱x~z*wTk,1H(LM5WP2K̺&$hiP!ðCfg8,k1nc#4"]dYdz EvXI9a""U`^ThA_yV-;^ls4M1 +Aōp"XV2ZQQYxJj MhvT<ؒnQ I`2@^yk1D tۮ_?6DNv%g ")MW yr4ЊZǼG9LxtjN:4RT V|oěR1TŅUE] =(ь+J `Cb.\q$фU! `q 0J-Lw+ ߬7Y-aBq\H= ceVZhfUG??Z3?.vQ7h~4RUi򔫒(c[;h,#K8˗r;2(C "01x:]DEJ8:Ubl6ԁo^7$$z7Z Ly_)_qP TYI0d;U?`VLʵܡeIHRޱHp[<Ẁ(JL ]AM]O1nOXC)\sh<̃<,<3 ؘM \M3C<J!CMMt+XvLB(' `sE ZJE!!JR`ݟl ]Ɇ|ED )c0˝.X-`tZL88ԟ U1JMџ 'vpUm aNFqix̌]q i0 CԈ$`aF OFOQʥ̕`Id".m"'&'F1}z@){5*d5\|T 2!:c1/E.K5J76 >dxcfc!qC"!lР"!p]N9b(% XՐģ=~p8t=SPM%a9@yDž.(d d܄!.CrpdePYd8 <4`FJ l܆W9"^I&JJRaaz!-Dc b$2MQN$4" d G^#_gQv&r %cij#Sfnަmm&Eoap'YbG rRrFb\\n $ޕ!m$'I`jp7&xuݢ͌Le]'$Ɔ*wn}駁R9V~SReTfeW^,)փd*?䥒@^c?jB%VՇ|%C: i)<_t*Qզu6ၐD䰶T Ts` ƍN+nƱ6c0HFpjQzj`z&(ޟ P++"b}"VqU4 .ZR#$0К\ȫ j#MIgR+YޭJikƉ)52*ǞXRvv(Nkj ! Fm*!ZR^m }X+8DC*D8-!!2"b`cl>Mr<ƺuB,1D˺-sɬ:<%c2꨹.X!*κZƥnҴl"g$~g+b(k"}{ *"l^|008,bZσxnjh-Fbn*G m@nn6zL.rn<{,8 QB,.^oe&h閮ޞ'㫺>$NڗB% /wmߊPnцr$`R/Fjdz]kʆݺcp7F0ͼ /2֯+8F)F%QmTU^O|xXY6$.McoZ}`h0JXnLM.άn|ڞR|/X].]ZEmiq%&♀͙lƦw&'ZqV|W3>2z! qȨ~7$qvfp1 2 2M r" h#.$j$ـf%+(n'wr g)S3g(K\_",opOi.W gs0W-2d63z."K [s62+qneE[O1X% /uܮrKs  F0c㢎JU0$SDvOfJsdj+nBGosjoEWsag,h7o& ^zK-Q7֮ غ }7!j٦~mv<{7g; em4U"sq6 7Gskdtwaf8F7Q>hxhiv6j˟vm6_nj HFyb t5_o37Ud#2?beJ^ǔu0#, !_^:fKQ8觩jZ?cw.*Bµ:p [z\'0y\:Nfi2gKi粥%kp.eK^+&k9f33Hc)WZU:vKC#ϳ?9ktDsn*6v@],ZF׈q'rFhwKc,6qFқzS7[79s: # ˶+SsI *-#^Jp3[{LC 2r7ΝuAs!SB_9wԓ{@#i | 22vYJ{ ?: t+:{ ~4^q+%#(=tBﰡޛs -߹*>u{GߪB/B#6 N~G<jfB 7c|S3N[ԏC4oWϼQ?>@8p: &TX(P'N8y+bQEgƍXm\!G<)I YlYmذ]âW-(bčhQGr붔iSOFuM[UW^=>_K]YgѦ5K6+֭[=Ȫ= k^ebU*5jekx0ᧆ&Wbږʍ[3ׁ%d:Vݭ\k׫QE ]@/\_o߿>B 6|ټys9t?>1b5ww~|zem\T֠=3eٳ_Dd-0jLS2"P. /Ӻ ;6P-D5)--0) xXſ`mr5T-9FRzrVbkNfl[码hqmp[!ܦc}rWxń8ވ7 nj15dٶQE`Rr۱Rm#Y ONQJmǙvlsVX"}t2M'=ӭ_LܦMG6j Mͫ@?ź'&2TSn22w>+$W /HiF)sX;\g1WW&:! 0`Ma i%pz"\ 98vL;"|&4 D 1jb0iKӚ.JH̓T,xEybӽD-Nk$}hqaa/m{5!ⶪ̑a~ G9%qe,gYb%\ Kkek[GkM1 \BU6q %"p#" C["+SHk>oafҙgv&R2̨d5,f:Y+PqT=[&'vK_RE.%1iQ&grIMAa8@(Mj 7'?QWF!͜Kæ+y|7ikvGڄJS(7tJH`'g2džݫd ;w){=Eiz 7d:ӉjU<[t1skX1ֲ1'@ͨɎ)T ͒ -tXO1#H<6F*4[2!ӂDŽFI+cjSy*!g?-ed]SCP]*MhM$&qUtN]QhL ʰ4,d$mb3Ջ2;. YoBR:5.b]gCpj:Jv!Z,Ud9[r-VÌ0;<)Oy^>nD c"&FHD] Ԫt^m%%Re[_Rq]-k(KgTqv,dgp(q100\jky >KM )]}zdJ(cj񔓜ߕd*{ːRs+N}/l0\MW4`f4APq~,/;g+Ļc`.xKuŒEα14ADT1R]۟<1ILNR(y\׉_No(5kokkޮnj܎*C.6/k'kۏ )d͔EiZn5q"V>9ǽI}ݜ ?y3Q/m*gf>1,f4Gc<5ZlEq:ܜHU)i/jJQΘYE-3܍H?SAdsgK=J=~'ד8Uʚ`O8!|_^X”%H#C! t~+~mYdcK|e@dNR{ ׆,~5rAHtXqmla;ېV`kۚǭz濢qWrƎ$Ckp6ƋFldd+oy- -ԫ^$o"D7%l帏wfR+Bުc;&t#JʬEתj0 ?$ЃXO-;ȝmn/C>1&+ņr C0 %^ VAgcM3/o)#obKhrϷxK*o(JN͊bˍMزP pRnwp0O@Х-DC΂ "x0( p Oc^.$ OL  8oe!PPLñBͅ.eJ/ ĝ6p|qqQB`$blqD ɎxQ1'.FnO OVp,,;q!TK LL,NRnXج)رpQ2fQAjB0Cpqւ y^F M,*"M2r:#G Lms@NtZ-ìhM(h&(;*//o>Jf*3.+7Nꛀo1]`!2(_PЧR!q,2Y r.g 7 .2i*' ЎN# ӝ|RȐ7Z(Cp+{6G}J# =.7B7JhRw$ '55#bshhJgVTt̞skF<3'˫v0 8%s!eF:6lK3|ij> ZiGeRűɑF\!rAsR0&4UB;ﮏ<5tCM<4)7-@NG2L9S*hb15q4G_#ʶҤȤ^ P-0q|"lq~NC2TQ3@CE/Cخf:"Q4J)"2rE S5Ҍ41jl4!VsOq=CXXXA:*Zu ZaI90) T5 "sV{MgUDMՈRƔMq^>{xrZB&=W!oOgUMM>Pc/Q1lc#uR/U\GK5<QHLuq V7N_3q)^oα8 +Wa4FuJ95vd ztHHtHQHbl[a +SWC%BAFPK;1 gcpagwd QД`gm,kaldviiVSS?]@+W@_@k".![ŵJtJL9o;k/^9(;/`6K쪯ppGdOqq-ifstnગ7uS^ '(R+8nquguvm)1hBt;ocg +18)uֈP9d}‚6Dxmx4&"O2XuԞWzw-77ׂmFV|qЪ(pDKрOhFy.a4bbu$6Q/j\D|\o:9|w{!zcD8IHg_wsוeo_JC30x򤘍~upYۃYX#X[Ziު[@!BܰWI7!cn&JzI+oٝon,3TU1VwB!zQ/y%ɏxpN[pODBFS$b ؈EPϼ*{9;ڵ}K?j"_;eȃ` k MLCf:Zc_I[!{%0ӽr=}@ &QRG*\xÇ#J/^Am3jQn C ɓ(OٱeFvɜIS&5sҌʟ+|S'}bn(Fn!IJHN\ZR3flYn[CJ]WV˗D LÃ)g"<dɔ-SY3ìeLtZ*mtW =:l@eAHÊ׺43s */z"Rm\kmYvQ]|vMz/R⹚)w`Q>%)ƘcYeN虄UHm5Zf .іnP>PEQBDHxB=CuD^WOyJbQI'zKQW洟ST.Id~oe$AvYC )'a6ce{Y{vfàxl SK+R0(ʨ5flb)2(x pndy(Y䬴s[ʑ*KeIS==yv^ ržZAb4&F!W*jfK]8rXeYL6L~X1@s+ZÀ:P_Pt,XF%6xQ ;e95- T$ŪZlECseu,CwZV8I45VMRS=11VlSW=/e[|q0XsY$sbuF*_%1L"9rV=s0K{L4q>-R3DBbv983<#ꭿ쬻..s5λ[{+@1+9Ko<ѯJrws w+'+MdD|eF1Y\'Pz{N7 *f42ݨW5 l%ls1mXJ nK 7b78[':o 'tmsXƴRmqZPJs4Z 1V1Zu wMI尦60']%QdzfaUa5'}RhdglhlPJg HviRyO,Wx?>I)`wF|Sv#Tfv#`{C)w&{z\TTOk>XUQ).y!_QV=1Ha=lqy'b%Zݳ 1>6ToC&GWN|EHUS}$&v|O3}gf=}0~gD~o p^j.lTpF^WM{stk |Ẇ`SG`279򀓷`Qy]*$[&zǁsPlkXb3b]A6W6 29E|e8%r(y7q1A8ICx`b1&u4#6$S ? ~fhg#<;S;Hg3}v oGHR38fN6-u7 \Gn{HA$uՇ} z]}hHUH~ZccNA_%B.Tx7dUֆC*G?ujV$wyH8Yn(wv0-g~Jp9/w^%.*Kӂ fsqVF֦h-$J\SUjU6a>s I{X#ď4D3L뇓=s?9 g  IAܨ[(-oS x明Xv$yHF%:b'EvUw{e57{wvQ p_ H&Hf+X8bqGM16$Ŗpb7g"zZuUїKg#_@yqH$WX*YmsD$5Ho♥2R( Gwؓ;ٝ~@@ e V5Fs1ӛljfz>ƀi (,ir MaHmixu,8 ^'hZj y.hv&RILn[C-h$%E mѲ^g♟ SzG4xlv6Hl^A`8Y΀h!>2D HI-߀aySt&-s9I58@l& rR#E4艃我H=0vh3( pkA%棼*7_֢.0mO,Ux8-}#%l[h6RZVYzA~ DQ׀ 9\{j&Hu!9T1zJayjZ3t_`uL)zp*rԡ z)* @TE#Fڭ 1-C1#2?&CaB5(Ex駓d~DB BY~`.0@H!ݴKu1nR>:Z>>( ],[3i[5 įtX5B4`xk xkpy}z0|[ x;1 p۹;[{ۺ;[ƻ{r];[{{w̋;[{؛ڻ۽;[[G۾;[{ۿ<\| ̿Q1\| "<$\&|(*,.02<4\&\;MCE-1.608/MANIFEST0000644000076400007640000000572212511671246012310 0ustar mariomarioMakefile.PL CHANGES CREDITS LICENSE MANIFEST MANIFEST.SKIP README bin/mce_grep lib/MCE.pm lib/MCE.pod lib/MCE/Candy.pm lib/MCE/Core.pod lib/MCE/Core/Input/Generator.pm lib/MCE/Core/Input/Handle.pm lib/MCE/Core/Input/Iterator.pm lib/MCE/Core/Input/Request.pm lib/MCE/Core/Input/Sequence.pm lib/MCE/Core/Manager.pm lib/MCE/Core/Validation.pm lib/MCE/Core/Worker.pm lib/MCE/Examples.pod lib/MCE/Flow.pm lib/MCE/Grep.pm lib/MCE/Loop.pm lib/MCE/Map.pm lib/MCE/Mutex.pm lib/MCE/Queue.pm lib/MCE/Relay.pm lib/MCE/Signal.pm lib/MCE/Step.pm lib/MCE/Stream.pm lib/MCE/Subs.pm lib/MCE/Util.pm examples/biofasta/README examples/biofasta/fasta_aidx.pl examples/biofasta/fasta_rdr1.pl examples/biofasta/fasta_rdr2.pl examples/biofasta/fasta_rdr3.pl examples/biofasta/include/Fasta.pm examples/biofasta/include/FastaC.pm examples/biofasta/include/fasta_aidx.txt examples/biofasta/include/fasta_rdr.txt examples/biofasta/include/fasta_seqlen.c examples/biofasta/include/sample.fasta examples/cat.pl examples/egrep.pl examples/files_flow.pl examples/files_mce.pl examples/files_thr.pl examples/findnull.pl examples/flow_demo.pl examples/flow_model.pl examples/forchunk.pl examples/foreach.pl examples/forseq.pl examples/interval.pl examples/iterator.pl examples/matmult/README examples/matmult/matmult_base.pl examples/matmult/matmult_mce_d.pl examples/matmult/matmult_mce_f.pl examples/matmult/matmult_mce_t.pl examples/matmult/matmult_perl.pl examples/matmult/matmult_simd.pl examples/matmult/strassen_07_f.pl examples/matmult/strassen_07_t.pl examples/matmult/strassen_49_f.pl examples/matmult/strassen_49_t.pl examples/matmult/strassen_perl.pl examples/mutex.pl examples/pipe1.pl examples/pipe2.pl examples/relay.pl examples/sampledb/README examples/sampledb/create.pl examples/sampledb/query1.pl examples/sampledb/query2.pl examples/sampledb/query3.pl examples/sampledb/update1.pl examples/sampledb/update2.pl examples/sampledb/update3.pl examples/seq_demo.pl examples/step_demo.pl examples/sync.pl examples/tbray/README examples/tbray/tbray_baseline1.pl examples/tbray/tbray_baseline2.pl examples/tbray/wf_mce1.pl examples/tbray/wf_mce2.pl examples/tbray/wf_mce3.pl examples/tbray/wf_mmap.pl examples/utf8.pl examples/wc.pl images/01_Bank_Queuing_Model.gif images/02_Bank_Model_Chunking.gif images/03_Bank_Model_Chunk_ID.gif images/04_Enabling_Chunking.gif images/05_Power_of_Randomness.gif images/06_Shared_Sockets.gif images/07_Sequential_IO.gif images/08_Natural_Callback.gif images/09_Supported_OS.gif images/10_Scaling_Pings.gif t/00_required_modules.t t/00_required_signals.t t/01_load_mce.t t/01_load_signal_arg.t t/01_load_signal_export.t t/01_load_signal_tag.t t/02_do_callback_args.t t/02_do_callback_result.t t/03_chunk_size.t t/03_user_args.t t/04_norm_que_local.t t/04_norm_que_manager.t t/04_norm_que_worker.t t/04_prio_que_local.t t/04_prio_que_manager.t t/04_prio_que_worker.t t/05_mce_flow.t t/05_mce_grep.t t/05_mce_loop.t t/05_mce_map.t t/05_mce_step.t t/05_mce_stream.t t/06_nodata_flow.t t/06_nodata_step.t META.yml MCE-1.608/Makefile.PL0000644000076400007640000001135312511672525013127 0ustar mariomario # Module makefile for MCE (using ExtUtils::MakeMaker) use 5.008; use strict; use warnings; use ExtUtils::MakeMaker; my @exe_files; if ($ENV{MCE_INSTALL_TOOLS}) { push @exe_files, 'bin/mce_grep'; } WriteMakefile( ABSTRACT => 'Many-Core Engine for Perl providing parallel processing capabilities', AUTHOR => 'Mario E. Roy ', NAME => 'MCE', PREREQ_PM => { 'bytes' => 0, 'constant' => 0, 'Carp' => 0, 'Fcntl' => 0, 'File::Path' => 0, 'Getopt::Long' => 0, 'IO::Handle' => 0, 'Scalar::Util' => 0, 'Socket' => 0, 'Storable' => 2.04, 'Symbol' => 0, 'Time::HiRes' => 0 }, VERSION => '1.608', EXE_FILES => [ @exe_files ], (($ExtUtils::MakeMaker::VERSION lt '6.25') ? (PL_FILES => { }) : ()), (($ExtUtils::MakeMaker::VERSION ge '6.31') ? (LICENSE => 'perl') : ()), (($ExtUtils::MakeMaker::VERSION ge '6.46') ? (META_MERGE => { build_requires => { 'ExtUtils::MakeMaker' => 0, 'Test::More' => 0.45 }, no_index => { 'directory' => [ 'examples', 'images' ], 'file' => [ 'bin/mce_grep' ] }, resources => { 'homepage' => 'http://code.google.com/p/many-core-engine-perl/', 'repository' => 'http://code.google.com/p/many-core-engine-perl/', 'license' => 'http://dev.perl.org/licenses/' }, provides => { 'MCE' => { 'file' => 'lib/MCE.pm', 'version' => '1.608' }, 'MCE::Candy' => { 'file' => 'lib/MCE/Candy.pm', 'version' => '1.608' }, 'MCE::Core::Input::Generator' => { 'file' => 'lib/MCE/Core/Input/Generator.pm', 'version' => '1.608' }, 'MCE::Core::Input::Handle' => { 'file' => 'lib/MCE/Core/Input/Handle.pm', 'version' => '1.608' }, 'MCE::Core::Input::Iterator' => { 'file' => 'lib/MCE/Core/Input/Iterator.pm', 'version' => '1.608' }, 'MCE::Core::Input::Request' => { 'file' => 'lib/MCE/Core/Input/Request.pm', 'version' => '1.608' }, 'MCE::Core::Input::Sequence' => { 'file' => 'lib/MCE/Core/Input/Sequence.pm', 'version' => '1.608' }, 'MCE::Core::Manager' => { 'file' => 'lib/MCE/Core/Manager.pm', 'version' => '1.608' }, 'MCE::Core::Validation' => { 'file' => 'lib/MCE/Core/Validation.pm', 'version' => '1.608' }, 'MCE::Core::Worker' => { 'file' => 'lib/MCE/Core/Worker.pm', 'version' => '1.608' }, 'MCE::Flow' => { 'file' => 'lib/MCE/Flow.pm', 'version' => '1.608' }, 'MCE::Grep' => { 'file' => 'lib/MCE/Grep.pm', 'version' => '1.608' }, 'MCE::Loop' => { 'file' => 'lib/MCE/Loop.pm', 'version' => '1.608' }, 'MCE::Map' => { 'file' => 'lib/MCE/Map.pm', 'version' => '1.608' }, 'MCE::Mutex' => { 'file' => 'lib/MCE/Mutex.pm', 'version' => '1.608' }, 'MCE::Queue' => { 'file' => 'lib/MCE/Queue.pm', 'version' => '1.608' }, 'MCE::Relay' => { 'file' => 'lib/MCE/Relay.pm', 'version' => '1.608' }, 'MCE::Signal' => { 'file' => 'lib/MCE/Signal.pm', 'version' => '1.608' }, 'MCE::Step' => { 'file' => 'lib/MCE/Step.pm', 'version' => '1.608' }, 'MCE::Stream' => { 'file' => 'lib/MCE/Stream.pm', 'version' => '1.608' }, 'MCE::Subs' => { 'file' => 'lib/MCE/Subs.pm', 'version' => '1.608' }, 'MCE::Util' => { 'file' => 'lib/MCE/Util.pm', 'version' => '1.608' } } }) : ()), (($ExtUtils::MakeMaker::VERSION ge '6.48') ? (MIN_PERL_VERSION => 5.008) : ()), INSTALLDIRS => (($] < 5.011) ? 'perl' : 'site') ); MCE-1.608/examples/0000755000076400007640000000000012511673554012773 5ustar mariomarioMCE-1.608/examples/seq_demo.pl0000755000076400007640000000303612511671246015125 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use MCE; use Time::HiRes 'sleep'; ## A demonstration applying sequences with user_tasks. ## Chunking can also be configured independently as well. ## Run with seq_demo.pl | sort sub user_func { my ($mce, $seq_n, $chunk_id) = @_; my $wid = MCE->wid; my $task_id = MCE->task_id; my $task_wid = MCE->task_wid; if (ref $seq_n eq 'ARRAY') { ## seq_n or $_ is an array reference when chunk_size > 1 foreach (@{ $seq_n }) { MCE->printf( "task_id %d: seq_n %s: chunk_id %d: wid %d: task_wid %d\n", $task_id, $_, $chunk_id, $wid, $task_wid ); } } else { MCE->printf( "task_id %d: seq_n %s: chunk_id %d: wid %d: task_wid %d\n", $task_id, $seq_n, $chunk_id, $wid, $task_wid ); } sleep 0.003; return; } ## Each task can be configured uniquely. my $mce = MCE->new( user_tasks => [{ max_workers => 2, chunk_size => 1, sequence => { begin => 11, end => 19, step => 1 }, user_func => \&user_func },{ max_workers => 2, chunk_size => 5, sequence => { begin => 21, end => 29, step => 1 }, user_func => \&user_func },{ max_workers => 2, chunk_size => 3, sequence => { begin => 31, end => 39, step => 1 }, user_func => \&user_func }] ); $mce->run; MCE-1.608/examples/iterator.pl0000755000076400007640000001022712511671246015162 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## This script, similar to the forseq.pl example as far as usage goes, assigns ## input_data a closure (the iterator itself) by calling a factory function. ## ## usage: iterator.pl [ size ] ## usage: iterator.pl [ begin end [ step [ format ] ] ] ## ## e.g. iterator.pl 10 20 2 ## ## The format string is passed to sprintf (% is optional). ## ## e.g. iterator.pl 20 30 0.2 %4.1f ## iterator.pl 20 30 0.2 4.1f ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use Time::HiRes qw(time); use MCE; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; my $s_begin = shift || 3000; my $s_end = shift; my $s_step = shift || 1; my $s_format = shift; if ($s_begin !~ /\A\d*\.?\d*\z/) { print {*STDERR} "usage: $prog_name [ size ]\n"; print {*STDERR} "usage: $prog_name [ begin end [ step [ format ] ] ]\n"; exit; } $s_format =~ s/^%// if (defined $s_format); unless (defined $s_end) { $s_end = $s_begin - 1; $s_begin = 0; } ############################################################################### ## ---------------------------------------------------------------------------- ## Input and output iterators using closures. ## ## A closure construction typically involves two functions: the closure itself; ## and a factory, the fuction that creates the closure. ## ############################################################################### ## Generates a sequence of numbers. The external variables ($n, $max, $step) ## are used for keeping state across successive calls to the closure. The ## iterator returns undef when $n exceeds max. sub input_iterator { my ($n, $max, $step) = @_; return sub { return if $n > $max; my $current = $n; $n += $step; return $current; }; } ## Preserves output order. The external variables (%result_n, %result_d) are ## used for temporary storage for out-of-order results. The external variable ## ($order_id) is incremented after printing to STDOUT in orderly fashion. ## ## The external variables keep their state across successive calls to the ## closure. sub preserve_order { my (%result_n, %result_d); my $order_id = 1; return sub { my ($chunk_id, $n, $data) = @_; $result_n{ $chunk_id } = $n; $result_d{ $chunk_id } = $data; while (1) { last unless exists $result_d{$order_id}; printf "n: %s sqrt(n): %f\n", $result_n{$order_id}, $result_d{$order_id}; delete $result_n{$order_id}; delete $result_d{$order_id}; $order_id++; } return; }; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallelize via MCE. ## ############################################################################### ## use MCE::Flow; ## Same thing in MCE 1.5+ ## ## MCE::Flow::init { ## max_workers => 'auto', chunk_size => 1 ## }; ## ## sub _func { ## my ($mce, $chunk_ref, $chunk_id) = @_; ## ## if (defined $s_format) { ## my $n = sprintf "%${s_format}", $_; ## MCE->gather($chunk_id, $n, sqrt($n)); ## } ## else { ## MCE->gather($chunk_id, $_, sqrt($_)); ## } ## } ## ## mce_flow { ## input_data => input_iterator($s_begin, $s_end, $s_step), ## gather => preserve_order ## ## }, \&_func; my $mce = MCE->new( max_workers => 'auto', chunk_size => 1, gather => preserve_order, user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; if (defined $s_format) { my $n = sprintf "%${s_format}", $_; MCE->gather($chunk_id, $n, sqrt($n)); } else { MCE->gather($chunk_id, $_, sqrt($_)); } } )->spawn; my $start = time; $mce->process( input_iterator($s_begin, $s_end, $s_step) ); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/cat.pl0000755000076400007640000001711712511671246014105 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## Cat script similar to the cat binary. ## ## The logic below only supports -n -u options. The focus is demonstrating ## Many-Core Engine for Perl. ## ## This script was created to show how order can be preserved even though there ## are only 4 shared socket pairs in MCE no matter the number of workers. ## ## Try running with -n option against a large file with long lines. This ## script will out-perform the cat binary in that case. ## ## The usage description was largely ripped off from the cat man page. ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; sub INIT { ## Provide file globbing support under Windows similar to Unix. @ARGV = <@ARGV> if ($^O eq 'MSWin32'); } use MCE; ############################################################################### ## ---------------------------------------------------------------------------- ## Display usage and exit. ## ############################################################################### sub usage { print <<"::_USAGE_BLOCK_END_::"; NAME $prog_name -- concatenate and print files SYNOPSIS $prog_name [-nu] [file ...] DESCRIPTION The $prog_name utility reads files sequentially, writing them to the standard output. The file operands are processed in command-line order. If file is a single dash ('-') or absent, $prog_name reads the standard input. The following options are available: --max-workers MAX_WORKERS Specify number of workers for MCE -- default: auto --chunk-size CHUNK_SIZE Specify chunk size for MCE -- default: 2 MiB -n Number the output lines, starting at 1 -u Disable output buffering EXIT STATUS The $prog_name utility exits 0 on success, and >0 if an error occurs. EXAMPLES The command: $prog_name file1 will print the contents of file1 to the standard output. The command: $prog_name file1 file2 > file3 will sequentially print the contents of file1 and file2 to the file file3, truncating file3 if it already exists. The command: $prog_name file1 - file2 - file3 will print the contents of file1, print data it receives from the stan- dard input until it receives an EOF (typing 'Ctrl/Z' in Windows, 'Ctrl/D' in UNIX), print the contents of file2, read and output contents of the standard input again, then finally output the contents of file3. ::_USAGE_BLOCK_END_:: exit 1 } ############################################################################### ## ---------------------------------------------------------------------------- ## Define defaults and process command-line arguments. ## ############################################################################### my $flag = sub { 1; }; my $isOk = sub { (@ARGV == 0 or $ARGV[0] =~ /^-/) ? usage() : shift @ARGV; }; my $chunk_size = '2m'; my $max_workers = 'auto'; my $skip_args = 0; my $n_flag = 0; my $u_flag = 0; my @files = (); while ( my $arg = shift @ARGV ) { unless ($skip_args) { if ($arg eq '-') { push @files, $arg; next; } if ($arg =~ m/^-[nu]+$/) { while ($arg) { my $a = chop($arg); $n_flag = $flag->() and next if ($a eq 'n'); $u_flag = $flag->() and next if ($a eq 'u'); } next; } $skip_args = $flag->() and next if ($arg eq '--'); $max_workers = $isOk->() and next if ($arg =~ /^--max[-_]workers$/); $chunk_size = $isOk->() and next if ($arg =~ /^--chunk[-_]size$/); if ($arg =~ /^--max[-_]workers=(.+)/) { $max_workers = $1; next; } if ($arg =~ /^--chunk[-_]size=(.+)/) { $chunk_size = $1; next; } usage() if ($arg =~ /^-/); } push @files, $arg; } if ($n_flag == 0 && $max_workers eq 'auto') { $max_workers = 2; } ############################################################################### ## ---------------------------------------------------------------------------- ## Launch Many-Core Engine. ## ############################################################################### my $mce = MCE->new( chunk_size => $chunk_size, max_workers => $max_workers, init_relay => 0, use_slurpio => 1, user_func => sub { my ($mce, $chunk_ref, $chunk_id) = @_; if ($n_flag) { ## Relays the total lines read. my $output = ''; my $line_count = ($$chunk_ref =~ tr/\n//); my $lines_read = MCE::relay { $_ += $line_count }; open my $fh, '<', $chunk_ref; $output .= sprintf "%6d\t%s", ++$lines_read, $_ while (<$fh>); close $fh; $output .= ":$chunk_id"; MCE->do('display_chunk', $output); } else { ## The following is another way to have ordered output. Workers ## write directly to STDOUT exclusively without any involvement ## from the manager process. The statements between relay_recv ## and relay run serially and most important orderly. ## STDERR/OUT flush automatically inside worker threads and ## processes. Disable buffering on file handles otherwise. MCE->relay_recv; ## my $val = MCE->relay_recv; ## relay simply forwards 0 below print $$chunk_ref; ## exclusive access to STDOUT ## important, flush immediately MCE->relay; } return; } )->spawn; local $| = 1 if $u_flag; ############################################################################### ## ---------------------------------------------------------------------------- ## Concatenate and print files ## ############################################################################### my ($order_id, $lines, %tmp); my $exit_status = 0; sub display_chunk { ## One can have this receive 2 arguments; $chunk_id and $chunk_data. ## However, MCE->freeze is called when more than 1 argument is sent. ## For performance, $chunk_id is attached to the end of $_[0]. my $chunk_id = substr($_[0], rindex($_[0], ':') + 1); my $chop_len = length($chunk_id) + 1; substr($_[0], -$chop_len, $chop_len, ''); if ($chunk_id == $order_id && keys %tmp == 0) { ## no need to save in cache if orderly print $_[0]; $order_id++; } else { ## hold temporarily otherwise $tmp{$chunk_id} = $_[0]; while (1) { last unless exists $tmp{$order_id}; print delete $tmp{$order_id++}; } } return; } ## Process files, otherwise read from standard input. if (@files > 0) { foreach my $file (@files) { $order_id = 1; $lines = 0; if ($file eq '-') { open(STDIN, '<', ($^O eq 'MSWin32') ? 'CON' : '/dev/tty') or die $!; $mce->process(\*STDIN); } elsif (! -e $file) { print {*STDERR} "$prog_name: $file: No such file or directory\n"; $exit_status = 2; } elsif (-d $file) { print {*STDERR} "$prog_name: $file: Is a directory\n"; $exit_status = 1; } else { $mce->process($file); } } } else { $order_id = 1; $lines = 0; $mce->process(\*STDIN); } ## Shutdown Many-Core Engine and exit. $mce->shutdown; exit $exit_status; MCE-1.608/examples/files_thr.pl0000755000076400007640000000304412511671246015307 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; ## usage: ./files_thr.pl [ startdir ] use threads; use threads::shared; use Time::HiRes 'sleep'; use MCE; use Thread::Queue; my $D = Thread::Queue->new($ARGV[0] || '.'); my $F = Thread::Queue->new; ## Glob() is not thread-safe in Perl 5.16.x; okay < 5.16; fixed in 5.18.2. ## Run with perl5.12 on Mavericks. Not all OS vendors have patched 5.16.x. my $providers = ($] < 5.016000 || $] >= 5.018002) ? 3 : 1; my $consumers = 8; my $mce = MCE->new( task_end => sub { my ($mce, $task_id, $task_name) = @_; $F->enqueue((undef) x $consumers) if $task_name eq 'dir'; }, user_tasks => [{ max_workers => $providers, task_name => 'dir', user_func => sub { ## Allow time for wid 1 to enqueue any dir entries. ## Otherwise, workers (wid 2+) may terminate early. sleep 0.1 if MCE->task_wid > 1; while (defined (my $dir = $D->dequeue_nb)) { my (@files, @dirs); foreach (glob("$dir/*")) { if (-d $_) { push @dirs, $_; next; } push @files, $_; } $D->enqueue(@dirs ) if scalar @dirs; $F->enqueue(@files) if scalar @files; } } },{ max_workers => $consumers, task_name => 'file', user_func => sub { while (defined (my $file = $F->dequeue)) { MCE->say($file); } } }] )->run; MCE-1.608/examples/mutex.pl0000755000076400007640000000112512511671246014470 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use MCE::Flow max_workers => 4; use MCE::Mutex; print "## running a\n"; my $a = MCE::Mutex->new; mce_flow sub { $a->lock; ## access shared resource my $wid = MCE->wid; MCE->say($wid); sleep 1; $a->unlock; }; print "## running b\n"; my $b = MCE::Mutex->new; mce_flow sub { $b->synchronize( sub { ## access shared resource my ($wid) = @_; MCE->say($wid); sleep 1; }, MCE->wid ); }; MCE-1.608/examples/files_mce.pl0000755000076400007640000000256612511671246015266 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; ## usage: ./files_mce.pl [ startdir [0|1] ] use Time::HiRes 'sleep'; use MCE; use MCE::Queue; my $D = MCE::Queue->new(queue => [ $ARGV[0] || '.' ]); my $F = MCE::Queue->new(fast => defined $ARGV[1] ? $ARGV[1] : 1); my $providers = 3; my $consumers = 8; my $mce = MCE->new( task_end => sub { my ($mce, $task_id, $task_name) = @_; $F->enqueue((undef) x $consumers) if $task_name eq 'dir'; }, user_tasks => [{ max_workers => $providers, task_name => 'dir', user_func => sub { ## Allow time for wid 1 to enqueue any dir entries. ## Otherwise, workers (wid 2+) may terminate early. sleep 0.1 if MCE->task_wid > 1; while (defined (my $dir = $D->dequeue_nb)) { my (@files, @dirs); foreach (glob("$dir/*")) { if (-d $_) { push @dirs, $_; next; } push @files, $_; } $D->enqueue(@dirs ) if scalar @dirs; $F->enqueue(@files) if scalar @files; } } },{ max_workers => $consumers, task_name => 'file', user_func => sub { while (defined (my $file = $F->dequeue)) { MCE->say($file); } } }] )->run; MCE-1.608/examples/matmult/0000755000076400007640000000000012511673554014456 5ustar mariomarioMCE-1.608/examples/matmult/matmult_mce_d.pl0000644000076400007640000000617012511671246017625 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl matmult_mce_d.pl 1024 [ n_workers ] ## Default matrix size 512 ## ## Default n_workers 8 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::IO::FastRaw; use PDL::IO::Storable; ## Required for passing PDL data use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; my $pdl_version = sprintf("%20s", $PDL::VERSION); $pdl_version =~ s/_.*$//; my $chk_version = sprintf("%20s", '2.4.10'); if ($^O eq 'MSWin32' && $pdl_version lt $chk_version) { print "This script requires PDL 2.4.10 or later for PDL::IO::FastRaw\n"; print "to work under the Windows environment.\n"; exit 1; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; my $n_workers = @ARGV ? shift : 8; if ($tam !~ /^\d+$/ || $tam < 2) { die "error: $tam must be an integer greater than 1.\n"; } my $cols = $tam; my $rows = $tam; my $step_size = ($tam > 2048) ? 24 : ($tam > 1024) ? 16 : 8; my $mce = configure_and_spawn_mce($n_workers); my $a = sequence $cols,$rows; my $b = sequence $rows,$cols; my $c = zeroes $rows,$rows; writefraw($b, "$tmp_dir/b"); my $start = time; $mce->run(0, { sequence => [ 0, $rows - 1, $step_size ], user_args => { b => "$tmp_dir/b" } } ); my $end = time; $mce->shutdown; ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$rows-1, $rows-1]) { my ($col, $row) = @$pair; $col %= $rows; $row %= $rows; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub get_rows_a { my ($start) = @_; my $stop = $start + $step_size - 1; $stop = $rows - 1 if ($stop >= $rows); return $a->slice(":,$start:$stop"); } sub insert_rows { my ($seq_n, $result_chunk) = @_; ins(inplace($c), $result_chunk, 0, $seq_n); return; } sub configure_and_spawn_mce { my $n_workers = shift || 8; return MCE->new( max_workers => $n_workers, job_delay => ($tam > 2048) ? 0.031 : undef, user_begin => sub { my ($self) = @_; $self->{matrix_b} = mapfraw($self->{user_args}->{b}); }, user_func => sub { my ($self, $seq_n, $chunk_id) = @_; my $a_chunk = $self->do('get_rows_a', $seq_n); my $result_chunk = $a_chunk x $self->{matrix_b}; $self->do('insert_rows', $seq_n, $result_chunk); return; } )->spawn; } MCE-1.608/examples/matmult/matmult_mce_f.pl0000644000076400007640000000571612511671246017634 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl matmult_mce_f.pl 1024 [ n_workers ] ## Default matrix size 512 ## ## Default n_workers 8 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::IO::FastRaw; use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; my $pdl_version = sprintf("%20s", $PDL::VERSION); $pdl_version =~ s/_.*$//; my $chk_version = sprintf("%20s", '2.4.10'); if ($^O eq 'MSWin32' && $pdl_version lt $chk_version) { print "This script requires PDL 2.4.10 or later for PDL::IO::FastRaw\n"; print "to work under the Windows environment.\n"; exit 1; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; my $n_workers = @ARGV ? shift : 8; if ($tam !~ /^\d+$/ || $tam < 2) { die "error: $tam must be an integer greater than 1.\n"; } my $cols = $tam; my $rows = $tam; my $step_size = ($tam > 2048) ? 24 : ($tam > 1024) ? 16 : 8; my $mce = configure_and_spawn_mce($n_workers); writefraw(sequence($cols,$rows), "$tmp_dir/a"); writefraw(sequence($rows,$cols), "$tmp_dir/b"); writefraw(zeroes($rows,$rows), "$tmp_dir/c"); my $start = time; $mce->run(0, { sequence => [ 0, $rows - 1, $step_size ] } ); my $end = time; $mce->shutdown; ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; my $c = mapfraw("$tmp_dir/c"); for my $pair ([0, 0], [324, 5], [42, 172], [$rows-1, $rows-1]) { my ($col, $row) = @$pair; $col %= $rows; $row %= $rows; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub configure_and_spawn_mce { my $n_workers = shift || 8; return MCE->new( max_workers => $n_workers, user_begin => sub { my ($self) = @_; $self->{l} = mapfraw("$tmp_dir/a"); $self->{r} = mapfraw("$tmp_dir/b"); $self->{o} = mapfraw("$tmp_dir/c"); }, user_func => sub { my ($self, $seq_n, $chunk_id) = @_; my $l = $self->{l}; my $r = $self->{r}; my $o = $self->{o}; my $start = $seq_n; my $stop = $start + $step_size - 1; $stop = $rows - 1 if ($stop >= $rows); use PDL::NiceSlice; $o(:,$start:$stop) .= $l(:,$start:$stop) x $r; no PDL::NiceSlice; return; } )->spawn; } MCE-1.608/examples/matmult/matmult_mce_t.pl0000644000076400007640000000523412511671246017645 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl matmult_mce_t.pl 1024 [ n_workers ] ## Default matrix size 512 ## ## Default n_workers 8 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::Parallel::threads qw(retrieve_pdls); use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; my $n_workers = @ARGV ? shift : 8; if ($tam !~ /^\d+$/ || $tam < 2) { die "error: $tam must be an integer greater than 1.\n"; } my $cols = $tam; my $rows = $tam; my $step_size = ($tam > 2048) ? 24 : ($tam > 1024) ? 16 : 8; my $mce = configure_and_spawn_mce($n_workers); my $a = sequence $cols,$rows; my $b = sequence $rows,$cols; my $c = zeroes $rows,$rows; $a->share_as('left_input'); $b->share_as('right_input'); $c->share_as('output'); my $start = time; $mce->run(0, { sequence => [ 0, $rows - 1, $step_size ] } ); my $end = time; $mce->shutdown; ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$rows-1, $rows-1]) { my ($col, $row) = @$pair; $col %= $rows; $row %= $rows; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub configure_and_spawn_mce { my $n_workers = shift || 8; return MCE->new( max_workers => $n_workers, user_begin => sub { my ($self) = @_; ( $self->{l}, $self->{r}, $self->{o} ) = retrieve_pdls( 'left_input', 'right_input', 'output' ); }, user_func => sub { my ($self, $seq_n, $chunk_id) = @_; my $l = $self->{l}; my $r = $self->{r}; my $o = $self->{o}; my $start = $seq_n; my $stop = $start + $step_size - 1; $stop = $rows - 1 if ($stop >= $rows); use PDL::NiceSlice; $o(:,$start:$stop) .= $l(:,$start:$stop) x $r; no PDL::NiceSlice; return; } )->spawn; } MCE-1.608/examples/matmult/strassen_perl.pl0000644000076400007640000001706112511671246017700 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl strassen_perl.pl 1024 ## Default matrix size 512 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; ## Wants power of 2 only unless (is_power_of_two($tam)) { die "error: $tam must be a power of 2 integer.\n"; } my $mce; $mce = configure_and_spawn_mce() if ($tam > 64); my $a = [ ]; my $b = [ ]; my $c = [ ]; my $rows = $tam; my $cols = $tam; my $cnt; $cnt = 0; for (0 .. $rows - 1) { $a->[$_] = [ $cnt .. $cnt + $cols - 1 ]; $cnt += $cols; } $cnt = 0; for (0 .. $cols - 1) { $b->[$_] = [ $cnt .. $cnt + $rows - 1 ]; $cnt += $rows; } my $start = time; strassen($a, $b, $c, $tam, $mce); my $end = time; ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$tam-1, $tam-1]) { my ($col, $row) = @$pair; $col %= $tam; $row %= $tam; printf "## (%d, %d): %s\n", $col, $row, $c->[$row][$col]; } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my @p; sub store_result { my ($n, $result) = @_; $p[$n] = $result; return; } sub configure_and_spawn_mce { return MCE->new( max_workers => 7, user_func => sub { my $self = $_[0]; my $data = $self->{user_data}; my $tam = $data->[3]; my $result = [ ]; strassen_r($data->[0], $data->[1], $result, $tam); $self->do('store_result', $data->[2], $result); } )->spawn; } sub is_power_of_two { my ($n) = @_; return 0 if ($n !~ /^\d+$/); return ($n != 0 && (($n & $n - 1) == 0)); } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen { my ($a, $b, $c, $tam, $mce) = @_; if ($tam <= 64) { for my $i (0 .. $tam - 1) { for my $j (0 .. $tam - 1) { $c->[$i][$j] = 0; for my $k (0 .. $tam - 1) { $c->[$i][$j] += $a->[$i][$k] * $b->[$k][$j]; } } } return; } my ($p1, $p2, $p3, $p4, $p5, $p6, $p7); my $nTam = $tam / 2; my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); my $t1 = [ ]; my $t2 = [ ]; sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); $mce->send([ $t1, $t2, 1, $nTam ]); sum_m($a21, $a22, $t1, $nTam); $mce->send([ $t1, $b11, 2, $nTam ]); subtract_m($b12, $b22, $t2, $nTam); $mce->send([ $a11, $t2, 3, $nTam ]); subtract_m($b21, $b11, $t2, $nTam); $mce->send([ $a22, $t2, 4, $nTam ]); sum_m($a11, $a12, $t1, $nTam); $mce->send([ $t1, $b22, 5, $nTam ]); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); $mce->send([ $t1, $t2, 6, $nTam ]); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); $mce->send([ $t1, $t2, 7, $nTam ]); $mce->run; $p1 = $p[1]; $p2 = $p[2]; $p3 = $p[3]; $p4 = $p[4]; $p5 = $p[5]; $p6 = $p[6]; $p7 = $p[7]; calc_m($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $nTam); return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen_r { my ($a, $b, $c, $tam) = @_; ## Perform the classic multiplication when matrix is <= 64 X 64 if ($tam <= 64) { for my $i (0 .. $tam - 1) { for my $j (0 .. $tam - 1) { $c->[$i][$j] = 0; for my $k (0 .. $tam - 1) { $c->[$i][$j] += $a->[$i][$k] * $b->[$k][$j]; } } } return; } ## Otherwise, perform multiplication using Strassen's algorithm my $nTam = $tam / 2; my $t1 = [ ]; my $t2 = [ ]; my $p1 = [ ]; my $p2 = [ ]; my $p3 = [ ]; my $p4 = [ ]; my $p5 = [ ]; my $p6 = [ ]; my $p7 = [ ]; ## Divide the matrices into 4 sub-matrices my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); ## Calculate p1 to p7 sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); strassen_r($t1, $t2, $p1, $nTam); sum_m($a21, $a22, $t1, $nTam); strassen_r($t1, $b11, $p2, $nTam); subtract_m($b12, $b22, $t2, $nTam); strassen_r($a11, $t2, $p3, $nTam); subtract_m($b21, $b11, $t2, $nTam); strassen_r($a22, $t2, $p4, $nTam); sum_m($a11, $a12, $t1, $nTam); strassen_r($t1, $b22, $p5, $nTam); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); strassen_r($t1, $t2, $p6, $nTam); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); strassen_r($t1, $t2, $p7, $nTam); ## Calculate and group into a single matrix $c calc_m($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $nTam); return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub divide_m { my ($m, $tam) = @_; my $m11 = [ ]; my $m12 = [ ]; my $m21 = [ ]; my $m22 = [ ]; for my $i (0 .. $tam - 1) { for my $j (0 .. $tam - 1) { $m11->[$i][$j] = $m->[$i][$j]; $m12->[$i][$j] = $m->[$i][$j + $tam]; $m21->[$i][$j] = $m->[$i + $tam][$j]; $m22->[$i][$j] = $m->[$i + $tam][$j + $tam]; } } return ($m11, $m12, $m21, $m22); } sub calc_m { my ($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $tam) = @_; my $t1 = [ ]; my $t2 = [ ]; sum_m($p1, $p4, $t1, $tam); sum_m($t1, $p7, $t2, $tam); subtract_m($t2, $p5, $p7, $tam); ## reuse $p7 to store c11 sum_m($p1, $p3, $t1, $tam); sum_m($t1, $p6, $t2, $tam); subtract_m($t2, $p2, $p6, $tam); ## reuse $p6 to store c22 sum_m($p3, $p5, $p1, $tam); ## reuse $p1 to store c12 sum_m($p2, $p4, $p3, $tam); ## reuse $p3 to store c21 for my $i (0 .. $tam - 1) { for my $j (0 .. $tam - 1) { $c->[$i][$j] = $p7->[$i][$j]; ## c11 = $p7 $c->[$i][$j + $tam] = $p1->[$i][$j]; ## c12 = $p1 $c->[$i + $tam][$j] = $p3->[$i][$j]; ## c21 = $p3 $c->[$i + $tam][$j + $tam] = $p6->[$i][$j]; ## c22 = $p6 } } return; } sub sum_m { my ($a, $b, $r, $tam) = @_; for my $i (0 .. $tam - 1) { for my $j (0 .. $tam - 1) { $r->[$i][$j] = $a->[$i][$j] + $b->[$i][$j]; } } return; } sub subtract_m { my ($a, $b, $r, $tam) = @_; for my $i (0 .. $tam - 1) { for my $j (0 .. $tam - 1) { $r->[$i][$j] = $a->[$i][$j] - $b->[$i][$j]; } } return; } MCE-1.608/examples/matmult/strassen_49_t.pl0000644000076400007640000002400412511671246017510 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl strassen_49_t.pl 1024 ## Default matrix size 512 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::Parallel::threads qw(retrieve_pdls free_pdls); use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; ## Wants power of 2 only unless (is_power_of_two($tam)) { die "error: $tam must be a power of 2 integer.\n"; } my $mce; $mce = configure_and_spawn_mce() if ($tam > 128); my $a = sequence $tam,$tam; my $b = sequence $tam,$tam; my $c = zeroes $tam,$tam; my $start = time; strassen($a, $b, $c, $tam, $mce); my $end = time; $mce->shutdown if (defined $mce); ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$tam-1, $tam-1]) { my ($col, $row) = @$pair; $col %= $tam; $row %= $tam; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub configure_and_spawn_mce { return MCE->new( max_workers => 49, user_func => sub { my $self = $_[0]; my $data = $self->{user_data}; my $sess_dir = $self->sess_dir; my $tam = $data->[1]; my $result = zeroes $tam,$tam; my ($a, $b) = retrieve_pdls( "$sess_dir/". $data->[0] ."a", "$sess_dir/". $data->[0] ."b" ); strassen_r($a, $b, $result, $tam); free_pdls($a, $b); $result->share_as("$sess_dir/p" . $data->[0]); } )->spawn; } sub is_power_of_two { my ($n) = @_; return 0 if ($n !~ /^\d+$/); return ($n != 0 && (($n & $n - 1) == 0)); } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub submit { my ($a, $b, $c, $tam, $mce, $t1, $t2) = @_; my $sess_dir = $mce->sess_dir; my $nTam = $tam / 2; my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); $t1->copy->share_as( "$sess_dir/". ($c + 1) ."a"); $t2->copy->share_as( "$sess_dir/". ($c + 1) ."b"); $mce->send([ $c + 1, $nTam ]); sum_m($a21, $a22, $t1, $nTam); $t1->copy->share_as( "$sess_dir/". ($c + 2) ."a"); $b11->copy->share_as("$sess_dir/". ($c + 2) ."b"); $mce->send([ $c + 2, $nTam ]); subtract_m($b12, $b22, $t2, $nTam); $a11->copy->share_as("$sess_dir/". ($c + 3) ."a"); $t2->copy->share_as( "$sess_dir/". ($c + 3) ."b"); $mce->send([ $c + 3, $nTam ]); subtract_m($b21, $b11, $t2, $nTam); $a22->copy->share_as("$sess_dir/". ($c + 4) ."a"); $t2->copy->share_as( "$sess_dir/". ($c + 4) ."b"); $mce->send([ $c + 4, $nTam ]); sum_m($a11, $a12, $t1, $nTam); $t1->copy->share_as( "$sess_dir/". ($c + 5) ."a"); $b22->copy->share_as("$sess_dir/". ($c + 5) ."b"); $mce->send([ $c + 5, $nTam ]); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); $t1->copy->share_as( "$sess_dir/". ($c + 6) ."a"); $t2->copy->share_as( "$sess_dir/". ($c + 6) ."b"); $mce->send([ $c + 6, $nTam ]); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); unless ($c == 10) { $t1->copy->share_as( "$sess_dir/". ($c + 7) ."a"); $t2->copy->share_as( "$sess_dir/". ($c + 7) ."b"); } else { $t1->share_as( "$sess_dir/". ($c + 7) ."a"); $t2->share_as( "$sess_dir/". ($c + 7) ."b"); } $mce->send([ $c + 7, $nTam ]); return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen { my ($a, $b, $c, $tam, $mce) = @_; if ($tam <= 128) { ins(inplace($c), $a x $b); return; } my $sess_dir = $mce->sess_dir; my (@p, $p1, $p2, $p3, $p4, $p5, $p6, $p7); my $nTam = $tam / 2; my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); my $t1 = zeroes $nTam,$nTam; my $u1 = zeroes $nTam/2,$nTam/2; my $u2 = zeroes $nTam/2,$nTam/2; sum_m($a21, $a22, $t1, $nTam); submit($t1, $b11, 20, $nTam, $mce, $u1, $u2); subtract_m($b12, $b22, $t1, $nTam); submit($a11, $t1, 30, $nTam, $mce, $u1, $u2); subtract_m($b21, $b11, $t1, $nTam); submit($a22, $t1, 40, $nTam, $mce, $u1, $u2); sum_m($a11, $a12, $t1, $nTam); submit($t1, $b22, 50, $nTam, $mce, $u1, $u2); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $a12, $nTam); ## Reuse $a12 submit($t1, $a12, 70, $nTam, $mce, $u1, $u2); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $a12, $nTam); ## Reuse $a12 submit($t1, $a12, 60, $nTam, $mce, $u1, $u2); sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $a12, $nTam); ## Reuse $a12 submit($t1, $a12, 10, $nTam, $mce, $u1, $u2); $mce->run(0); for my $i (1 .. 7) { $p[$i] = zeroes $nTam,$nTam; ($p1, $p2, $p3, $p4, $p5, $p6, $p7) = retrieve_pdls( "$sess_dir/p$i"."1", "$sess_dir/p$i"."2", "$sess_dir/p$i"."3", "$sess_dir/p$i"."4", "$sess_dir/p$i"."5", "$sess_dir/p$i"."6", "$sess_dir/p$i"."7" ); calc_m($p1, $p2, $p3, $p4, $p5, $p6, $p7, $p[$i], $nTam/2, $u1,$u2); free_pdls($p1, $p2, $p3, $p4, $p5, $p6, $p7); } calc_m($p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7], $c, $nTam, $t1, $a12); return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen_r { my ($a, $b, $c, $tam) = @_; ## Perform the classic multiplication when matrix is <= 128 X 128 if ($tam <= 128) { ins(inplace($c), $a x $b); return; } ## Otherwise, perform multiplication using Strassen's algorithm my $nTam = $tam / 2; my $p2 = zeroes $nTam,$nTam; my $p3 = zeroes $nTam,$nTam; my $p4 = zeroes $nTam,$nTam; my $p5 = zeroes $nTam,$nTam; ## Divide the matrices into 4 sub-matrices my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); ## Calculate p1 to p7 my $t1 = zeroes $nTam,$nTam; sum_m($a21, $a22, $t1, $nTam); strassen_r($t1, $b11, $p2, $nTam); subtract_m($b12, $b22, $t1, $nTam); strassen_r($a11, $t1, $p3, $nTam); subtract_m($b21, $b11, $t1, $nTam); strassen_r($a22, $t1, $p4, $nTam); sum_m($a11, $a12, $t1, $nTam); strassen_r($t1, $b22, $p5, $nTam); subtract_m($p4, $p5, $t1, $nTam); ## c11 ins(inplace($c), $t1, 0, 0); sum_m($p3, $p5, $t1, $nTam); ## c12 ins(inplace($c), $t1, $nTam, 0); sum_m($p2, $p4, $t1, $nTam); ## c21 ins(inplace($c), $t1, 0, $nTam); subtract_m($p3, $p2, $t1, $nTam); ## c22 ins(inplace($c), $t1, $nTam, $nTam); my $t2 = zeroes $nTam,$nTam; sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); strassen_r($t1, $t2, $p2, $nTam); ## Reuse $p2 to store p1 subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); strassen_r($t1, $t2, $p3, $nTam); ## Reuse $p3 to store p6 subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); strassen_r($t1, $t2, $p4, $nTam); ## Reuse $p4 to store p7 my $n1 = $nTam - 1; my $n2 = $nTam + $n1; sum_m($p2, $p4, $t1, $nTam); ## c11 use PDL::NiceSlice; $c(0:$n1,0:$n1) += $t1; no PDL::NiceSlice; sum_m($p2, $p3, $t1, $nTam); ## c22 use PDL::NiceSlice; $c($nTam:$n2,$nTam:$n2) += $t1; no PDL::NiceSlice; return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub divide_m { my ($m, $tam) = @_; my $n1 = $tam - 1; my $n2 = $tam + $n1; return ( $m->slice("0:$n1,0:$n1"), ## m11 $m->slice("$tam:$n2,0:$n1"), ## m12 $m->slice("0:$n1,$tam:$n2"), ## m21 $m->slice("$tam:$n2,$tam:$n2") ## m22 ); } sub calc_m { my ($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $tam, $t1, $t2) = @_; sum_m($p1, $p4, $t1, $tam); sum_m($t1, $p7, $t2, $tam); subtract_m($t2, $p5, $p7, $tam); ## reuse $p7 to store c11 sum_m($p1, $p3, $t1, $tam); sum_m($t1, $p6, $t2, $tam); subtract_m($t2, $p2, $p6, $tam); ## reuse $p6 to store c22 sum_m($p3, $p5, $p1, $tam); ## reuse $p1 to store c12 sum_m($p2, $p4, $p3, $tam); ## reuse $p3 to store c21 ins(inplace($c), $p7, 0, 0); ## c11 = $p7 ins(inplace($c), $p1, $tam, 0); ## c12 = $p1 ins(inplace($c), $p3, 0, $tam); ## c21 = $p3 ins(inplace($c), $p6, $tam, $tam); ## c22 = $p6 return; } sub sum_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a + $b); return; } sub subtract_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a - $b); return; } MCE-1.608/examples/matmult/matmult_base.pl0000644000076400007640000000240712511671246017467 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl matmult_base.pl 1024 ## Default matrix size 512 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; if ($tam !~ /^\d+$/ || $tam < 2) { die "error: $tam must be an integer greater than 1.\n"; } my $cols = $tam; my $rows = $tam; my $a = sequence $cols,$rows; my $b = sequence $rows,$cols; my $start = time; my $c = $a x $b; ## Performs matrix multiplication my $end = time; ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$rows-1, $rows-1]) { my ($col, $row) = @$pair; $col %= $rows; $row %= $rows; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; MCE-1.608/examples/matmult/strassen_07_f.pl0000644000076400007640000002150512511671246017467 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl strassen_07_f.pl 1024 ## Default matrix size 512 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::IO::FastRaw; use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; my $pdl_version = sprintf("%20s", $PDL::VERSION); $pdl_version =~ s/_.*$//; my $chk_version = sprintf("%20s", '2.4.10'); if ($^O eq 'MSWin32' && $pdl_version lt $chk_version) { print "This script requires PDL 2.4.10 or later for PDL::IO::FastRaw\n"; print "to work under the Windows environment.\n"; exit 1; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; ## Wants power of 2 only unless (is_power_of_two($tam)) { die "error: $tam must be a power of 2 integer.\n"; } my $mce; $mce = configure_and_spawn_mce() if ($tam > 128); my $a = sequence $tam,$tam; my $b = sequence $tam,$tam; my $c = zeroes $tam,$tam; my $start = time; strassen($a, $b, $c, $tam, $mce); my $end = time; $mce->shutdown if (defined $mce); ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$tam-1, $tam-1]) { my ($col, $row) = @$pair; $col %= $tam; $row %= $tam; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub configure_and_spawn_mce { return MCE->new( max_workers => 7, user_func => sub { my $self = $_[0]; my $data = $self->{user_data}; my $tam = $data->[3]; my $result = zeroes $tam,$tam; my $a = mapfraw($data->[0]); my $b = mapfraw($data->[1]); strassen_r($a, $b, $result, $tam); undef $a; undef $b; unlink $data->[0]; unlink $data->[0] . ".hdr"; unlink $data->[1]; unlink $data->[1] . ".hdr"; writefraw($result, $self->sess_dir . "/p" . $data->[2]); } )->spawn; } sub is_power_of_two { my ($n) = @_; return 0 if ($n !~ /^\d+$/); return ($n != 0 && (($n & $n - 1) == 0)); } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen { my ($a, $b, $c, $tam, $mce) = @_; if ($tam <= 128) { ins(inplace($c), $a x $b); return; } my $sess_dir = $mce->sess_dir; my ($p1, $p2, $p3, $p4, $p5, $p6, $p7); my $nTam = $tam / 2; my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); my $t1 = zeroes $nTam,$nTam; sum_m($a21, $a22, $t1, $nTam); writefraw($t1, "$sess_dir/2a"); writefraw($b11, "$sess_dir/2b"); $mce->send([ "$sess_dir/2a", "$sess_dir/2b", 2, $nTam ]); subtract_m($b12, $b22, $t1, $nTam); writefraw($a11, "$sess_dir/3a"); writefraw($t1, "$sess_dir/3b"); $mce->send([ "$sess_dir/3a", "$sess_dir/3b", 3, $nTam ]); subtract_m($b21, $b11, $t1, $nTam); writefraw($a22, "$sess_dir/4a"); writefraw($t1, "$sess_dir/4b"); $mce->send([ "$sess_dir/4a", "$sess_dir/4b", 4, $nTam ]); sum_m($a11, $a12, $t1, $nTam); writefraw($t1, "$sess_dir/5a"); writefraw($b22, "$sess_dir/5b"); $mce->send([ "$sess_dir/5a", "$sess_dir/5b", 5, $nTam ]); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $a12, $nTam); ## Reuse $a12 writefraw($t1, "$sess_dir/7a"); writefraw($a12, "$sess_dir/7b"); $mce->send([ "$sess_dir/7a", "$sess_dir/7b", 7, $nTam ]); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $a12, $nTam); ## Reuse $a12 writefraw($t1, "$sess_dir/6a"); writefraw($a12, "$sess_dir/6b"); $mce->send([ "$sess_dir/6a", "$sess_dir/6b", 6, $nTam ]); sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $a12, $nTam); ## Reuse $a12 writefraw($t1, "$sess_dir/1a"); writefraw($a12, "$sess_dir/1b"); $mce->send([ "$sess_dir/1a", "$sess_dir/1b", 1, $nTam ]); $mce->run(0); $p1 = mapfraw("$sess_dir/p1"); $p2 = mapfraw("$sess_dir/p2"); $p3 = mapfraw("$sess_dir/p3"); $p4 = mapfraw("$sess_dir/p4"); $p5 = mapfraw("$sess_dir/p5"); $p6 = mapfraw("$sess_dir/p6"); $p7 = mapfraw("$sess_dir/p7"); calc_m($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $nTam, $t1, $a12); undef $p1; undef $p2; undef $p3; undef $p4; undef $p5; undef $p6; undef $p7; for (1 .. 7) { unlink "$sess_dir/p$_"; unlink "$sess_dir/p$_.hdr"; } return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen_r { my ($a, $b, $c, $tam) = @_; ## Perform the classic multiplication when matrix is <= 128 X 128 if ($tam <= 128) { ins(inplace($c), $a x $b); return; } ## Otherwise, perform multiplication using Strassen's algorithm my $nTam = $tam / 2; my $p2 = zeroes $nTam,$nTam; my $p3 = zeroes $nTam,$nTam; my $p4 = zeroes $nTam,$nTam; my $p5 = zeroes $nTam,$nTam; ## Divide the matrices into 4 sub-matrices my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); ## Calculate p1 to p7 my $t1 = zeroes $nTam,$nTam; sum_m($a21, $a22, $t1, $nTam); strassen_r($t1, $b11, $p2, $nTam); subtract_m($b12, $b22, $t1, $nTam); strassen_r($a11, $t1, $p3, $nTam); subtract_m($b21, $b11, $t1, $nTam); strassen_r($a22, $t1, $p4, $nTam); sum_m($a11, $a12, $t1, $nTam); strassen_r($t1, $b22, $p5, $nTam); subtract_m($p4, $p5, $t1, $nTam); ## c11 ins(inplace($c), $t1, 0, 0); sum_m($p3, $p5, $t1, $nTam); ## c12 ins(inplace($c), $t1, $nTam, 0); sum_m($p2, $p4, $t1, $nTam); ## c21 ins(inplace($c), $t1, 0, $nTam); subtract_m($p3, $p2, $t1, $nTam); ## c22 ins(inplace($c), $t1, $nTam, $nTam); my $t2 = zeroes $nTam,$nTam; sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); strassen_r($t1, $t2, $p2, $nTam); ## Reuse $p2 to store p1 subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); strassen_r($t1, $t2, $p3, $nTam); ## Reuse $p3 to store p6 subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); strassen_r($t1, $t2, $p4, $nTam); ## Reuse $p4 to store p7 my $n1 = $nTam - 1; my $n2 = $nTam + $n1; sum_m($p2, $p4, $t1, $nTam); ## c11 use PDL::NiceSlice; $c(0:$n1,0:$n1) += $t1; no PDL::NiceSlice; sum_m($p2, $p3, $t1, $nTam); ## c22 use PDL::NiceSlice; $c($nTam:$n2,$nTam:$n2) += $t1; no PDL::NiceSlice; return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub divide_m { my ($m, $tam) = @_; my $n1 = $tam - 1; my $n2 = $tam + $n1; return ( $m->slice("0:$n1,0:$n1"), ## m11 $m->slice("$tam:$n2,0:$n1"), ## m12 $m->slice("0:$n1,$tam:$n2"), ## m21 $m->slice("$tam:$n2,$tam:$n2") ## m22 ); } sub calc_m { my ($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $tam, $t1, $t2) = @_; sum_m($p1, $p4, $t1, $tam); sum_m($t1, $p7, $t2, $tam); subtract_m($t2, $p5, $p7, $tam); ## reuse $p7 to store c11 sum_m($p1, $p3, $t1, $tam); sum_m($t1, $p6, $t2, $tam); subtract_m($t2, $p2, $p6, $tam); ## reuse $p6 to store c22 sum_m($p3, $p5, $p1, $tam); ## reuse $p1 to store c12 sum_m($p2, $p4, $p3, $tam); ## reuse $p3 to store c21 ins(inplace($c), $p7, 0, 0); ## c11 = $p7 ins(inplace($c), $p1, $tam, 0); ## c12 = $p1 ins(inplace($c), $p3, 0, $tam); ## c21 = $p3 ins(inplace($c), $p6, $tam, $tam); ## c22 = $p6 return; } sub sum_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a + $b); return; } sub subtract_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a - $b); return; } MCE-1.608/examples/matmult/matmult_simd.pl0000644000076400007640000000435712511671246017517 0ustar mariomario#!/usr/bin/env perl ## ## This script was taken from https://gist.github.com/run4flat/4942132 for ## folks wanting to review, study, and compare with MCE. Script was modified ## to support the optional N_threads argument. ## ## Usage: ## perl matmult_simd.pl 1024 [ N_threads ] ## Default matrix size 512 ## ## Default N_threads 8 ## ## by David Mertens ## based on code by Mario Roy ## ################# # Preliminaries # ################# use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::Parallel::threads qw(retrieve_pdls); use PDL::Parallel::threads::SIMD qw(parallel_id parallelize); # Get the matrix size and croak on bad input my $tam = @ARGV ? shift : 512; die "error: $tam must be an integer greater than 1.\n" if $tam !~ /^\d+$/ or $tam < 2; my $cols = $tam; my $rows = $tam; ########################### # Create some shared data # ########################### sequence($cols, $rows)->share_as('left_input'); sequence($rows, $cols)->share_as('right_input'); my $output = zeroes($rows, $rows)->share_as('output'); my $N_threads = @ARGV ? shift : 8; ################################### # Run the calculation in parallel # ################################### my $start = time; parallelize { my ($l, $r, $o) = retrieve_pdls('left_input', 'right_input', 'output'); my $pid = parallel_id; # chop up the input matrix based on the number of rows and the number # of threads. my $step = int($rows / $N_threads + 0.99); my $start = $pid * $step; my $stop = ($pid + 1) * $step - 1; $stop = $rows - 1 if $stop >= $rows; use PDL::NiceSlice; $o(:, $start:$stop) .= $l(:,$start:$stop) x $r; no PDL::NiceSlice; } $N_threads; my $end = time; ######################### # Print results # ######################### printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$tam-1, $tam-1]) { my ($row, $col) = @$pair; $row %= $rows; $col %= $cols; print "## ($row, $col): ", $output->at($row, $col), "\n"; } print "\n"; MCE-1.608/examples/matmult/strassen_49_f.pl0000644000076400007640000002467612511671246017511 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl strassen_49_f.pl 1024 ## Default matrix size 512 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::IO::FastRaw; use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; my $pdl_version = sprintf("%20s", $PDL::VERSION); $pdl_version =~ s/_.*$//; my $chk_version = sprintf("%20s", '2.4.10'); if ($^O eq 'MSWin32' && $pdl_version lt $chk_version) { print "This script requires PDL 2.4.10 or later for PDL::IO::FastRaw\n"; print "to work under the Windows environment.\n"; exit 1; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; ## Wants power of 2 only unless (is_power_of_two($tam)) { die "error: $tam must be a power of 2 integer.\n"; } my $mce; $mce = configure_and_spawn_mce() if ($tam > 128); my $a = sequence $tam,$tam; my $b = sequence $tam,$tam; my $c = zeroes $tam,$tam; my $start = time; strassen($a, $b, $c, $tam, $mce); my $end = time; $mce->shutdown if (defined $mce); ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$tam-1, $tam-1]) { my ($col, $row) = @$pair; $col %= $tam; $row %= $tam; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub configure_and_spawn_mce { return MCE->new( max_workers => 49, user_func => sub { my $self = $_[0]; my $data = $self->{user_data}; my $sess_dir = $self->sess_dir; my $tam = $data->[1]; my $result = zeroes $tam,$tam; my $a = mapfraw("$sess_dir/". $data->[0] ."a"); my $b = mapfraw("$sess_dir/". $data->[0] ."b"); strassen_r($a, $b, $result, $tam); undef $a; undef $b; unlink "$sess_dir/". $data->[0] ."a"; unlink "$sess_dir/". $data->[0] ."a.hdr"; unlink "$sess_dir/". $data->[0] ."b"; unlink "$sess_dir/". $data->[0] ."b.hdr"; writefraw($result, "$sess_dir/p" . $data->[0]); } )->spawn; } sub is_power_of_two { my ($n) = @_; return 0 if ($n !~ /^\d+$/); return ($n != 0 && (($n & $n - 1) == 0)); } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub submit { my ($a, $b, $c, $tam, $mce, $t1, $t2) = @_; my $sess_dir = $mce->sess_dir; my $nTam = $tam / 2; my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); writefraw($t1, "$sess_dir/". ($c + 1) ."a"); writefraw($t2, "$sess_dir/". ($c + 1) ."b"); $mce->send([ $c + 1, $nTam ]); sum_m($a21, $a22, $t1, $nTam); writefraw($t1, "$sess_dir/". ($c + 2) ."a"); writefraw($b11, "$sess_dir/". ($c + 2) ."b"); $mce->send([ $c + 2, $nTam ]); subtract_m($b12, $b22, $t2, $nTam); writefraw($a11, "$sess_dir/". ($c + 3) ."a"); writefraw($t2, "$sess_dir/". ($c + 3) ."b"); $mce->send([ $c + 3, $nTam ]); subtract_m($b21, $b11, $t2, $nTam); writefraw($a22, "$sess_dir/". ($c + 4) ."a"); writefraw($t2, "$sess_dir/". ($c + 4) ."b"); $mce->send([ $c + 4, $nTam ]); sum_m($a11, $a12, $t1, $nTam); writefraw($t1, "$sess_dir/". ($c + 5) ."a"); writefraw($b22, "$sess_dir/". ($c + 5) ."b"); $mce->send([ $c + 5, $nTam ]); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); writefraw($t1, "$sess_dir/". ($c + 6) ."a"); writefraw($t2, "$sess_dir/". ($c + 6) ."b"); $mce->send([ $c + 6, $nTam ]); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); writefraw($t1, "$sess_dir/". ($c + 7) ."a"); writefraw($t2, "$sess_dir/". ($c + 7) ."b"); $mce->send([ $c + 7, $nTam ]); return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen { my ($a, $b, $c, $tam, $mce) = @_; if ($tam <= 128) { ins(inplace($c), $a x $b); return; } my $sess_dir = $mce->sess_dir; my (@p, $p1, $p2, $p3, $p4, $p5, $p6, $p7); my $nTam = $tam / 2; my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); my $t1 = zeroes $nTam,$nTam; my $u1 = zeroes $nTam/2,$nTam/2; my $u2 = zeroes $nTam/2,$nTam/2; sum_m($a21, $a22, $t1, $nTam); submit($t1, $b11, 20, $nTam, $mce, $u1, $u2); subtract_m($b12, $b22, $t1, $nTam); submit($a11, $t1, 30, $nTam, $mce, $u1, $u2); subtract_m($b21, $b11, $t1, $nTam); submit($a22, $t1, 40, $nTam, $mce, $u1, $u2); sum_m($a11, $a12, $t1, $nTam); submit($t1, $b22, 50, $nTam, $mce, $u1, $u2); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $a12, $nTam); ## Reuse $a12 submit($t1, $a12, 70, $nTam, $mce, $u1, $u2); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $a12, $nTam); ## Reuse $a12 submit($t1, $a12, 60, $nTam, $mce, $u1, $u2); sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $a12, $nTam); ## Reuse $a12 submit($t1, $a12, 10, $nTam, $mce, $u1, $u2); $mce->run(0); for my $i (1 .. 7) { $p[$i] = zeroes $nTam,$nTam; $p1 = mapfraw("$sess_dir/p$i"."1"); $p2 = mapfraw("$sess_dir/p$i"."2"); $p3 = mapfraw("$sess_dir/p$i"."3"); $p4 = mapfraw("$sess_dir/p$i"."4"); $p5 = mapfraw("$sess_dir/p$i"."5"); $p6 = mapfraw("$sess_dir/p$i"."6"); $p7 = mapfraw("$sess_dir/p$i"."7"); calc_m($p1, $p2, $p3, $p4, $p5, $p6, $p7, $p[$i], $nTam/2, $u1,$u2); undef $p1; undef $p2; undef $p3; undef $p4; undef $p5; undef $p6; undef $p7; for my $j (1 .. 7) { unlink "$sess_dir/p$i$j"; unlink "$sess_dir/p$i$j.hdr"; } } calc_m($p[1],$p[2],$p[3],$p[4],$p[5],$p[6],$p[7], $c, $nTam, $t1, $a12); return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen_r { my ($a, $b, $c, $tam) = @_; ## Perform the classic multiplication when matrix is <= 128 X 128 if ($tam <= 128) { ins(inplace($c), $a x $b); return; } ## Otherwise, perform multiplication using Strassen's algorithm my $nTam = $tam / 2; my $p2 = zeroes $nTam,$nTam; my $p3 = zeroes $nTam,$nTam; my $p4 = zeroes $nTam,$nTam; my $p5 = zeroes $nTam,$nTam; ## Divide the matrices into 4 sub-matrices my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); ## Calculate p1 to p7 my $t1 = zeroes $nTam,$nTam; sum_m($a21, $a22, $t1, $nTam); strassen_r($t1, $b11, $p2, $nTam); subtract_m($b12, $b22, $t1, $nTam); strassen_r($a11, $t1, $p3, $nTam); subtract_m($b21, $b11, $t1, $nTam); strassen_r($a22, $t1, $p4, $nTam); sum_m($a11, $a12, $t1, $nTam); strassen_r($t1, $b22, $p5, $nTam); subtract_m($p4, $p5, $t1, $nTam); ## c11 ins(inplace($c), $t1, 0, 0); sum_m($p3, $p5, $t1, $nTam); ## c12 ins(inplace($c), $t1, $nTam, 0); sum_m($p2, $p4, $t1, $nTam); ## c21 ins(inplace($c), $t1, 0, $nTam); subtract_m($p3, $p2, $t1, $nTam); ## c22 ins(inplace($c), $t1, $nTam, $nTam); my $t2 = zeroes $nTam,$nTam; sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); strassen_r($t1, $t2, $p2, $nTam); ## Reuse $p2 to store p1 subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); strassen_r($t1, $t2, $p3, $nTam); ## Reuse $p3 to store p6 subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); strassen_r($t1, $t2, $p4, $nTam); ## Reuse $p4 to store p7 my $n1 = $nTam - 1; my $n2 = $nTam + $n1; sum_m($p2, $p4, $t1, $nTam); ## c11 use PDL::NiceSlice; $c(0:$n1,0:$n1) += $t1; no PDL::NiceSlice; sum_m($p2, $p3, $t1, $nTam); ## c22 use PDL::NiceSlice; $c($nTam:$n2,$nTam:$n2) += $t1; no PDL::NiceSlice; return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub divide_m { my ($m, $tam) = @_; my $n1 = $tam - 1; my $n2 = $tam + $n1; return ( $m->slice("0:$n1,0:$n1"), ## m11 $m->slice("$tam:$n2,0:$n1"), ## m12 $m->slice("0:$n1,$tam:$n2"), ## m21 $m->slice("$tam:$n2,$tam:$n2") ## m22 ); } sub calc_m { my ($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $tam, $t1, $t2) = @_; sum_m($p1, $p4, $t1, $tam); sum_m($t1, $p7, $t2, $tam); subtract_m($t2, $p5, $p7, $tam); ## reuse $p7 to store c11 sum_m($p1, $p3, $t1, $tam); sum_m($t1, $p6, $t2, $tam); subtract_m($t2, $p2, $p6, $tam); ## reuse $p6 to store c22 sum_m($p3, $p5, $p1, $tam); ## reuse $p1 to store c12 sum_m($p2, $p4, $p3, $tam); ## reuse $p3 to store c21 ins(inplace($c), $p7, 0, 0); ## c11 = $p7 ins(inplace($c), $p1, $tam, 0); ## c12 = $p1 ins(inplace($c), $p3, 0, $tam); ## c21 = $p3 ins(inplace($c), $p6, $tam, $tam); ## c22 = $p6 return; } sub sum_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a + $b); return; } sub subtract_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a - $b); return; } MCE-1.608/examples/matmult/matmult_perl.pl0000644000076400007640000000666312511671246017527 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl matmult_perl.pl 1024 [ n_workers ] ## Default matrix size 512 ## ## Default n_workers 8 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Storable qw(freeze thaw); use Time::HiRes qw(time); use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; my $n_workers = @ARGV ? shift : 8; if ($tam !~ /^\d+$/ || $tam < 2) { die "error: $tam must be an integer greater than 1.\n"; } my $mce = configure_and_spawn_mce($n_workers); my $cols = $tam; my $rows = $tam; my $a = [ ]; my $b = [ ]; my $c = [ ]; my $cnt; $cnt = 0; for (0 .. $rows - 1) { $a->[$_] = freeze([ $cnt .. $cnt + $cols - 1 ]); $cnt += $cols; } ## Transpose $b to a cache file open my $fh, '>', "$tmp_dir/cache.b"; for my $col (0 .. $rows - 1) { my $d = [ ]; $cnt = $col; for my $row (0 .. $cols - 1) { $d->[$row] = $cnt; $cnt += $rows; } my $d_serialized = freeze($d); print $fh length($d_serialized), "\n", $d_serialized; } close $fh; my $start = time; $mce->run(0, { sequence => { begin => 0, end => $rows - 1, step => 1 }, user_args => { cols => $cols, rows => $rows, path_b => "$tmp_dir/cache.b" } } ); my $end = time; $mce->shutdown; ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$rows-1, $rows-1]) { my ($col, $row) = @$pair; $col %= $rows; $row %= $rows; printf "## (%d, %d): %s\n", $col, $row, $c->[$row][$col]; } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub get_row_a { my ($i) = @_; return $a->[ $i ]; } sub insert_row { my ($i, $result) = @_; $c->[ $i ] = $result; return; } sub configure_and_spawn_mce { my $n_workers = shift || 8; return MCE->new( max_workers => $n_workers, user_begin => sub { my ($self) = @_; my $buffer; open my $fh, '<', $self->{user_args}->{path_b}; $self->{cache_b} = [ ]; for my $j (0 .. $self->{user_args}->{rows} - 1) { read $fh, $buffer, <$fh>; $self->{cache_b}->[$j] = thaw $buffer; } close $fh; }, user_func => sub { my ($self, $i, $chunk_id) = @_; my $a_i = thaw(scalar $self->do('get_row_a', $i)); my $cache_b = $self->{cache_b}; my $result_i = [ ]; my $rows = $self->{user_args}->{rows}; my $cols = $self->{user_args}->{cols}; for my $j (0 .. $rows - 1) { my $c_j = $cache_b->[$j]; $result_i->[$j] = 0; for my $k (0 .. $cols - 1) { $result_i->[$j] += $a_i->[$k] * $c_j->[$k]; } } $self->do('insert_row', $i, $result_i); return; } )->spawn; } MCE-1.608/examples/matmult/strassen_07_t.pl0000644000076400007640000002054212511671246017505 0ustar mariomario#!/usr/bin/env perl ## ## Usage: ## perl strassen_07_t.pl 1024 ## Default matrix size 512 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes qw(time); use PDL; use PDL::Parallel::threads qw(retrieve_pdls free_pdls); use MCE::Signal qw($tmp_dir -use_dev_shm); use MCE; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### my $tam = @ARGV ? shift : 512; ## Wants power of 2 only unless (is_power_of_two($tam)) { die "error: $tam must be a power of 2 integer.\n"; } my $mce; $mce = configure_and_spawn_mce() if ($tam > 128); my $a = sequence $tam,$tam; my $b = sequence $tam,$tam; my $c = zeroes $tam,$tam; my $start = time; strassen($a, $b, $c, $tam, $mce); my $end = time; $mce->shutdown if (defined $mce); ## Print results -- use same pairs to match David Mertens' output. printf "\n## $prog_name $tam: compute time: %0.03f secs\n\n", $end - $start; for my $pair ([0, 0], [324, 5], [42, 172], [$tam-1, $tam-1]) { my ($col, $row) = @$pair; $col %= $tam; $row %= $tam; printf "## (%d, %d): %s\n", $col, $row, $c->at($col, $row); } print "\n"; ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub configure_and_spawn_mce { return MCE->new( max_workers => 7, user_func => sub { my $self = $_[0]; my $data = $self->{user_data}; my $tam = $data->[3]; my $result = zeroes $tam,$tam; my ($a, $b) = retrieve_pdls($data->[0], $data->[1]); strassen_r($a, $b, $result, $tam); free_pdls($a, $b); $result->share_as($self->sess_dir . "/p" . $data->[2]); } )->spawn; } sub is_power_of_two { my ($n) = @_; return 0 if ($n !~ /^\d+$/); return ($n != 0 && (($n & $n - 1) == 0)); } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen { my ($a, $b, $c, $tam, $mce) = @_; if ($tam <= 128) { ins(inplace($c), $a x $b); return; } my $sess_dir = $mce->sess_dir; my ($p1, $p2, $p3, $p4, $p5, $p6, $p7); my $nTam = $tam / 2; my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); my $t1 = zeroes $nTam,$nTam; sum_m($a21, $a22, $t1, $nTam); $t1->copy->share_as("$sess_dir/2a"); $b11->copy->share_as("$sess_dir/2b"); $mce->send([ "$sess_dir/2a", "$sess_dir/2b", 2, $nTam ]); subtract_m($b12, $b22, $t1, $nTam); $a11->copy->share_as("$sess_dir/3a"); $t1->copy->share_as("$sess_dir/3b"); $mce->send([ "$sess_dir/3a", "$sess_dir/3b", 3, $nTam ]); subtract_m($b21, $b11, $t1, $nTam); $a22->copy->share_as("$sess_dir/4a"); $t1->copy->share_as("$sess_dir/4b"); $mce->send([ "$sess_dir/4a", "$sess_dir/4b", 4, $nTam ]); sum_m($a11, $a12, $t1, $nTam); $t1->copy->share_as("$sess_dir/5a"); $b22->copy->share_as("$sess_dir/5b"); $mce->send([ "$sess_dir/5a", "$sess_dir/5b", 5, $nTam ]); subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $a12, $nTam); ## Reuse $a12 $t1->copy->share_as("$sess_dir/7a"); $a12->copy->share_as("$sess_dir/7b"); $mce->send([ "$sess_dir/7a", "$sess_dir/7b", 7, $nTam ]); subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $a12, $nTam); ## Reuse $a12 $t1->copy->share_as("$sess_dir/6a"); $a12->copy->share_as("$sess_dir/6b"); $mce->send([ "$sess_dir/6a", "$sess_dir/6b", 6, $nTam ]); sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $a12, $nTam); ## Reuse $a12 $t1->share_as("$sess_dir/1a"); ## Share $t1 (no need to copy) $a12->copy->share_as("$sess_dir/1b"); $mce->send([ "$sess_dir/1a", "$sess_dir/1b", 1, $nTam ]); $mce->run(0); ($p1, $p2, $p3, $p4, $p5, $p6, $p7) = retrieve_pdls( "$sess_dir/p1", "$sess_dir/p2", "$sess_dir/p3", "$sess_dir/p4", "$sess_dir/p5", "$sess_dir/p6", "$sess_dir/p7" ); calc_m($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $nTam, $t1, $a12); free_pdls($p1, $p2, $p3, $p4, $p5, $p6, $p7); return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub strassen_r { my ($a, $b, $c, $tam) = @_; ## Perform the classic multiplication when matrix is <= 128 X 128 if ($tam <= 128) { ins(inplace($c), $a x $b); return; } ## Otherwise, perform multiplication using Strassen's algorithm my $nTam = $tam / 2; my $p2 = zeroes $nTam,$nTam; my $p3 = zeroes $nTam,$nTam; my $p4 = zeroes $nTam,$nTam; my $p5 = zeroes $nTam,$nTam; ## Divide the matrices into 4 sub-matrices my ($a11, $a12, $a21, $a22) = divide_m($a, $nTam); my ($b11, $b12, $b21, $b22) = divide_m($b, $nTam); ## Calculate p1 to p7 my $t1 = zeroes $nTam,$nTam; sum_m($a21, $a22, $t1, $nTam); strassen_r($t1, $b11, $p2, $nTam); subtract_m($b12, $b22, $t1, $nTam); strassen_r($a11, $t1, $p3, $nTam); subtract_m($b21, $b11, $t1, $nTam); strassen_r($a22, $t1, $p4, $nTam); sum_m($a11, $a12, $t1, $nTam); strassen_r($t1, $b22, $p5, $nTam); subtract_m($p4, $p5, $t1, $nTam); ## c11 ins(inplace($c), $t1, 0, 0); sum_m($p3, $p5, $t1, $nTam); ## c12 ins(inplace($c), $t1, $nTam, 0); sum_m($p2, $p4, $t1, $nTam); ## c21 ins(inplace($c), $t1, 0, $nTam); subtract_m($p3, $p2, $t1, $nTam); ## c22 ins(inplace($c), $t1, $nTam, $nTam); my $t2 = zeroes $nTam,$nTam; sum_m($a11, $a22, $t1, $nTam); sum_m($b11, $b22, $t2, $nTam); strassen_r($t1, $t2, $p2, $nTam); ## Reuse $p2 to store p1 subtract_m($a21, $a11, $t1, $nTam); sum_m($b11, $b12, $t2, $nTam); strassen_r($t1, $t2, $p3, $nTam); ## Reuse $p3 to store p6 subtract_m($a12, $a22, $t1, $nTam); sum_m($b21, $b22, $t2, $nTam); strassen_r($t1, $t2, $p4, $nTam); ## Reuse $p4 to store p7 my $n1 = $nTam - 1; my $n2 = $nTam + $n1; sum_m($p2, $p4, $t1, $nTam); ## c11 use PDL::NiceSlice; $c(0:$n1,0:$n1) += $t1; no PDL::NiceSlice; sum_m($p2, $p3, $t1, $nTam); ## c22 use PDL::NiceSlice; $c($nTam:$n2,$nTam:$n2) += $t1; no PDL::NiceSlice; return; } ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### sub divide_m { my ($m, $tam) = @_; my $n1 = $tam - 1; my $n2 = $tam + $n1; return ( $m->slice("0:$n1,0:$n1"), ## m11 $m->slice("$tam:$n2,0:$n1"), ## m12 $m->slice("0:$n1,$tam:$n2"), ## m21 $m->slice("$tam:$n2,$tam:$n2") ## m22 ); } sub calc_m { my ($p1, $p2, $p3, $p4, $p5, $p6, $p7, $c, $tam, $t1, $t2) = @_; sum_m($p1, $p4, $t1, $tam); sum_m($t1, $p7, $t2, $tam); subtract_m($t2, $p5, $p7, $tam); ## reuse $p7 to store c11 sum_m($p1, $p3, $t1, $tam); sum_m($t1, $p6, $t2, $tam); subtract_m($t2, $p2, $p6, $tam); ## reuse $p6 to store c22 sum_m($p3, $p5, $p1, $tam); ## reuse $p1 to store c12 sum_m($p2, $p4, $p3, $tam); ## reuse $p3 to store c21 ins(inplace($c), $p7, 0, 0); ## c11 = $p7 ins(inplace($c), $p1, $tam, 0); ## c12 = $p1 ins(inplace($c), $p3, 0, $tam); ## c21 = $p3 ins(inplace($c), $p6, $tam, $tam); ## c22 = $p6 return; } sub sum_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a + $b); return; } sub subtract_m { my ($a, $b, $r, $tam) = @_; ins(inplace($r), $a - $b); return; } MCE-1.608/examples/matmult/README0000644000076400007640000004045612511671246015343 0ustar mariomario ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### :: These examples demonstrate the various ways in parallelizing matrix multiplication with MCE. The examples make use of PDL::IO::FastRaw and PDL::Parallel::threads. PDL::Parallel::threads was created by David Mertens. PDL::IO::FastRaw comes pre-installed with PDL. One can diff the examples to see the comparison between using PDL::IO::FastRaw and PDL::Parallel::threads. diff matmult_mce_f.pl matmult_mce_t.pl diff strassen_07_f.pl strassen_07_t.pl diff strassen_49_f.pl strassen_49_t.pl :: All times below are reported in number of seconds. Examples will attempt to use /dev/shm under Linux for the raw MMAP files. Try the thread variant if /dev/shm is not writable for you. Not recommended is to run the *_[df].pl examples on an Oracle server making use of /dev/shm. Check the available size of /dev/shm before running. Benchmark results were captured from a 24-way and a 32-way server. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### -- Usage ------------------------------------------------------------------- :: perl matmult.pl 1024 [ n_workers ] ## Default matrix size 512 ## Default n_workers 8 matmult_base.pl PDL $c = $a x $b (1 worker) matmult_mce_d.pl Uses MCE's do method to fetch (a) and store result (c) Uses PDL::IO::FastRaw to read (b) matmult_mce_f.pl MCE + PDL::IO::FastRaw matmult_mce_t.pl MCE + PDL::Parallel::threads matmult_perl.pl MCE + classic implementation in pure Perl matmult_simd.pl Parallelization via PDL::Parallel::threads::SIMD Script was taken from https://gist.github.com/run4flat/4942132 for folks wanting to review, study, and compare with MCE. The script was modified to support the optional N_threads argument. Both the script and SIMD module were created by David Mertens. :: perl strassen.pl 1024 ## Default matrix size 512 strassen_07_f.pl MCE divide-and-conquer 1 level PDL::IO::FastRaw, 7 workers strassen_07_t.pl MCE divide-and-conquer 1 level PDL::Parallel::threads, 7 workers strassen_49_f.pl MCE divide-and-conquer 2 levels submission using 1 MCE PDL::IO::FastRaw, 49 workers strassen_49_t.pl MCE divide-and-conquer 2 levels submission using 1 MCE PDL::Parallel::threads, 49 workers strassen_perl.pl MCE divide-and-conquer implementation in pure Perl 7 workers :: All examples ending in *_[df].pl spawn children via fork. Matmult_simd.pl and *_t.pl examples utilize threads otherwise. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### :: The matmult_simd.pl example divides work equally among all workers. It's possible that some workers will end up taking longer due to other jobs running on the system when dividing the work equally. This can have the effect of a longer ramp-down time from when the first and last workers complete processing. MCE, on the other hand, can break up work into smaller chunks. The chunk size is determined by the matrix size. The step_size, for the sequence option, is one way of enabling chunking in MCE. my $step_size = ($tam > 2048) ? 24 : ($tam > 1024) ? 16 : 8; Chunking combined with the bank-teller queuing model helps reduce the ramp-down time at the end of the job. Please note that matmult_mce_[df].pl spawns children whereas the other two spawns threads. matmult_mce_d 5120: 65.556s compute: 24 workers: 66.621s script matmult_mce_f 5120: 60.971s compute: 24 workers: 62.558s script matmult_mce_t 5120: 64.673s compute: 24 workers: 65.909s script matmult_simd 5120: 67.210s compute: 24 workers: 67.732s script Matmult_mce_d fetches the next chunk data and submits the result via the "do" method in MCE. Only the "b" matrix is read via PDL::IO::FastRaw among workers. I was not expecting this example to keep up actually. The bank-teller queuing model is now applied for the sequence option beginning with MCE 1.406. $mce->run(0, { sequence => [ 0, $rows - 1, $step_size ] } ); :: The time measured for MCE is the actual compute time and does not include the time to spawn, whereas matmult_simd does. Please keep this in mind when comparing results. The script execution time is measured as well. e.g. time perl matmult_mce_f.pl 5120 24 MCE has a one time cost for creating a pool of workers which can be recycled without having to spawn again. One could instantiate a MCE instance inside a perldl session and reuse the same instance multiple times. Hence, the one time cost diminishes with multiple use. :: The strassen examples apply the Strassen divide-and-conquer algorithm with modifications to recycle piddles and slices as much as possible to maximize memory utilization. http://en.wikipedia.org/wiki/Strassen_algorithm Two implementations are provided, each with PDL::IO::FastRaw and PDL::Parallel::threads (for sharing data). One level submission, 1 MCE instance, 7 workers Two level submission (all-at-once), 1 MCE instance, 49 workers Amazingly, the 49 workers implementation utilizing PDL::IO::FastRaw consumes slightly less memory than PDL::Parallel::threads. ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### :: This system contains 2x Intel E5649 processors with 32GB 1066 MHz RAM. The OS is RHEL 6.3, Perl 5.10.1, perl-PDL-2.4.7-1 installed via yum. -- Results for 1024x1024 on a 24-way, 32GB box ------------------------------ matmult_base: 2.686s compute: 1 worker: 2.894s script running time matmult_mce_d: 0.545s compute: 24 workers: 0.852s script matmult_mce_f: 0.479s compute: 24 workers: 0.824s script matmult_mce_t: 0.510s compute: 24 workers: 1.473s script (interesting) matmult_simd: 0.780s compute: 24 workers: 1.065s script strassen_07_f: 0.385s compute: 7 workers: 0.665s script strassen_07_t: 0.397s compute: 7 workers: 0.992s script strassen_49_f: 0.326s compute: 49 workers: 0.617s script (very nice) strassen_49_t: 0.353s compute: 49 workers: 1.969s script matmult_perl: 23.471s compute: 24 workers: 24.175s script strassen_perl: 44.685s compute: 7 workers: 45.119s script Output (0,0) 365967179776 (1023,1023) 563314846859776 -- Results for 2048x2048 on a 24-way, 32GB box ------------------------------ matmult_base: 21.521s compute: 1 worker: 21.783s script: 0.3% memory matmult_mce_d: 4.206s compute: 24 workers: 4.528s script: 3.5% memory matmult_mce_f: 3.483s compute: 24 workers: 4.017s script: 3.1% memory matmult_mce_t: 4.113s compute: 24 workers: 5.191s script: 0.9% memory matmult_simd: 4.617s compute: 24 workers: 4.901s script: 0.8% memory strassen_07_f: 1.951s compute: 7 workers: 2.249s script: 1.5% memory strassen_07_t: 1.934s compute: 7 workers: 2.576s script: 1.4% memory strassen_49_f: 1.486s compute: 49 workers: 1.823s script: 2.5% memory strassen_49_t: 1.494s compute: 49 workers: 3.192s script: 2.7% memory matmult_perl: 185.343s compute: 24 workers: 187.698s script: 9.7% memory strassen_perl: 319.708s compute: 7 workers: 320.969s script: 8.6% memory Output (0,0) 5859767746560 (2047,2047) 1.80202496872953e+16 matmul examples (0,0) 5859767746560 (2047,2047) 1.8020249687295e+16 strassen examples -- Results for 4096x4096 on a 24-way, 32GB box ------------------------------ matmult_base: 172.145s compute: 1 worker: 172.145s script: 1.2% memory matmult_mce_d: 34.954s compute: 24 workers: 35.717s script: 12.0% memory matmult_mce_f: 36.457s compute: 24 workers: 37.336s script: 10.8% memory matmult_mce_t: 32.565s compute: 24 workers: 33.723s script: 1.8% memory matmult_simd: 34.161s compute: 24 workers: 34.614s script: 2.0% memory strassen_07_f: 12.701s compute: 7 workers: 13.186s script: 5.5% memory strassen_07_t: 12.964s compute: 7 workers: 13.671s script: 4.8% memory strassen_49_f: 8.867s compute: 49 workers: 9.338s script: 7.4% memory strassen_49_t: 8.918s compute: 49 workers: 10.761s script: 7.9% memory Output (0,0) 93790635294720 (4095,4095) 5.76554474219245e+17 matmul examples (0,0) 93790635294720 (4095,4095) 5.76554474219244e+17 strassen example -- Results for 5120x5120 on a 24-way, 32GB box ------------------------------ matmult_base: 336.464s compute: 1 worker: 336.867s script: 1.9% memory matmult_mce_d: 65.556s compute: 24 workers: 66.621s script: 18.2% memory matmult_mce_f: 60.971s compute: 24 workers: 62.558s script: 16.1% memory matmult_mce_t: 64.673s compute: 24 workers: 65.909s script: 2.5% memory matmult_simd: 67.210s compute: 24 workers: 67.732s script: 2.9% memory Output (0,0) 228997817958400 (5119,5119) 1.75944746804184e+18 Observation The difference with memory utilization between matmult_mce_t and matmult_simd is largely due to MCE working on smaller chunks (24 rows). MCE would also utilize 2.9% had work been divided equally among the number of workers (214 rows -- one chunk). -- Results for 8192x8192 on a 24-way, 32GB box ------------------------------ matmult_base: 1388.241s compute: 1 worker: 1388.888s script: 4.8% memory strassen_07_f: 83.562s compute: 7 workers: 84.397s script: 21.3% memory strassen_07_t: 85.366s compute: 7 workers: 86.559s script: 18.8% memory strassen_49_f: 57.019s compute: 49 workers: 57.893s script: 29.5% memory strassen_49_t: 64.476s compute: 49 workers: 66.776s script: 29.9% memory Output (0,0) 1.50092500906803e+15 (8191,8191) 1.84482444489628e+19 Observation Strassen_49_f consumes less memory than strassen_49_t including having faster processing time. This is my favorite of the bunch. The same can be seen on the 32-way box. Compare compute time with script time. There's a larger gap when using threads (*_t) versus forking (*_f). ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### :: This system contains 2x Intel E5-2660 processors with 128GB 1600 MHz RAM. The OS is CentOS 6.3, Perl 5.10.1, perl-PDL-2.4.7-1 installed via yum. :: Performance mode was enabled prior to testing. This has little effect on larger matrix sizes though, but is helpful for 1024x1024 results. The following was run as root to disable CPU frequency scaling. # /etc/init.d/cpuspeed stop Disabling ondemand cpu frequency scaling: [ OK ] -- Results for 1024x1024 on a 32-way, 128GB box ----------------------------- matmult_base: 2.230s compute: 1 worker: 2.361s script running time matmult_mce_d: 0.196s compute: 32 workers: 0.386s script matmult_mce_f: 0.179s compute: 32 workers: 0.398s script matmult_mce_t: 0.182s compute: 32 workers: 0.964s script (interesting) matmult_simd: 0.536s compute: 32 workers: 0.696s script strassen_07_f: 0.232s compute: 7 workers: 0.402s script strassen_07_t: 0.221s compute: 7 workers: 0.544s script strassen_49_f: 0.199s compute: 49 workers: 0.386s script (very nice) strassen_49_t: 0.206s compute: 49 workers: 1.314s script matmult_perl: 15.105s compute: 32 workers: 15.634s script strassen_perl: 38.764s compute: 7 workers: 39.063s script Output (0,0) 365967179776 (1023,1023) 563314846859776 -- Results for 5120x5120 on a 32-way, 128GB box ----------------------------- matmult_base: 282.508s compute: 1 worker: 282.776s script: 0.5% memory matmult_mce_d: 21.706s compute: 32 workers: 22.744s script: 5.9% memory matmult_mce_f: 20.665s compute: 32 workers: 22.235s script: 5.4% memory matmult_mce_t: 21.212s compute: 32 workers: 22.163s script: 0.7% memory matmult_simd: 22.056s compute: 32 workers: 22.407s script: 0.8% memory Output (0,0) 228997817958400 (5119,5119) 1.75944746804184e+18 -- Results for 6144x6144 on a 32-way, 128GB box ----------------------------- matmult_base: 486.967s compute: 1 worker: 487.299s script: 0.7% memory matmult_mce_d: 36.383s compute: 32 workers: 37.065s script: 8.3% memory matmult_mce_f: 35.898s compute: 32 workers: 38.072s script: 7.8% memory matmult_mce_t: 35.850s compute: 32 workers: 36.893s script: 0.9% memory matmult_simd: 37.121s compute: 32 workers: 37.561s script: 1.0% memory Output (0,0) 474873065373696 (6143,6143) 4.37797347894126e+18 -- Results for 7168x7168 on a 32-way, 128GB box ----------------------------- matmult_base: 779.615s compute: 1 worker: 780.024s script: 0.9% memory matmult_mce_d: 57.781s compute: 32 workers: 59.628s script: 11.2% memory matmult_mce_f: 56.083s compute: 32 workers: 58.997s script: 10.2% memory matmult_mce_t: 54.158s compute: 32 workers: 55.318s script: 1.1% memory matmult_simd: 59.207s compute: 32 workers: 59.755s script: 1.4% memory Output (0,0) 879791667937280 (7167,7167) 9.46237929052649e+18 Observation The same pattern is seen when comparing memory utilization between matmult_mce_t and matmult_simd. The difference is 384MB for a matrix size of 7168. MCE chunks away at 24 rows per each chunk, not 224 rows. -- Results for 8192x8192 on a 32-way, 128GB box ----------------------------- matmult_base: 1161.419s compute: 1 worker: 1161.883s script: 1.2% memory matmult_mce_d: 316.989s compute: 32 workers: 319.482s script: 14.5% memory matmult_mce_f: 317.054s compute: 32 workers: 320.737s script: 13.1% memory matmult_mce_t: 83.002s compute: 32 workers: 84.255s script: 1.4% memory matmult_simd: 87.355s compute: 32 workers: 88.019s script: 1.7% memory strassen_07_f: 59.998s compute: 7 workers: 60.670s script: 5.2% memory strassen_07_t: 62.259s compute: 7 workers: 63.097s script: 4.7% memory strassen_49_f: 29.972s compute: 49 workers: 30.663s script: 7.3% memory strassen_49_t: 36.392s compute: 49 workers: 38.075s script: 7.4% memory Output (0,0) 1.50092500906803e+15 (8191,8191) 1.84482444489628e+19 Observation The strassen results are quite fast and breaking 30 seconds for compute time -- even with strassen_49_f. However, something seems wrong when processing a matrix size of 8192 using PDL::IO::FastRaw. Both matmult_mce_d and matmult_mce_f are seen taking considerable amount of time over matmult_mce_t and matmult_simd. This pattern was not present when processing a matrix size of 7168 earlier. Repeated attempts show the same behavior. Making a copy of the "b" matrix solves the problem, but that requires an additional 16.8 GB of memory to run for 32 workers. $self->{r} = mapfraw("$tmp_dir/b")->copy; Readfraw also solves the problem. Although, that wants a whopping 50.4 GB of additional memory which seems quite high. $self->{r} = readfraw("$tmp_dir/b"); ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### The Strassen algorithm can introduce rounding errors noted above in the output. Most often, it may not be a problem. -- Mario MCE-1.608/examples/sync.pl0000755000076400007640000000156012511671246014305 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## Barrier synchronization example. ## http://en.wikipedia.org/wiki/Barrier_(computer_science) ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use MCE; sub user_func { my ($mce) = @_; my $wid = MCE->wid; MCE->sendto("STDOUT", "a: $wid\n"); ## MCE 1.0+ MCE->sync; MCE->sendto(\*STDOUT, "b: $wid\n"); ## MCE 1.5+ MCE->sync; MCE->print("c: $wid\n"); ## MCE 1.5+ MCE->sync; return; } my $mce = MCE->new( max_workers => 4, user_func => \&user_func )->run; MCE-1.608/examples/forchunk.pl0000755000076400007640000000563312511671246015155 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## This example demonstrates the sqrt example from Parallel::Loops ## (Parallel::Loops v0.07 utilizing Parallel::ForkManager v1.07). ## ## Testing was on a Linux VM; Perl v5.20.1; Haswell i7 at 2.6 GHz. ## The number indicates the size of input displayed in 1 second. ## Output was directed to >/dev/null. ## ## Parallel::Loops: 1,600 Forking each @input is expensive ## MCE->foreach...: 23,000 Workers persist between each @input ## MCE->forseq....: 200,000 Uses sequence of numbers as input ## MCE->forchunk..: 800,000 IPC overhead is greatly reduced ## ## usage: forchunk.pl [ size ] ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use Time::HiRes qw(time); use MCE; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; my $size = shift || 3000; unless ($size =~ /\A\d+\z/) { print {*STDERR} "usage: $prog_name [ size ]\n"; exit; } my @input_data = (0 .. $size - 1); ############################################################################### ## ---------------------------------------------------------------------------- ## Parallelize via MCE's foreach method. ## ############################################################################### ## Make an output iterator for gather. Output order is preserved. my $chunk_size = 2500; sub preserve_order { my %tmp; my $order_id = 1; return sub { my ($chunk_id, $data_ref) = @_; $tmp{$chunk_id} = $data_ref; while (1) { last unless exists $tmp{$order_id}; my $i = ($order_id - 1) * $chunk_size; for ( @{ $tmp{$order_id} } ) { printf "n: %d sqrt(n): %f\n", $input_data[$i++], $_; } delete $tmp{$order_id++}; } return; }; } ## Configure MCE. ## use MCE::Flow; ## Same thing in MCE 1.5+ ## ## mce_flow { ## max_workers => 'auto', chunk_size => $chunk_size, ## gather => preserve_order ## }, ## sub { ## my ($mce, $chunk_ref, $chunk_id) = @_; ## my @result; ## ## for ( @{ $chunk_ref } ) { ## push @result, sqrt($_); ## } ## ## MCE->gather($chunk_id, \@result); ## ## }, @input_data; my $mce = MCE->new( max_workers => 'auto', chunk_size => $chunk_size, gather => preserve_order ); ## Below, $chunk_ref is a reference to an array containing the next ## $chunk_size items from @input_data. my $start = time; $mce->forchunk( \@input_data, sub { my ($mce, $chunk_ref, $chunk_id) = @_; my @result; for ( @{ $chunk_ref } ) { push @result, sqrt($_); } MCE->gather($chunk_id, \@result); }); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/relay.pl0000755000076400007640000000435012511671246014445 0ustar mariomario#!/usr/bin/env perl ## The relay method is for receiving and passing on information. Relay is ## enabled by specifing the init_relay option which takes a hash or array ## reference, or a scalar value. Relaying is orderly and driven by chunk_id ## when processing data, otherwise task_wid. Omitting the code block ## (e.g. MCE::relay) relays forward. ## ## Relaying is not met for passing big data. The last worker will likely ## stall if exceeding the buffer size for the socket. Not exceeding 8 KiB ## is safe across all platforms. ## ## Also see examples findnull.pl, cat.pl, or biofasta/fasta_aidx.pl. use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use MCE::Flow max_workers => 4; print "\n"; ############################################################################### mce_flow { init_relay => { p => 0, e => 0 }, ## Relaying multiple values (HASH) }, sub { my $wid = MCE->wid; ## do work my $pass = $wid % 3; my $errs = $wid % 2; ## relay my %last_rpt = MCE::relay { $_->{p} += $pass; $_->{e} += $errs }; MCE->print("$wid: passed $pass, errors $errs\n"); return; }; my %results = MCE->relay_final; print " passed $results{p}, errors $results{e} final\n\n"; ############################################################################### mce_flow { init_relay => [ 0, 0 ], ## Relaying multiple values (ARRAY) }, sub { my $wid = MCE->wid; ## do work my $pass = $wid % 3; my $errs = $wid % 2; ## relay my @last_rpt = MCE::relay { $_->[0] += $pass; $_->[1] += $errs }; MCE->print("$wid: passed $pass, errors $errs\n"); return; }; my ($pass, $errs) = MCE->relay_final; print " passed $pass, errors $errs final\n\n"; ############################################################################### mce_flow { init_relay => 0, ## Relaying a single value }, sub { my $wid = MCE->wid; ## do work my $bytes_read = 1000 + ((MCE->wid % 3) * 3); ## relay my $last_offset = MCE::relay { $_ += $bytes_read }; ## output MCE->print("$wid: $bytes_read\n"); return; }; my $total = MCE->relay_final; print " $total size\n\n"; MCE-1.608/examples/foreach.pl0000755000076400007640000000506412511671246014743 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## This example demonstrates the sqrt example from Parallel::Loops ## (Parallel::Loops v0.07 utilizing Parallel::ForkManager v1.07). ## ## Testing was on a Linux VM; Perl v5.20.1; Haswell i7 at 2.6 GHz. ## The number indicates the size of input displayed in 1 second. ## Output was directed to >/dev/null. ## ## Parallel::Loops: 1,600 Forking each @input is expensive ## MCE->foreach...: 23,000 Workers persist between each @input ## MCE->forseq....: 200,000 Uses sequence of numbers as input ## MCE->forchunk..: 800,000 IPC overhead is greatly reduced ## ## usage: foreach.pl [ size ] ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use Time::HiRes qw(time); use MCE; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; my $size = shift || 3000; unless ($size =~ /\A\d+\z/) { print {*STDERR} "usage: $prog_name [ size ]\n"; exit; } my @input_data = (0 .. $size - 1); ############################################################################### ## ---------------------------------------------------------------------------- ## Parallelize via MCE's foreach method. ## ############################################################################### ## Make an output iterator for gather. Output order is preserved. sub preserve_order { my %tmp; my $order_id = 1; return sub { my ($chunk_id, $data) = @_; $tmp{$chunk_id} = $data; while (1) { last unless exists $tmp{$order_id}; printf "n: %d sqrt(n): %f\n", $input_data[$order_id - 1], delete $tmp{$order_id++}; } return; }; } ## Configure MCE. ## use MCE::Flow; ## Same thing in MCE 1.5+ ## ## mce_flow { ## max_workers => 'auto', chunk_size => 1, gather => preserve_order ## }, ## sub { ## my ($mce, $chunk_ref, $chunk_id) = @_; ## MCE->gather($chunk_id, sqrt($chunk_ref->[0])); ## ## }, @input_data; my $mce = MCE->new( max_workers => 'auto', chunk_size => 1, gather => preserve_order ); ## Use $chunk_ref->[0] or $_ to retrieve the single element. my $start = time; $mce->foreach( \@input_data, sub { my ($mce, $chunk_ref, $chunk_id) = @_; MCE->gather($chunk_id, sqrt($chunk_ref->[0])); }); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/interval.pl0000755000076400007640000000560112511671246015155 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## This example demonstrates the "interval" option in MCE. ## ## usage: interval.pl [ delay ] ## Default is 0.1 ## interval.pl 0.005 ## interval.pl 1.000 ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; use Time::HiRes 'time'; use MCE; my $d = shift || 0.1; local $| = 1; ############################################################################### ## ---------------------------------------------------------------------------- ## User functions for MCE. ## ############################################################################### sub create_task { my ($node_id) = @_; my $seq_size = 6; my $seq_start = ($node_id - 1) * $seq_size + 1; my $seq_end = $seq_start + $seq_size - 1; return { max_workers => 2, sequence => [ $seq_start, $seq_end ], interval => { delay => $d, max_nodes => 4, node_id => $node_id } }; } sub user_begin { my ($mce, $task_id, $task_name) = @_; ## The yield method causes this worker to wait for its next ## time interval slot before running. Yield has no effect ## without the "interval" option. ## Yielding is beneficial inside a user_begin block. A use case ## is staggering database connections among workers in order ## to not impact the DB server. MCE->yield; MCE->printf( "Node %2d: %0.5f -- Worker %2d: %12s -- Started\n", MCE->task_id + 1, time, MCE->task_wid, '' ); return; } { my $prev_time = time; sub user_func { my ($mce, $seq_n, $chunk_id) = @_; ## Yield simply waits for the next time interval. MCE->yield; ## Calculate how long this worker has waited. my $curr_time = time; my $time_waited = $curr_time - $prev_time; $prev_time = $curr_time; MCE->printf( "Node %2d: %0.5f -- Worker %2d: %12.5f -- Seq_N %3d\n", MCE->task_id + 1, time, MCE->task_wid, $time_waited, $seq_n ); return; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Simulate a 4 node environment passing node_id to create_task. ## ############################################################################### print "Node_ID Current_Time Worker_ID Time_Waited Comment\n"; MCE->new( user_begin => \&user_begin, user_func => \&user_func, user_tasks => [ create_task(1), create_task(2), create_task(3), create_task(4) ] )->run; MCE-1.608/examples/flow_model.pl0000755000076400007640000000760012511671246015461 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## This example demonstrates MCE::Flow, MCE::Queue, and MCE->gather. ## Allow 5.5 seconds for the ping timeout to expire before seeing results. ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use Net::Ping; use MCE::Flow; use MCE::Queue; my $Q = MCE::Queue->new; ############################################################################### ## Configure MCE options. Both user_begin and user_end are called by the worker ## process. The manager process calls task_end after the task has completed. ## Note the use of task_name below. MCE::Flow::init { user_begin => sub { my ($mce) = @_; if (MCE->task_name eq 'pinger') { $mce->{pinger} = Net::Ping->new('syn'); $mce->{pinger}->hires; } return; }, user_end => sub { my ($mce) = @_; if (MCE->task_name eq 'pinger') { $mce->{pinger}->close; } return; }, task_end => sub { my ($mce, $task_id, $task_name) = @_; if ($task_name eq 'pinger') { my $n_workers = $mce->{user_tasks}->[$task_id + 1]->{max_workers}; $Q->enqueue((undef) x $n_workers); } return; } }; ############################################################################### ## There are 2 sub-tasks below. Pinger calls gather for failed pings. However, ## task2 calls gather twice. Think of gather as yielding data which may be ## called as often as needed. For this demo, we pass the key/value pair. ## Notice enqueue and dequeue as well. sub pinger { my ($mce, $chunk_ref, $chunk_id) = @_; my $pinger = $mce->{pinger}; my %pass = (); my @fail = (); ## $chunk_ref points to an array containing chunk_size items foreach my $host ( @{ $chunk_ref } ) { $pinger->ping($host, 3.333); } ## Let pinger process entire chunk all at once while ((my $host, my $rtt, my $ip) = $pinger->ack) { $pass{$host} = $pass{$ip} = 1; } ## Process hosts/IPs my @successful; foreach my $host ( @{ $chunk_ref } ) { unless (exists $pass{$host}) { MCE->gather("$host.status", "Failed ping"); } else { push @successful, $host; } } ## Enqueue all at once for successful hosts/IPs $Q->enqueue(@successful) if (@successful); return; } sub task2 { my ($mce) = @_; while (defined (my $host = $Q->dequeue)) { ## Do something with $host ... my %h = (); $h{raw} = "task2 data"; $h{fun} = "other data"; MCE->gather("$host.status", "Successful"); MCE->gather("$host.data", \%h); } return; } ############################################################################### ## Two of the IPs will fail. This can very well be a large array. The pinger ## workers will process an entire chunk all at once. my @h = qw( 127.0.0.1 127.0.0.8 127.0.0.9 ); ## MCE options can be passed here or through MCE::Flow::init above. Both ## max_workers and task_name support an array reference for setting each ## sub-task individually. Gather calls above pass a key/value pair which ## ends up into the hash variable %r below. print "## Please wait. This can take 3.4 seconds.\n"; my %r = mce_flow { chunk_size => 100, max_workers => [ 4, 20 ], task_name => [ 'pinger', 'task2' ] }, \&pinger, \&task2, @h; ## Output ## 127.0.0.1: Successful: task2 data ## 127.0.0.8: Failed ping: ## 127.0.0.9: Failed ping: foreach my $host (@h) { my $status = $r{"$host.status"}; my $rawdata = (exists $r{"$host.data"}) ? $r{"$host.data"}->{raw} : ""; print "$host: $status: $rawdata\n"; } MCE-1.608/examples/flow_demo.pl0000755000076400007640000001055612511671246015311 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use MCE::Flow Sereal => 1; # Use Sereal for serialization if available use MCE::Queue fast => 1; # MCE 1.520 (compare with => 0, also Sereal) # Results from CentOS 7 VM (4 cores): time flow_demo.pl | wc -l # # Sereal 0, fast 0: 4.703s # Serialization via Storable # Sereal 1, fast 0: 3.926s # Serialization via Sereal # Sereal 1, fast 1: 2.092s # Enable fast optimization; crazy :) my $setter_q = MCE::Queue->new; my $pinger_q = MCE::Queue->new; my $writer_q = MCE::Queue->new; # See https://metacpan.org/pod/MCE::Core#SYNTAX-for-INPUT_DATA for a DBI # input iterator (db_iter). Set chunk_size accordingly. Do not go above # 300 for chunk_size if running a SNMP crawling (imho). sub make_number_iter { my ($first, $last) = @_; my $done; my $n = $first; return sub { my $chunk_size = $_[0]; return if $done; my $min = ($n + $chunk_size - 1 > $last) ? $last : $n + $chunk_size - 1; my @numbers = ($n .. $min); $n = $min + 1; $done = 1 if $min == $last; return @numbers; }; } # Begin and End functions. sub _begin { my ($mce, $task_id, $task_name) = @_; if ($task_name eq 'writer') { # $mce->{dbh} = DBI->connect(...); $mce->{dbh} = 'each (writer) obtains db handle once'; } return; } sub _end { my ($mce, $task_id, $task_name) = @_; if ($task_name eq 'writer') { # $mce->{dbh}->disconnect; delete $mce->{dbh}; } return; } # Actual roles. Uncomment MCE->yield below if processing thousands or more. sub poller { my ($mce, $chunk_ref, $chunk_id) = @_; my (@pinger_w, @setter_w, @writer_w); # MCE->yield; # run gracefully, see examples/interval.pl foreach (@$chunk_ref) { if ($_ % 100 == 0) { push @pinger_w, $_; # poller cannot connect, check ping status } else { if ($_ % 33 == 0) { push @setter_w, $_; # device needs settings } else { push @writer_w, $_; # all is well } } } $pinger_q->enqueue( [ \@pinger_w, $chunk_id, 'ping' ] ) if @pinger_w; $setter_q->enqueue( [ \@setter_w, $chunk_id, 'set ' ] ) if @setter_w; $setter_q->enqueue( [ \@writer_w, $chunk_id, 'ok ' ] ) if @writer_w; return; } sub setter { my ($mce) = @_; # MCE->yield; # adjust interval option below; 0.007 while (defined (my $next_ref = $setter_q->dequeue)) { my ($chunk_ref, $chunk_id, $status) = @{ $next_ref }; $writer_q->enqueue( [ $chunk_ref, $chunk_id, $status ] ); } return; } sub pinger { my ($mce) = @_; # MCE->yield; # all workers are assigned interval slot while (defined (my $next_ref = $pinger_q->dequeue)) { my ($chunk_ref, $chunk_id, $status) = @{ $next_ref }; $writer_q->enqueue( [ $chunk_ref, $chunk_id, $status ] ); } return; } sub writer { my ($mce) = @_; my $dbh = $mce->{dbh}; while (defined (my $next_ref = $writer_q->dequeue)) { my ($chunk_ref, $chunk_id, $status) = @{ $next_ref }; MCE->say("$chunk_id $status " . scalar @$chunk_ref); } return; } # Configure MCE options; task_name, max_workers can take and anonymous array. # # Change max_workers to [ 160, 100, 80, 4 ] if processing millions of rows. # Also tune netfilter on Linux: /etc/sysctl.conf # net.netfilter.nf_conntrack_udp_timeout = 10 # net.netfilter.nf_conntrack_udp_timeout_stream = 10 # net.nf_conntrack_max = 131072 my $n_pollers = 100; my $n_setters = 20; my $n_pingers = 10; my $n_writers = 4; MCE::Flow::init { chunk_size => 300, input_data => make_number_iter(1, 2_000_000), interval => 0.007, user_begin => \&_begin, user_end => \&_end, task_name => [ 'poller', 'setter', 'pinger', 'writer' ], max_workers => [ $n_pollers, $n_setters, $n_pingers, $n_writers ], task_end => sub { my ($mce, $task_id, $task_name) = @_; if ($task_name eq 'poller') { $setter_q->enqueue((undef) x $n_setters); } elsif ($task_name eq 'setter') { $pinger_q->enqueue((undef) x $n_pingers); } elsif ($task_name eq 'pinger') { $writer_q->enqueue((undef) x $n_writers); } } }; mce_flow \&poller, \&setter, \&pinger, \&writer; MCE-1.608/examples/sampledb/0000755000076400007640000000000012511673554014562 5ustar mariomarioMCE-1.608/examples/sampledb/query1.pl0000644000076400007640000000270212511671246016342 0ustar mariomario#!/usr/bin/env perl ## Usage: perl query1.pl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my ($prog_name, $prog_dir, $db_file); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $db_file = "$prog_dir/SAMPLE.DB"; } use MCE::Loop max_workers => 3; use Time::HiRes 'time'; use DBI; my $start = time; ############################################################################### sub db_iter { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq ORDER BY seq_id" ); $sth->execute; return sub { if (my @row = $sth->fetchrow_array) { return @row; } return; }; } ############################################################################### my ($dsn, $user, $password) = ("dbi:SQLite:dbname=$db_file", "", ""); mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; my ($seq_id, $value1, $value2) = @{ $chunk_ref }; MCE->printf("%8ld %10ld %14.3lf\n", $seq_id, $value1, $value2); } db_iter($dsn, $user, $password); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/sampledb/query3.pl0000644000076400007640000000365612511671246016355 0ustar mariomario#!/usr/bin/env perl ## Usage: perl query3.pl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my ($prog_name, $prog_dir, $db_file); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $db_file = "$prog_dir/SAMPLE.DB"; } use MCE::Flow max_workers => 3, chunk_size => 300; use Time::HiRes 'time'; use DBI; my $start = time; ############################################################################### sub db_iter { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq ORDER BY seq_id" ); $sth->execute; return sub { my ($chunk_size) = @_; if (my $rows_ref = $sth->fetchall_arrayref(undef, $chunk_size)) { return @{ $rows_ref }; } return; }; } sub preserve_order { my %tmp; my $order_id = 1; return sub { $tmp{ (shift) } = \@_; while (1) { last unless exists $tmp{$order_id}; print @{ delete $tmp{$order_id++} }; } return; } } ############################################################################### my ($dsn, $user, $password) = ("dbi:SQLite:dbname=$db_file", "", ""); mce_flow { input_data => db_iter($dsn, $user, $password), gather => preserve_order }, sub { my ($mce, $chunk_ref, $chunk_id) = @_; my $output = ''; foreach my $row_ref (@{ $chunk_ref }) { my ($seq_id, $value1, $value2) = @{ $row_ref }; $output .= sprintf("%8ld %10ld %14.3lf\n", $seq_id, $value1, $value2); } MCE->gather($chunk_id, $output); }; printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/sampledb/update2.pl0000644000076400007640000000610312511671246016457 0ustar mariomario#!/usr/bin/env perl ## Usage: perl update2.pl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my ($prog_name, $prog_dir, $db_file); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $db_file = "$prog_dir/SAMPLE.DB"; } use MCE::Loop max_workers => 3, chunk_size => 300; use Time::HiRes 'time'; use DBI; my $start = time; ############################################################################### sub db_iter_client_server { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq" ); $sth->execute; return sub { my ($chunk_size) = @_; if (my $rows_ref = $sth->fetchall_arrayref(undef, $chunk_size)) { return @{ $rows_ref }; } return; }; } sub db_iter_auto_offset { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $offset = 0; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq LIMIT ? OFFSET ?" ); return sub { my ($chunk_size) = @_; $sth->execute($chunk_size, $offset); if (my $rows_ref = $sth->fetchall_arrayref(undef, $chunk_size)) { $offset += $chunk_size; return @{ $rows_ref }; } return; }; } ############################################################################### my ($dsn, $user, $password) = ("dbi:SQLite:dbname=$db_file", "", ""); MCE::Loop::init { user_begin => sub { my ($mce, $task_id, $task_name) = @_; ## Store dbh and prepare statements inside the mce hash. $mce->{dbh} = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; $mce->{dbh}->do('PRAGMA synchronous = OFF'); $mce->{dbh}->do('PRAGMA journal_mode = MEMORY'); $mce->{upd} = $mce->{dbh}->prepare( "UPDATE seq SET value1 = ?, value2 = ? WHERE seq_id = ?" ); return; }, user_end => sub { my ($mce, $task_id, $task_name) = @_; $mce->{upd}->finish; $mce->{dbh}->disconnect; return; } }; mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; my ($dbh, $upd) = ($mce->{dbh}, $mce->{upd}); my $output = ''; $dbh->begin_work; foreach my $row_ref (@{ $chunk_ref }) { my ($seq_id, $value1, $value2) = @{ $row_ref }; $output .= "Updating row $seq_id\n"; $upd->execute(int($value1 * 1.333), $value2 * 1.333, $seq_id); } MCE->print($output); $dbh->commit; } db_iter_auto_offset($dsn, $user, $password); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/sampledb/update1.pl0000644000076400007640000000601012511671246016453 0ustar mariomario#!/usr/bin/env perl ## Usage: perl update1.pl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my ($prog_name, $prog_dir, $db_file); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $db_file = "$prog_dir/SAMPLE.DB"; } use MCE::Loop max_workers => 3; use Time::HiRes 'time'; use DBI; my $start = time; ############################################################################### sub db_iter_client_server { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq" ); $sth->execute; return sub { if (my @row = $sth->fetchrow_array) { return @row; } return; }; } sub db_iter_auto_offset { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $offset = 0; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq LIMIT ? OFFSET ?" ); my $rows_ref; my $prefetch_size = 300; my $i = $prefetch_size; return sub { if ($i == $prefetch_size) { $sth->execute($prefetch_size, $offset); $rows_ref = $sth->fetchall_arrayref(undef, $prefetch_size); $offset += $prefetch_size; $i = 0; } if (defined $rows_ref && defined $rows_ref->[$i]) { return @{ $rows_ref->[$i++] }; } return; }; } ############################################################################### my ($dsn, $user, $password) = ("dbi:SQLite:dbname=$db_file", "", ""); MCE::Loop::init { user_begin => sub { my ($mce, $task_id, $task_name) = @_; ## Store dbh and prepare statements inside the mce hash. $mce->{dbh} = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; $mce->{dbh}->do('PRAGMA synchronous = OFF'); $mce->{dbh}->do('PRAGMA journal_mode = MEMORY'); $mce->{upd} = $mce->{dbh}->prepare( "UPDATE seq SET value1 = ?, value2 = ? WHERE seq_id = ?" ); return; }, user_end => sub { my ($mce, $task_id, $task_name) = @_; $mce->{upd}->finish; $mce->{dbh}->disconnect; return; } }; mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; my ($dbh, $upd) = ($mce->{dbh}, $mce->{upd}); my ($seq_id, $value1, $value2) = @{ $chunk_ref }; MCE->say("Updating row $seq_id"); $upd->execute(int($value1 * 1.333), $value2 * 1.333, $seq_id); } db_iter_auto_offset($dsn, $user, $password); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/sampledb/query2.pl0000644000076400007640000000320012511671246016335 0ustar mariomario#!/usr/bin/env perl ## Usage: perl query2.pl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my ($prog_name, $prog_dir, $db_file); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $db_file = "$prog_dir/SAMPLE.DB"; } use MCE::Loop max_workers => 3, chunk_size => 300; use Time::HiRes 'time'; use DBI; my $start = time; ############################################################################### sub db_iter { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq ORDER BY seq_id" ); $sth->execute; return sub { my ($chunk_size) = @_; if (my $rows_ref = $sth->fetchall_arrayref(undef, $chunk_size)) { return @{ $rows_ref }; } return; }; } ############################################################################### my ($dsn, $user, $password) = ("dbi:SQLite:dbname=$db_file", "", ""); mce_loop { my ($mce, $chunk_ref, $chunk_id) = @_; my $output = ''; foreach my $row_ref (@{ $chunk_ref }) { my ($seq_id, $value1, $value2) = @{ $row_ref }; $output .= sprintf("%8ld %10ld %14.3lf\n", $seq_id, $value1, $value2); } MCE->print($output); } db_iter($dsn, $user, $password); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/sampledb/create.pl0000644000076400007640000000357112511671246016364 0ustar mariomario#!/usr/bin/env perl ## Usage: perl create.pl [ n_rows ] use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my ($prog_name, $prog_dir, $db_file); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $db_file = "$prog_dir/SAMPLE.DB"; } use Time::HiRes 'time'; use DBI; my $start = time; ############################################################################### my $n_rows = shift || 3000; my $batch_size = 3000; my $batch_i = $batch_size; unless ($n_rows =~ /\A\d+\z/) { die "usage: perl $prog_name [ n_rows ]\n"; } if (-e $db_file) { unlink $db_file or die "Cannot unlink $db_file: $!\n"; } my ($dsn, $user, $password) = ("dbi:SQLite:dbname=$db_file", "", ""); my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc', }) or die $DBI::errstr; exit($?) unless -e $db_file; $dbh->do('PRAGMA page_size = 4096'); ############################################################################### my ($sql, $sth); $sql = <<'END_SQL'; CREATE TABLE seq ( seq_id INTEGER PRIMARY KEY, value1 INTEGER, value2 REAL ) END_SQL $dbh->do($sql); $sth = $dbh->prepare( "INSERT INTO seq (seq_id, value1, value2) VALUES (?, ?, ?)" ); $dbh->begin_work; ## $dbh->do('BEGIN'); for my $seq_id (1 .. $n_rows) { $sth->execute($seq_id, $seq_id * 2, $seq_id * 1.618); unless (--$batch_i) { $dbh->commit; ## $dbh->do('COMMIT'); $dbh->begin_work; ## $dbh->do('BEGIN'); $batch_i = $batch_size; } } $dbh->commit; ## $dbh->do('COMMIT'); $sth->finish; $dbh->disconnect; printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/sampledb/update3.pl0000644000076400007640000000613412511671246016464 0ustar mariomario#!/usr/bin/env perl ## Usage: perl update3.pl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; my ($prog_name, $prog_dir, $db_file); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $db_file = "$prog_dir/SAMPLE.DB"; } use MCE::Flow max_workers => 3, chunk_size => 300; use Time::HiRes 'time'; use DBI; my $start = time; ############################################################################### sub db_iter_client_server { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq" ); $sth->execute; return sub { my ($chunk_size) = @_; if (my $rows_ref = $sth->fetchall_arrayref(undef, $chunk_size)) { return @{ $rows_ref }; } return; }; } sub db_iter_auto_offset { my ($dsn, $user, $password) = @_; my $dbh = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; my $offset = 0; my $sth = $dbh->prepare( "SELECT seq_id, value1, value2 FROM seq LIMIT ? OFFSET ?" ); return sub { my ($chunk_size) = @_; $sth->execute($chunk_size, $offset); if (my $rows_ref = $sth->fetchall_arrayref(undef, $chunk_size)) { $offset += $chunk_size; return @{ $rows_ref }; } return; }; } ############################################################################### my ($dsn, $user, $password) = ("dbi:SQLite:dbname=$db_file", "", ""); MCE::Flow::init { user_begin => sub { my ($mce, $task_id, $task_name) = @_; ## Store dbh and prepare statements inside the mce hash. $mce->{dbh} = DBI->connect($dsn, $user, $password, { PrintError => 0, RaiseError => 1, AutoCommit => 1, FetchHashKeyName => 'NAME_lc' }) or die $DBI::errstr; $mce->{dbh}->do('PRAGMA synchronous = OFF'); $mce->{dbh}->do('PRAGMA journal_mode = MEMORY'); $mce->{upd} = $mce->{dbh}->prepare( "UPDATE seq SET value1 = ?, value2 = ? WHERE seq_id = ?" ); return; }, user_end => sub { my ($mce, $task_id, $task_name) = @_; $mce->{upd}->finish; $mce->{dbh}->disconnect; return; } }; mce_flow { input_data => db_iter_auto_offset($dsn, $user, $password) }, sub { my ($mce, $chunk_ref, $chunk_id) = @_; my ($dbh, $upd) = ($mce->{dbh}, $mce->{upd}); my $output = ''; $dbh->begin_work; foreach my $row_ref (@{ $chunk_ref }) { my ($seq_id, $value1, $value2) = @{ $row_ref }; $output .= "Updating row $seq_id\n"; $upd->execute(int($value1 * 1.333), $value2 * 1.333, $seq_id); } MCE->print($output); $dbh->commit; }; printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/sampledb/README0000644000076400007640000000570112511671246015441 0ustar mariomario This dir contains examples demonstrating DBI/DBD::SQLite with MCE. ## Usage #################################################################### 1) perl create.pl [ n_rows ] creates SAMPLE.DB ; default 3k rows perl create.pl 100000 re-create DB ; 100k rows 2) perl query1.pl chunk_size => NA ; MCE::Loop perl query2.pl chunk_size => 300 ; MCE::Loop no output order perl query3.pl chunk_size => 300 ; MCE::Flow w/ output order 3) perl update1.pl chunk_size => NA ; MCE::Loop perl update2.pl chunk_size => 300 ; MCE::Loop perl update3.pl chunk_size => 300 ; MCE::Flow For SQLite, simulatenous updates and select are possible due to calling fetchall for the select process. The update examples have 2 db iterator functions; db_iter_client_server and db_iter_auto_offset. Configure MCE to use db_iter_client_server when running against a client/server DB. The query1/update1 examples are beneficial for apps with long compute time per each row, thus chunking is not desired. Ensure Perl can load DBI and DBD::SQLite. Install any missing module(s). perl -MDBI -MDBD::SQLite -e 1 ArchLinux ; pacman -S perl-dbi perl-dbd-sqlite Cygwin ; install via setup-x86; perl-DBI, perl-DBD-SQLite Debian ; apt-get install libdbi-perl libdbd-sqlite3-perl FreeBSD ; pkg install p5-DBI p5-DBD-SQLite Red Hat ; yum install perl-DBI perl-DBD-SQLite Via CPAN ; cpan install DBD::SQLite ## Running ################################################################## Copy the sampledb dir to a writeable location. YMMV as far as running times. Testing was completed on a CentOS 7 VM (2.6 GHz Intel Core i7, 1600 MHz RAM) under /dev/shm/. 1) perl create.pl 100000 0.322s ; 100k rows ; ~ 2.0MB 2) perl query1.pl |tail 4.933s ; w/ MCE->printf(...) perl query1.pl 3.122s ; no MCE->printf(...) perl query2.pl |tail 0.165s ; chunk_size => 100 perl query2.pl |tail 0.125s ; chunk_size => 300 perl query2.pl |tail 0.117s ; chunk_size => 900 perl query3.pl |tail 0.167s ; chunk_size => 100 perl query3.pl |tail 0.124s ; chunk_size => 300 perl query3.pl |tail 0.118s ; chunk_size => 900 3) perl update1.pl >/dev/null 21.163s ; prefetch size: 300 one update per commit perl update2.pl >/dev/null 2.931s ; chunk_size => 100 perl update2.pl >/dev/null 0.963s ; chunk_size => 300 perl update2.pl >/dev/null 0.532s ; chunk_size => 900 perl update3.pl >/dev/null 2.936s ; chunk_size => 100 perl update3.pl >/dev/null 0.961s ; chunk_size => 300 perl update3.pl >/dev/null 0.537s ; chunk_size => 900 MCE-1.608/examples/utf8.pl0000755000076400007640000000405112511671246014215 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## UTF-8 example. ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use MCE::Loop chunk_size => 'auto', max_workers => 'auto'; use utf8; use open qw(:utf8 :std); ############################################################################### ## ---------------------------------------------------------------------------- ## UTF-8 example. This is based on a sample code sent to me by Marcus Smith. ## ############################################################################### ## Iterate over @list and output to STDOUT three times: ## - Once from a normal for-loop, ## - Once after fetching results from MCE->do ## - Once after fetching results from MCE->gather ## Some Unicode characters from Basic Latin, Latin-1, and beyond. ## my @list = (qw(U Ö Å Ǣ Ȝ), "\N{INTERROBANG}"); my @list = qw(U Ö Å Ǣ Ȝ); print "0: for-loop: $_\n" for (@list); print "\n"; MCE::Loop::init { gather => sub { print shift() } }; sub callback { my ($msg) = @_; print $msg; return; } mce_loop { my $wid = MCE->wid; for (@{ $_ }) { MCE->do("callback", "$wid: MCE->do: $_\n"); } MCE->sync; for (@{ $_ }) { MCE->gather("$wid: MCE->gather: $_\n"); } } @list; print "\n"; ############################################################################### ## ---------------------------------------------------------------------------- ## Process a scalar like a file. Setting chunk_size to 1 for the demonstration. ## ############################################################################### my $unicode = join("\n", @list) . "\n"; MCE::Loop::init { chunk_size => 1 }; mce_loop_f { my $wid = MCE->wid; MCE->print("$wid: MCE->print: $_"); } \$unicode; print "\n"; MCE-1.608/examples/findnull.pl0000755000076400007640000001504612511671246015150 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## This script outputs line numbers containing null values. ## Matches on regular expressions: /\|\|/, /\|\t\s*\|/, or /\| \s*\|/ ## Null value findings are reported to STDERR. ## ## Slurp IO in MCE is extremely fast. So, no matter how many workers ## you give to the problem, only a single worker slurps the next chunk ## at a given time. You get "sustained" sequential IO plus the workers ## for parallel processing. ## ## usage: findnull.pl [-l] datafile ## findnull.pl wc.pl ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; sub INIT { ## Provide file globbing support under Windows similar to Unix. @ARGV = <@ARGV> if ($^O eq 'MSWin32'); } use MCE; ############################################################################### ## ---------------------------------------------------------------------------- ## Display usage and exit. ## ############################################################################### sub usage { print <<"::_USAGE_BLOCK_END_::"; NAME $prog_name -- report line numbers containing null values SYNOPSIS $prog_name [-l] file DESCRIPTION The $prog_name script displays the line number containing null value(s). A null value is a match on /\\|\\|/ or /\\|\\s+\\|/. The following options are available: --max-workers MAX_WORKERS Specify number of workers for MCE -- default: auto --chunk-size CHUNK_SIZE Specify chunk size for MCE -- default: 2 MiB -l Display the number of lines for the file EXIT STATUS The $prog_name utility exits 0 on success, and >0 if an error occurs. ::_USAGE_BLOCK_END_:: exit 1 } ############################################################################### ## ---------------------------------------------------------------------------- ## Define defaults and process command-line arguments. ## ############################################################################### my $flag = sub { 1; }; my $isOk = sub { (@ARGV == 0 or $ARGV[0] =~ /^-/) ? usage() : shift @ARGV; }; my $chunk_size = '2m'; my $max_workers = 'auto'; my $skip_args = 0; my $l_flag = 0; my $file; while ( my $arg = shift @ARGV ) { unless ($skip_args) { $l_flag = $flag->() and next if ($arg eq '-l'); $skip_args = $flag->() and next if ($arg eq '--'); $max_workers = $isOk->() and next if ($arg =~ /^--max[-_]workers$/); $chunk_size = $isOk->() and next if ($arg =~ /^--chunk[-_]size$/); if ($arg =~ /^--max[-_]workers=(.+)/) { $max_workers = $1; next; } if ($arg =~ /^--chunk[-_]size=(.+)/) { $chunk_size = $1; next; } usage() if ($arg =~ /^-/); } $file = $arg; } usage() unless (defined $file); unless (-e $file) { print "$prog_name: $file: No such file or directory\n"; exit 2; } if (-d $file) { print "$prog_name: $file: Is a directory\n"; exit 1; } ## It is faster to pattern match separately versus combining them ## into one regex delimited by '|'. my @patterns = ('\|\|', '\|\t\s*\|', '\| \s*\|'); my $re = '(?:' . join('|', @patterns) . ')'; $re = qr/$re/; ## Report line numbers containing null values. ## Display the total lines read. my $mce = MCE->new( chunk_size => $chunk_size, max_workers => $max_workers, input_data => $file, gather => preserve_order(), user_func => \&user_func, use_slurpio => 1, init_relay => 0 )->run; if ($l_flag) { my $total_lines = MCE->relay_final; print "$total_lines $file\n" if ($l_flag); } exit; ############################################################################### ## ---------------------------------------------------------------------------- ## Manager function(s). ## ############################################################################### ## Output iterator for gather for preserving output order. sub preserve_order { my %tmp; my $order_id = 1; return sub { my ($chunk_id, $output_ref) = @_; if ($chunk_id == $order_id && keys %tmp == 0) { ## no need to save in cache if orderly print STDERR ${ $output_ref }; $order_id++; } else { ## hold temporarily otherwise $tmp{$chunk_id} = $output_ref; while (1) { last unless exists $tmp{$order_id}; print STDERR ${ delete $tmp{$order_id++} }; } } return; }; } ############################################################################### ## ---------------------------------------------------------------------------- ## Worker function(s). ## ############################################################################### ## The user_func block is called once per each input_data chunk. sub user_func { my ($mce, $chunk_ref, $chunk_id) = @_; my ($found_match, $line_count, @lines); ## Check each regex individually -- faster than (?:...|...|...) ## This is optional, was done to quickly determine for any patterns. for (0 .. @patterns - 1) { if ($$chunk_ref =~ /$patterns[$_]/) { $found_match = 1; last; } } ## Slurp IO is enabled. $chunk_ref points to the raw scalar chunk. ## Each worker receives a chunk relatively fast. open my $_MEM_FH, '<', $chunk_ref; binmode $_MEM_FH; if ($found_match) { ## append line number(s) if found match my ($re1, $re2, $re3) = @patterns; while (<$_MEM_FH>) { # push @lines, $. if (/$re/); push @lines, $. if /$re1/ or /$re2/ or /$re3/; } } else { ## read quickly otherwise 1 while (<$_MEM_FH>); } $line_count = $.; ## obtain number of lines read close $_MEM_FH; ## Relaying is orderly and driven by chunk_id when processing data, otherwise ## task_wid. Only the first sub-task is allowed to relay information. ## Relay the total lines read. $_ is same as $lines_read inside the block. ## my $lines_read = MCE->relay( sub { $_ += $line_count } ); my $lines_read = MCE::relay { $_ += $line_count }; ## Gather output. my $output = ''; for (@lines) { $output .= "NULL value at line ".($_ + $lines_read)." in $file\n"; } MCE->gather($chunk_id, \$output); return; } MCE-1.608/examples/biofasta/0000755000076400007640000000000012511673554014563 5ustar mariomarioMCE-1.608/examples/biofasta/fasta_rdr3.pl0000644000076400007640000000721012511671246017144 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ## FASTA Reader; parallel callback mode using mce_flow_f. ## ## Synopsis ## fasta_rdr3.pl [ /path/to/fastafile.fa [ trim_seq ] ] ## ## CKSIZE=64m perl fasta_rdr3.pl hg19.fa 0 run with chunk_size => '64m' ## CKSIZE=4m perl fasta_rdr3.pl uniref.fa 1 run with chunk_size => '4m' ## ## time NPROCS=1 CKSIZE=4m ./fasta_rdr3.pl uniref.fa 0 | wc -l ## time NPROCS=8 CKSIZE=4m ./fasta_rdr3.pl uniref.fa 1 | wc -l ## ## cat uniref100.fasta | NPROCS=8 CKSIZE=4m perl fasta_rdr3.pl - 0 ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/include'; use Time::HiRes qw( time ); use MCE::Flow; use Fasta; my $nprocs = $ENV{NPROCS} || 2; my $cksize = $ENV{CKSIZE} || '2m'; ## Process file. my $file = shift || \*STDIN; $file = \*STDIN if $file eq '-'; my $trim_seq = shift || 0; my $start = time; mce_flow_f { max_workers => $nprocs, chunk_size => $cksize, RS => "\n>", use_slurpio => 1, gather => output_iter(), }, \&user_func, $file; printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; exit; ############################################################################### ## ## Manager function(s). ## ############################################################################### { ## Iterator for preserving output order. sub output_iter { ## One can have this receive 2 arguments; $chunk_id and $chunk_data. ## However, MCE->freeze is called when more than 1 argument is sent. ## For performance, $chunk_id is attached to the end of $_[0]. my %tmp; my $order_id = 1; return sub { my $chunk_id = substr($_[0], rindex($_[0], ':') + 1); my $chop_len = length($chunk_id) + 1; substr($_[0], -$chop_len, $chop_len, ''); ## trim out chunk_id if ($chunk_id == $order_id && keys %tmp == 0) { ## no need to save in cache if orderly print $_[0]; $order_id++; } else { ## hold temporarily otherwise $tmp{$chunk_id} = $_[0]; while (1) { last unless exists $tmp{$order_id}; print delete $tmp{$order_id++}; } } return; }; } } ############################################################################### ## ## Worker function(s). ## ############################################################################### { use constant { ID => 0, DESC => 1, SEQ => 2 }; ## Callback function for reader called once per each sequence. my $output = ''; ## unique, not shared sub reader_cb { # my ($id, $desc, $seq) = @_; ## this passes by value, # $output .= "$id\t$desc\n"; ## consuming extra memory # $output .= ">$_[ID]\n". $_[SEQ] ."\n"; ## hash-like access, least $output .= "$_[ID]\t$_[DESC]\n"; ## memory consumption return; } ## The user_func block is called once per each input_data chunk. sub user_func { my ($mce, $slurp_ref, $chunk_id) = @_; ## run, calls reader_cb for each sequence Fasta::Reader($slurp_ref, $trim_seq, \&reader_cb); ## send output to the manager process MCE->gather($output .':'. $chunk_id); ## attach chunk_id value $output = ''; ## reset output buffer return; } } MCE-1.608/examples/biofasta/fasta_rdr2.pl0000644000076400007640000000452612511671246017152 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ## FASTA Reader; parallel iterator mode using mce_flow_f. ## ## Synopsis ## fasta_rdr2.pl [ /path/to/fastafile.fa [ trim_seq ] ] ## ## CKSIZE=64m perl fasta_rdr2.pl hg19.fa 0 run with chunk_size => '64m' ## CKSIZE=4m perl fasta_rdr2.pl uniref.fa 1 run with chunk_size => '4m' ## ## time NPROCS=1 CKSIZE=4m ./fasta_rdr2.pl uniref.fa 0 | wc -l ## time NPROCS=8 CKSIZE=4m ./fasta_rdr2.pl uniref.fa 1 | wc -l ## ## cat uniref100.fasta | NPROCS=8 CKSIZE=4m perl fasta_rdr2.pl - 0 ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/include'; use Time::HiRes qw( time ); use MCE::Flow; use Fasta; my $nprocs = $ENV{NPROCS} || 2; my $cksize = $ENV{CKSIZE} || '2m'; ## Process file. my $file = shift || \*STDIN; $file = \*STDIN if $file eq '-'; my $trim_seq = shift || 0; my $start = time; mce_flow_f { max_workers => $nprocs, chunk_size => $cksize, RS => "\n>", use_slurpio => 1, }, \&user_func, $file; printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; exit; ############################################################################### ## ## Worker function(s). ## ############################################################################### { use constant { ID => 0, DESC => 1, SEQ => 2 }; ## The user_func block is called once per each input_data chunk. sub user_func { my ($mce, $slurp_ref, $chunk_id) = @_; ## read from scalar reference my $next_seq = Fasta::Reader($slurp_ref, $trim_seq); ## loop through sequences in $slurp_ref my ($id, $desc, $seq, $output); while (my $fa = &$next_seq()) { # ($id, $desc, $seq) = @{ $fa }; ## this passes by value, # $output .= "$id\t$desc\n"; ## consuming extra memory # $output .= ">$fa->[ID]\n".$fa->[SEQ]."\n"; ## hash-like access, least $output .= "$fa->[ID]\t$fa->[DESC]\n"; ## memory consumption } ## send output to STDOUT for this chunk MCE->print($output); return; } } MCE-1.608/examples/biofasta/fasta_aidx.pl0000644000076400007640000001016412511671246017221 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ## FASTA index (.fai) generation for FASTA files. ## ## The original plan was to run CPAN BioUtil::Seq::FastaReader in parallel. ## I wanted to process by records versus lines ($/ = "\n>") for faster ## performance. Created for the investigative Bioinformatics field. ## ## Synopsis ## fasta_aidx.pl [ /path/to/fastafile.fa ] ## ## FAIDXC=1 perl fasta_aidx.pl ... use Inline C for better performance ## NPROCS=4 perl fasta_aidx.pl ... run with max_workers => 4 ## ## CKSIZE=64m perl fasta_aidx.pl hg19.fa run with chunk_size => '64m' ## CKSIZE=4m perl fasta_aidx.pl uniref.fa run with chunk_size => '4m' ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/include'; use Time::HiRes qw( time ); use MCE::Flow; use Fasta; ## Enable Inline C if desired. my $faidx = \&Fasta::Faidx; if (exists $ENV{FAIDXC} && $ENV{FAIDXC} eq '1') { require FastaC; $faidx = \&FastaC::Faidx; } ## Obtain handle to file. my $file = shift || \*STDIN; $file = \*STDIN if $file eq '-'; my $start = time; my $output_fh; if (ref $file) { $output_fh = \*STDOUT; ## Output is sent to STDOUT if reading STDIN } else { die "cannot access $file: $!\n" unless -f $file; open($output_fh, '>', "$file.fai") or die "$file.fai: open: $!\n"; } ## Run in parallel via MCE. Pass fh to output iter. my $nprocs = $ENV{NPROCS} || 2; my $cksize = $ENV{CKSIZE} || '4m'; print {*STDERR} "Building $file.fai\n" unless ref $file; mce_flow_f { gather => output_iter($output_fh), init_relay => 0, max_workers => $nprocs, chunk_size => $cksize, RS => "\n>", use_slurpio => 1, }, \&user_func, $file; ## Finish. close $output_fh unless ref $file; printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; exit; ############################################################################### ## ## Manager function(s). ## ############################################################################### { ## Iterator for preserving output order. sub output_iter { my ($output_fh) = @_; my %tmp; my $order_id = 1; ## One can have this receive 2 arguments; $chunk_id and $chunk_data. ## However, MCE->freeze is called when more than 1 argument is sent. ## For performance, $chunk_id is attached to the end of $_[0]. return sub { my $chunk_id = substr($_[0], rindex($_[0], ':') + 1); my $chop_len = length($chunk_id) + 1; substr($_[0], -$chop_len, $chop_len, ''); if ($chunk_id == $order_id && keys %tmp == 0) { ## no need to save in cache if orderly print {$output_fh} $_[0]; $order_id++; } else { ## hold temporarily otherwise $tmp{$chunk_id} = $_[0]; while (1) { last unless exists $tmp{$order_id}; print {$output_fh} delete $tmp{$order_id++}; } } return; }; } } ############################################################################### ## ## Worker function(s). ## ############################################################################### { ## The user_func block is called once per each input_data chunk. sub user_func { my ($mce, $slurp_ref, $chunk_id) = @_; my @output; ## run, appends to @output; relay next offset value after running ## relaying is orderly and driven by chunk_id behind the scene my $acc = $faidx->($slurp_ref, \@output); my $off = MCE::relay { $_ += $acc }; ## update offsets; reader appended 3 items (left, $c3, right) ## e.g. "$c1\t$c2\t", $c3, "\t$c4\t$c5\n" my $i = 1; my $cnt = scalar @output / 3; for (1 .. $cnt) { $output[$i] += $off if ($output[$i] >= 0); $i += 3; } ## send output to the manager process MCE->gather(join('', @output) .':'. $chunk_id); return; } } MCE-1.608/examples/biofasta/include/0000755000076400007640000000000012511673554016206 5ustar mariomarioMCE-1.608/examples/biofasta/include/fasta_aidx.txt0000644000076400007640000000736312511671246021057 0ustar mariomario ## This file provides the time taken to index three FASTA files. () The system is configured with two Intel Xeon E5-2660 (v1) 2.2 GHz; 128 GB 1600 MHz ECC memory; TurboBoost enabled; running CentOS 7. Testing occured from /dev/shm. The unix real time is recorded; e.g. time perl fasta_aidx.pl ... ########################################################################## ## Results for 93 sequences from hg19.fa; 3.0 GiB, 62.7 million lines. ########################################################################## () hg19.fa pyfaidx 0m52.626s (Python) samtools 0m20.077s (C) fastahack 0m 3.897s (C++) CKSIZE=64m Perl MCE w/InlineC fasta_aidx.pl 0m34.575s NPROCS=1 0m 9.126s 3.0 GHz 0m20.169s NPROCS=2 0m 5.500s 3.0 GHz 0m11.706s NPROCS=4 0m 3.594s 3.0 GHz 0m 8.646s NPROCS=8 0m 3.458s 2.9 GHz 0m 6.379s NPROCS=12 0m 3.510s 2.8 GHz 0m 5.550s NPROCS=16 0m 3.540s 2.7 GHz 0m 5.988s NPROCS=20 0m 3.624s 2.7 GHz Several records within hg19.fa are quite large; up to ~ 250 MiB. ########################################################################## ## Results for 2 million sequences extracted from UniRef100; ## $ gunzip -c uniref100.fasta.gz | head -15687827 > uniref.2mil.fa ## 1.0 GiB, 15.7 million lines. ########################################################################## () uniref.2mil.fa pyfaidx 0m20.520s (Python) fastahack 0m14.112s (C++) samtools 0m 8.332s (C) CKSIZE=4m Perl MCE w/InlineC fasta_aidx.pl 0m19.165s NPROCS=1 0m12.376s 3.0 GHz 0m 9.595s NPROCS=2 0m 6.211s 3.0 GHz 0m 5.009s NPROCS=4 0m 3.193s 3.0 GHz 0m 2.700s NPROCS=8 0m 1.785s 2.9 GHz 0m 1.959s NPROCS=12 0m 1.349s 2.8 GHz 0m 1.570s NPROCS=16 0m 1.089s 2.7 GHz 0m 1.558s NPROCS=20 0m 1.068s 2.7 GHz 0m 1.433s NPROCS=24 0m 1.038s 2.7 GHz 0m 1.354s NPROCS=28 0m 0.949s 2.7 GHz 0m 1.267s NPROCS=32 0m 0.916s 2.7 GHz The dual E5-2660 (v1) combined have 32 logical cores, of which 16 are real cores and 16 hyper-threads; TurboBoost enabled. ########################################################################## ## Results for 10 million sequences extracted from UniRef100; ## $ gunzip -c uniref100.fasta.gz | head -78658664 > uniref.10mil.fa ## 4.7 GiB; 78.7 million lines. ########################################################################## () uniref.10mil.fa pyfaidx 1m39.297s (Python) fastahack 0m58.831s (C++) samtools 0m42.117s (C) CKSIZE=4m Perl MCE w/InlineC fasta_aidx.pl 1m36.285s NPROCS=1 1m 0.879s 3.0 GHz 0m48.307s NPROCS=2 0m30.265s 3.0 GHz 0m25.091s NPROCS=4 0m15.913s 3.0 GHz 0m13.354s NPROCS=8 0m 8.428s 2.9 GHz 0m 9.261s NPROCS=12 0m 6.034s 2.8 GHz 0m 7.227s NPROCS=16 0m 4.852s 2.7 GHz 0m 7.075s NPROCS=20 0m 4.727s 2.7 GHz 0m 6.545s NPROCS=24 0m 4.390s 2.7 GHz 0m 6.108s NPROCS=28 0m 4.138s 2.7 GHz 0m 5.636s NPROCS=32 0m 3.884s 2.7 GHz The dual E5-2660 (v1) combined have 32 logical cores, of which 16 are real cores and 16 hyper-threads; TurboBoost enabled. MCE-1.608/examples/biofasta/include/Fasta.pm0000644000076400007640000001373112511671246017603 0ustar mariomario ## Created to demonstrate accessing FASTA data by records, not lines. ## MCE scripts must specify option; RS => "\n>" use strict; use warnings; use bytes; package Fasta; ############################################################################### ## Generates output suitable for FASTA (.fai) index files. ############################################################################### sub Faidx { my ($file, $to) = @_; my $do_iter = (ref $to ne 'ARRAY') ? 1 : 0; my ($open_flg, $finished, $first_flg) = (0, 0, 1); my ($fh, $pos, $hdr, $seq); if (ref $file eq '' || ref $file eq 'SCALAR') { open($fh, '<', $file) or die "$file: open: !\n"; $open_flg = 1; } else { $fh = $file; } my ($c1, $c2, $c3, $c4, $c5, $p1, $p2, $acc, $adj, $errcnt); ## $c1 = the name of the sequence ## $c2 = the length of the sequence ## $c3 = the offset of the first base in the file ## $c4 = the number of bases in each fasta line ## $c5 = the number of bytes in each fasta line $acc = Fasta::_read_to_byte($fh, '>'); ## read to first /^>/m ## define the iterator function my $iter = sub { return if $finished; local $/ = "\n>"; ## input record separator while ($seq = <$fh>) { if (substr($seq, -1, 1) eq '>') { $adj = 1; substr($seq, -1, 1, ''); ## trim trailing ">" } else { $adj = 0; } $pos = index($seq, "\n") + 1; ## header and sequence $hdr = substr($seq, 0, $pos); ## 1st, extract the header substr($seq, 0, $pos, ''); ## trim header afterwards ($c1) = ($hdr) =~ /^(\S+)/; ## compute initial values $c2 = length($seq); $c3 = $acc + length($hdr); $c5 = index($seq, "\n"); $acc = $c3 + $c2 + $adj; if ($c5 < 0) { ## has no bases return [ $c1, 0, -1, 0, 0, $acc ] if $do_iter; ## return if iter push @{$to}, "$c1\t0\t", -1, "\t0\t0\n"; ## append to array next; } $c4 = (substr($seq, $c5 - 1, 1) eq "\r") ? $c5 - 1 : $c5; ## scans $seq twice (index and tr); thus the reason for FastaC my @a; $p1 = $c5 + 1; $errcnt = 0; ## start on 2nd bases line while ($p1 < $c2) { ## collect line lengths $p2 = index($seq, "\n", $p1); push @a, $p2 - $p1; $p1 = $p2 + 1; } if (scalar @a) { pop @a while ($a[-1] == 0); ## pop trailing blank lines pop @a; ## pop last line w/ bases foreach (@a) { ## any length mismatch? $errcnt++ if $_ != $c5; } } $seq =~ tr/\t\r\n //d; ## trim white space $c2 = length($seq); $c5++; ## if ($errcnt) { print {*STDERR} "SKIPPED: mismatched line lengths within sequence $c1\n"; next; } return [ $c1, $c2, $c3, $c4, $c5, $acc ] if $do_iter; ## iterator push @{$to}, "$c1\t$c2\t", $c3, "\t$c4\t$c5\n"; ## array } close $fh if $open_flg; $finished = 1; return; }; ## return the iterator itself, otherwise run if ($do_iter) { return $iter; } else { 1 while $iter->(); return $acc; } } ############################################################################### ## General FASTA reader extracting ID, description, and sequence. ############################################################################### sub Reader { my ($file, $trim_seq, $callback) = @_; my $do_iter = (ref $callback ne 'CODE') ? 1 : 0; my ($open_flg, $finished) = (0, 0); my ($fh, $pos, $hdr, $seq, $id, $desc); if (ref $file eq '' || ref $file eq 'SCALAR') { open($fh, '<', $file) or die "$file: open: !\n"; $open_flg = 1; } else { $fh = $file; } _read_to_byte($fh, '>'); ## read to first /^>/m ## define the iterator function my $iter = sub { return if $finished; local $/ = "\n>"; ## input record separator while ($seq = <$fh>) { if (substr($seq, -1, 1) eq '>') { substr($seq, -1, 1, ''); ## trim trailing ">" } $pos = index($seq, "\n") + 1; ## header and sequence $hdr = substr($seq, 0, $pos - 1); ## 1st, extract the header substr($seq, 0, $pos, ''); ## trim header afterwards chop $hdr if substr($hdr, -1, 1) eq "\r"; ## trim trailing "\r" $seq =~ tr/\t\r\n //d if $trim_seq; ## trim white space if (($pos = index($hdr, ' ')) > 0) { ## id and description $id = substr($hdr, 0, $pos); $desc = substr($hdr, $pos + 1); } else { $id = $hdr; $desc = ''; } return [ $id, $desc, $seq ] if $do_iter; ## iterator, otherwise call $callback->($id, $desc, $seq); ## callback function } close $fh if $open_flg; $finished = 1; return; }; ## return the iterator itself, otherwise run if ($do_iter) { return $iter; } else { 1 while $iter->(); return; } } ############################################################################### ## Private function(s). ############################################################################### sub _read_to_byte { my ($fh, $byte) = @_; my ($bytes, $line_pos) = (0, 0); local $/ = \1; ## read one byte including while (<$fh>) { ## the first /^$byte/m $bytes++; last if ($line_pos++ == 0 && $_ eq $byte); $line_pos = 0 if ($_ eq "\n"); } return $bytes; } 1; MCE-1.608/examples/biofasta/include/sample.fasta0000644000076400007640000000365312511671246020512 0ustar mariomario>seq1 description1 TAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCC TAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCAACCCTAACCCT AACCCTAACCCTAACCCTAACCCTAACCCCTAACCCTAACCCTAACCCTAACCCTAACCT AACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCCTAACCC TAACCCTAAACCCTAAACCCTAACCCTAACCCTAACCCTAACCCTAACCCCAACCCCAAC CCCAACCCCAACCCCAACCCCAACCCTAACCCCTAACCCTAACCCTAACCCTACCCTAAC CCTAACCCTAACCCTAACCCTAACCCTAACCCCTAACCCCTAACCCTAACCCTAACCCTA ACCCTAACCCTAACCCTAACCCCTAACCCTAACCCTAACCCTAACCCTCGCGGTACCCTC AGCCGGCCCGCCCGCCCGGGTCTGACCTGAGGAGAACTGTGCTCCGCCTTCAGAGTACCA CCGAAATCTGTGCAGAGGACAACGCAGCTCCGCCCTCGCGGTGCTCTCCGGGTCTGTGCT GAGGAGAACGCAACTCCGCCGGCGCAGGCG >seq2 description2 TAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCC TAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCAACCCTAACCCT AACCCTAACCCTAACCCTAACCCTAACCCCTAACCCTAACCCTAACCCTAACCCTAACCT AACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCCTAACCC TAACCCTAAACCCTAAACCCTAACCCTAACCCTAACCCTAACCCTAACCCCAACCCCAAC CCCAACCCCAACCCCAACCCCAACCCTAACCCCTAACCCTAACCCTAACCCTACCCTAAC CCTAACCCTAACCCTAACCCTAACCCTAACCCCTAACCCCTAACCCTAACCCTAACCCTA ACCCTAACCCTAACCCTAACCCCTAACCCTAACCCTAACCCTAACCCTCGCGGTACCCTC AGCCGGCCCGCCCGCCCGGGTCTGACCTGAGGAGAACTGTGCTCCGCCTTCAGAGTACCA CCGAAATCTGTGCAGAGGACAACGCAGCTCCGCCCTCGCGGTGCTCTCCGGGTCTGTGCT GAGGAGAACGCAAC >seq3 description3 TAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCC TAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCAACCCTAACCCT AACCCTAACCCTAACCCTAACCCTAACCCCTAACCCTAACCCTAACCCTAACCCTAACCT AACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCCTAACCC TAACCCTAAACCCTAAACCCTAACCCTAACCCTAACCCTACTACCCTAACCCTAACCCTA ACCCTAACCCTAACCCTAACCCCTAACCCCTAACCCTAACCCTAACCCTAACCCTAACCC TAACCCTAACCCCTAACCCTAACCCTAACCCTAACCCTCGCGGTACCCTCAGCCGGCCCG CCCGCCCGGGTCTGACCTGAGGAGAACTGTGCTCCGCCTTCAGAGTACCACCGAAATCTG TGCAGAGGACAACGCAGCTCCGCCCTCGCGGTGCTCTCCGGGTCTGTGCTGAGGAGAACG CAACTCCGCCGGCGCAGGCGACCCTAACCCCAACCCCAACCCCAACCCCAACCCCAACCC CAACCCTAACCCCTAACCCTAACCCT MCE-1.608/examples/biofasta/include/FastaC.pm0000644000076400007640000000621312511671246017703 0ustar mariomario ## Created to demonstrate accessing FASTA data by records, not lines. ## MCE scripts must specify option; RS => "\n>" use strict; use warnings; use bytes; my $prog_dir; BEGIN { use Cwd 'abs_path'; $prog_dir = ($0 =~ m{^(.*)[\\/]} && $1 || abs_path); $ENV{PERL_INLINE_DIRECTORY} = "${prog_dir}/.Inline"; mkdir "${prog_dir}/.Inline" unless -d "${prog_dir}/.Inline"; } package FastaC; ############################################################################### ## Generates output suitable for FASTA (.fai) index files. ############################################################################### use Inline 'C' => "${prog_dir}/include/fasta_seqlen.c"; sub Faidx { my ($file, $to) = @_; my $do_iter = (ref $to ne 'ARRAY') ? 1 : 0; my ($open_flg, $finished, $first_flg) = (0, 0, 1); my ($fh, $pos, $hdr, $seq); if (ref $file eq '' || ref $file eq 'SCALAR') { open($fh, '<', $file) or die "$file: open: !\n"; $open_flg = 1; } else { $fh = $file; } my ($c1, $c2, $c3, $c4, $c5, $p1, $p2, $acc, $adj, $errcnt); ## $c1 = the name of the sequence ## $c2 = the length of the sequence ## $c3 = the offset of the first base in the file ## $c4 = the number of bases in each fasta line ## $c5 = the number of bytes in each fasta line $acc = Fasta::_read_to_byte($fh, '>'); ## read to first /^>/m ## define the iterator function my $iter = sub { return if $finished; local $/ = "\n>"; ## input record separator while ($seq = <$fh>) { if (substr($seq, -1, 1) eq '>') { $adj = 1; substr($seq, -1, 1, ''); ## trim trailing ">" } else { $adj = 0; } $pos = index($seq, "\n") + 1; ## header and sequence $hdr = substr($seq, 0, $pos); ## 1st, extract the header substr($seq, 0, $pos, ''); ## trim header afterwards ($c1) = ($hdr) =~ /^(\S+)/; ## compute initial values $c2 = length($seq); $c3 = $acc + length($hdr); $c5 = index($seq, "\n"); $acc = $c3 + $c2 + $adj; if ($c5 < 0) { ## has no bases return [ $c1, 0, -1, 0, 0, $acc ] if $do_iter; ## return if iter push @{$to}, "$c1\t0\t", -1, "\t0\t0\n"; ## append to array next; } $c4 = (substr($seq, $c5 - 1, 1) eq "\r") ? $c5 - 1 : $c5; ## scans $seq once; also checks for mismatch in line lengths ($c2, $errcnt) = @{ fasta_seqlen($seq, ++$c5) }; ## if ($errcnt) { print {*STDERR} "SKIPPED: mismatched line lengths within sequence $c1\n"; next; } return [ $c1, $c2, $c3, $c4, $c5, $acc ] if $do_iter; ## iterator push @{$to}, "$c1\t$c2\t", $c3, "\t$c4\t$c5\n"; ## array } close $fh if $open_flg; $finished = 1; return; }; ## return the iterator itself, otherwise run if ($do_iter) { return $iter; } else { 1 while $iter->(); return $acc; } } 1; MCE-1.608/examples/biofasta/include/fasta_seqlen.c0000644000076400007640000000310512511671246021012 0ustar mariomario#line 2 "./include/fasta_seqlen.c" //############################################################################# // ---------------------------------------------------------------------------- // C source for returning the sequence length in a string including error // checking for mismatch in line lengths. // // my $ret = fasta_seqlen($str, $firstlen); // ## seqlen: $ret->[0] // ## errcnt: $ret->[1] > 0 e.g. length mismatch among lines // //############################################################################# #include SV * fasta_seqlen(SV *str_sv, SV *firstlen_sv) { AV *ret = newAV(); const char *str = SvPVX(str_sv); uint32_t firstlen = SvUV(firstlen_sv); uint32_t strsize = SvCUR(str_sv); uint32_t lastline, linelen, seqlen, blank, errcnt; lastline = linelen = seqlen = blank = errcnt = 0; while (strsize--) { linelen++; if (*str > ' ') { seqlen++; } else if (*str == '\r') { // filter out cr from nl calculations linelen--; } else if (*str == '\n') { if (linelen == 1) { blank++; } else if (blank && linelen > 1) { errcnt++; } else if (linelen != firstlen) { errcnt++; lastline = 1; } else { lastline = 0; } linelen = 0; } str++; } if (lastline == 1 && errcnt == 1) errcnt--; av_push(ret, newSVuv(seqlen)); av_push(ret, newSVuv(errcnt)); // ret = [ seqlen, errcnt ] return newRV_noinc((SV*) ret); } MCE-1.608/examples/biofasta/include/fasta_rdr.txt0000644000076400007640000000345412511671246020716 0ustar mariomario ## This file provides the time taken to read a FASTA file in parallel. The system is a Core i7 Quad-Core (Haswell) at 2.6 GHz running OS X v10.9.5. Testing occured from a RAMdisk volume to factor out disk IO. fasta_rdr1.pl: serial iterator, parallel consumers fasta_rdr2.pl: parallel iterator mode fasta_rdr3.pl: parallel callback mode ## Synopsis fasta_rdr [ /path/to/fastafile.fa [ trim_seq ] ] NPROCS=4 perl fasta_rdr hg19.fa 0 run with 4 workers, no trimming NPROCS=8 perl fasta_rdr uniref.fa 1 run with 8 workers, trim white space ## Results for 2 million sequences extracted from UniRef100.fasta.gz. $ gunzip -c uniref100.fasta.gz | head -15687827 > uniref.fa 1.0 GiB, 15.7 million lines -- extract id, description, and sequence (no trimming) NPROCS=4 perl fasta_rdr1.pl uniref.fa 0 | wc -l 29.041s NPROCS=1 CKSIZE=4m perl fasta_rdr2.pl uniref.fa 0 | wc -l 6.976s NPROCS=2 CKSIZE=4m perl fasta_rdr2.pl uniref.fa 0 | wc -l 3.506s NPROCS=4 CKSIZE=4m perl fasta_rdr2.pl uniref.fa 0 | wc -l 2.100s NPROCS=1 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 0 | wc -l 3.341s NPROCS=2 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 0 | wc -l 1.637s NPROCS=4 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 0 | wc -l 0.993s -- trim white space NPROCS=4 perl fasta_rdr1.pl uniref.fa 1 | wc -l 31.748s NPROCS=1 CKSIZE=4m perl fasta_rdr2.pl uniref.fa 1 | wc -l 8.947s NPROCS=2 CKSIZE=4m perl fasta_rdr2.pl uniref.fa 1 | wc -l 4.564s NPROCS=4 CKSIZE=4m perl fasta_rdr2.pl uniref.fa 1 | wc -l 2.682s NPROCS=1 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 1 | wc -l 5.472s NPROCS=2 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 1 | wc -l 2.735s NPROCS=4 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 1 | wc -l 1.577s MCE-1.608/examples/biofasta/fasta_rdr1.pl0000644000076400007640000000401212511671246017137 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ## FASTA Reader; serial iterator, parallel consumers using mce_flow. ## ## Synopsis ## fasta_rdr1.pl [ /path/to/fastafile.fa [ trim_seq ] ] ## ## NPROCS=4 perl fasta_rdr1.pl hg19.fa 0 run with max_workers => 4 ## NPROCS=8 perl fasta_rdr1.pl uniref.fa 1 run with max_workers => 8 ## ## time NPROCS=1 ./fasta_rdr1.pl uniref.fa 0 | wc -l ## time NPROCS=8 ./fasta_rdr1.pl uniref.fa 1 | wc -l ## ## cat uniref100.fasta | NPROCS=8 perl fasta_rdr1.pl - 0 ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/include'; use Time::HiRes qw( time ); use MCE::Flow; use Fasta; my $nprocs = $ENV{NPROCS} || 2; ## Process file. my $file = shift || \*STDIN; $file = \*STDIN if $file eq '-'; my $trim_seq = shift || 0; my $start = time; mce_flow { input_data => Fasta::Reader($file, $trim_seq), max_workers => $nprocs, }, \&user_func; printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; exit; ############################################################################### ## ## Worker function(s). ## ############################################################################### { use constant { ID => 0, DESC => 1, SEQ => 2 }; ## The user_func block is called once per each iterator request. sub user_func { my ($mce, $iter_ref, $chunk_id) = @_; # my ($id, $desc, $seq) = @{ $iter_ref->[0] }; ## $_ points to ->[0] when # my ($id, $desc, $seq) = @{ $_ }; ## chunk_size == 1, default ## for input iterators my $fa = $iter_ref->[0]; ## send output to STDOUT for this record # MCE->print(">$fa->[ID]\n".$fa->[SEQ]."\n"); MCE->print("$fa->[ID]\t$fa->[DESC]\n"); return; } } MCE-1.608/examples/biofasta/README0000644000076400007640000000561412511671246015445 0ustar mariomario ## Parallel demonstration for Bioinformatics After releasing MCE v1.522 on Christmas day, I saw BioUtil v2014.1226 released one day later. Several folks have asked for parallel examples. At the time, BioUtil processed by lines. I thought (not knowing what I was getting into) why not process by records instead via $/ = "\n>". The installation of Inline::C is optional unless running with FAIDXC=1. apt-get install \ (Debian/Ubuntu) gcc perl-modules libinline-perl libparse-recdescent-perl pkg install \ (FreeBSD/PC-BSD/TrueOS) gcc p5-ExtUtils-MakeMaker p5-Inline p5-Parse-RecDescent yum install \ (Red Hat/CentOS/Fedora) gcc perl-ExtUtils-MakeMaker perl-Inline perl-Parse-RecDescent FAIDXC=1 perl fasta_aidx.pl < include/sample.fasta -- Directory contents README This readme file fasta_aidx.pl FASTA Indexer : Parallel FASTA index generation fasta_rdr1.pl FASTA Reader : Serial iterator mode, many consumers fasta_rdr2.pl FASTA Reader : Parallel iterator mode fasta_rdr3.pl FASTA Reader : Parallel callback mode include/ Fasta.pm Perl module containing Faidx and Reader FastaC.pm Perl module containing Faidx calling C code fasta_aidx.txt Results from a dual E5-2660 (v1) 2.2 GHz machine fasta_rdr.txt Results from a Haswell Core i7 2.6 GHz laptop fasta_seqlen.c C code for better performance sample.fasta Sample FASTA file ## Synopsis The examples were created to demonstrate parallelism in MCE; fasta_aidx.pl is not an official indexer by any means. -- FASTA Indexer fasta_aidx.pl [ /path/to/fastafile.fa ] fasta_aidx.pl < include/sample.fasta ## output sent to STDOUT NPROCS=4 CKSIZE=4m perl fasta_aidx.pl uniref.fa ## 100% Perl NPROCS=4 CKSIZE=64m perl fasta_aidx.pl hg19.fa NPROCS=4 CKSIZE=4m FAIDXC=1 perl fasta_aidx.pl uniref.fa ## Inline C NPROCS=4 CKSIZE=64m FAIDXC=1 perl fasta_aidx.pl hg19.fa -- FASTA Reader fasta_rdr3.pl [ /path/to/fastafile.fa [ trim_seq ] ] fasta_rdr3.pl < include/sample.fasta ## output sent to STDOUT NPROCS=4 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 0 | wc -l NPROCS=4 CKSIZE=4m perl fasta_rdr3.pl uniref.fa 1 | wc -l ## History -- Four folks have inspired me along the way Demian Riccardi Mentioned on making an annoucement to BioPerl. I had nothing to offer as far as examples until now. Hisham ElDai Made a suggestion on downloading hg19.fa. This release consumes lesser memory for processing input data. Jillian Rowe Mentioned Scientist using MCE every day. This inspired me to not give up, especially with this release. Wei Shen Author of BioUtil. The FastaReader function inspired me to write parallel examples. MCE-1.608/examples/forseq.pl0000755000076400007640000000614512511671246014634 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## This example demonstrates the sqrt example from Parallel::Loops ## (Parallel::Loops v0.07 utilizing Parallel::ForkManager v1.07). ## ## Testing was on a Linux VM; Perl v5.20.1; Haswell i7 at 2.6 GHz. ## The number indicates the size of input displayed in 1 second. ## Output was directed to >/dev/null. ## ## Parallel::Loops: 1,600 Forking each @input is expensive ## MCE->foreach...: 23,000 Workers persist between each @input ## MCE->forseq....: 200,000 Uses sequence of numbers as input ## MCE->forchunk..: 800,000 IPC overhead is greatly reduced ## ## usage: forseq.pl [ size ] ## usage: forseq.pl [ begin end [ step [ format ] ] ] ## ## The % character is optional for format. ## forseq.pl 20 30 0.2 %4.1f ## forseq.pl 20 30 0.2 4.1f ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use Time::HiRes qw(time); use MCE; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; my $s_begin = shift || 3000; my $s_end = shift; my $s_step = shift || 1; my $s_format = shift; if ($s_begin !~ /\A\d*\.?\d*\z/) { print {*STDERR} "usage: $prog_name [ size ]\n"; print {*STDERR} "usage: $prog_name [ begin end [ step [ format ] ] ]\n"; exit; } unless (defined $s_end) { $s_end = $s_begin - 1; $s_begin = 0; } ############################################################################### ## ---------------------------------------------------------------------------- ## Parallelize via MCE's forseq method. ## ############################################################################### ## Make an output iterator for gather. Output order is preserved. sub preserve_order { my (%result_n, %result_d); my $order_id = 1; return sub { my ($chunk_id, $n, $data) = @_; $result_n{$chunk_id} = $n; $result_d{$chunk_id} = $data; while (1) { last unless exists $result_d{$order_id}; printf "n: %s sqrt(n): %f\n", $result_n{$order_id}, $result_d{$order_id}; delete $result_n{$order_id}; delete $result_d{$order_id}; $order_id++; } return; }; } ## Configure MCE. my $seq = { begin => $s_begin, end => $s_end, step => $s_step, format => $s_format }; ## use MCE::Flow; ## Same thing in MCE 1.5+ ## ## mce_flow_s { ## max_workers => 'auto', chunk_size => 1, gather => preserve_order ## }, ## sub { ## my ($mce, $n, $chunk_id) = @_; ## MCE->gather($chunk_id, $n, sqrt($n)); ## ## }, $s_begin, $s_end, $s_step; my $mce = MCE->new( max_workers => 'auto', chunk_size => 1, gather => preserve_order ); ## Use $n or $_ to retrieve the single element. my $start = time; $mce->forseq( $seq, sub { my ($mce, $n, $chunk_id) = @_; MCE->gather($chunk_id, $n, sqrt($n)); }); printf {*STDERR} "\n## Compute time: %0.03f\n\n", time - $start; MCE-1.608/examples/pipe1.pl0000755000076400007640000001607012511671246014351 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## Process STDIN or FILE via Perl in parallel. ## ## This is by no means a complete script, but rather a "how-to" for folks ## wanting to create their own parallel script. ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my ($prog_name, $prog_dir); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $ENV{PATH} = $prog_dir .($^O eq 'MSWin32' ? ';' : ':'). $ENV{PATH}; } use Getopt::Long qw( :config bundling pass_through no_ignore_case no_auto_abbrev ); use Scalar::Util qw( looks_like_number ); use Fcntl qw( O_RDONLY ); use MCE::Signal qw( -use_dev_shm ); use MCE::Loop; ############################################################################### ## ---------------------------------------------------------------------------- ## Display usage and exit. ## ############################################################################### sub usage { print {*STDERR} <<"::_USAGE_BLOCK_END_::"; NAME $prog_name -- process STDIN or FILE via Perl in parallel SYNOPSIS $prog_name [script_options] [FILE] DESCRIPTION The $prog_name script processes STDIN or FILE in parallel. STDIN is read unless FILE is specified. Specifing more than 1 file will error. The following options are available: --RS RECORD_SEPARATOR Input record separator -- default: newline --chunk-size CHUNK_SIZE Specify chunk size for MCE -- default: auto Can also take a suffix; K/k (kilobytes) or M/m (megabytes). Less than or equal to 8192 is the number of records. Greater than 8192 is the number of bytes. The maximum is 24m by MCE internally. --max-workers MAX_WORKERS Specify number of workers for MCE -- default: 8 --parallel-io Enable parallel IO for FILE. This is not recommended if running on several nodes simultaneously and reading from the same shared storage. EXIT STATUS $prog_name exits 0 on success, and >0 if an error occurs. EXAMPLES Process STDIN (workers request the next chunk from the manager process). cat infile | $prog_name --chunk-size=2k >out 2>err $prog_name --chunk-size=2k < infile >out 2>err Process FILE (workers communicate the next offset among themselves). $prog_name --chunk-size=2k infile >out 2>err ::_USAGE_BLOCK_END_:: exit 1; } ############################################################################### ## ---------------------------------------------------------------------------- ## Define defaults and process command-line arguments. Determine input stream. ## ############################################################################### my $RS = $/; my $chunk_size = 'auto'; my $max_workers = 8; my $parallel_io = 0; { local $SIG{__WARN__} = sub { }; GetOptions( 'RS=s' => \$RS, 'chunk-size|chunk_size=s' => \$chunk_size, 'max-workers|max_workers=s' => \$max_workers, 'parallel-io|parallel_io' => \$parallel_io ); if ($max_workers !~ /^auto/) { unless (looks_like_number($max_workers) && $max_workers > 0) { print {*STDERR} "$prog_name: $max_workers: invalid max workers\n"; exit 2; } } if ($chunk_size !~ /^auto/) { if ($chunk_size =~ /^(\d+)K/i) { $chunk_size = $1 * 1024; } elsif ($chunk_size =~ /^(\d+)M/i) { $chunk_size = $1 * 1024 * 1024; } if (!looks_like_number($chunk_size) || $chunk_size < 1) { print {*STDERR} "$prog_name: $chunk_size: invalid chunk size\n"; exit 2; } } } usage() if (@ARGV > 1); my $input = (defined $ARGV[0]) ? $ARGV[0] : \*STDIN; if (ref $input eq '') { if (! -e $input) { print {*STDERR} "$prog_name: $input: No such file or directory\n"; exit 2; } if (-d $input) { print {*STDERR} "$prog_name: $input: Is a directory\n"; exit 2; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Output function. Define the gather iterator for preserving output order. ## ############################################################################### my $buf = sprintf('%65536s', ''); ## Create a continuous buffer for the my $exit_status = 0; ## output routine. sub output { my ($file, $sendto_fh) = @_; my ($fh, $n_read); if (-s $file) { sysopen($fh, $file, O_RDONLY); while (1) { $n_read = sysread($fh, $buf, 65536); last if $n_read == 0; syswrite($sendto_fh, $buf, $n_read); } close $fh; } unlink $file; return; } sub gather_iterator { my ($out_fh, $err_fh) = @_; my %tmp; my $order_id = 1; return sub { my ($chunk_id, $path, $status) = @_; $tmp{$chunk_id} = $path; $exit_status = $status if ($status > $exit_status); while (1) { last unless exists $tmp{$order_id}; $path = delete $tmp{$order_id++}; output("$path.err", $err_fh); output("$path.out", $out_fh); } }; } ############################################################################### ## ---------------------------------------------------------------------------- ## Configure MCE. Process STDIN in parallel afterwards. The mce_loop_f routine ## can take a GLOB reference or a scalar containing the path to the file. ## ############################################################################### MCE::Loop::init { RS => $RS, use_slurpio => 1, parallel_io => $parallel_io, chunk_size => $chunk_size, max_workers => $max_workers, gather => gather_iterator(\*STDOUT, \*STDERR) }; mce_loop_f { my ($mce, $chunk_ref, $chunk_id) = @_; my $path = MCE->tmp_dir .'/'. $chunk_id; my $chunk_status = 0; open my $out_fh, ">", "$path.out"; open my $err_fh, ">", "$path.err"; ## open my $mem_fh, "<", $chunk_ref; ## $chunk_ref is a scalar ref ## ## when use_slurpio => 1 ## while (<$mem_fh>) { ## print $out_fh $_; ## Consider appending to an array. ## } ## Then write to output handle. ## ## close $mem_fh; print $out_fh $$chunk_ref; ## (or) write entire chunk close $out_fh; close $err_fh; MCE->gather($chunk_id, $path, $chunk_status); } $input; ############################################################################### ## ---------------------------------------------------------------------------- ## Cleanup and exit. ## ############################################################################### MCE::Loop::finish; exit $exit_status; MCE-1.608/examples/wc.pl0000755000076400007640000002224412511671246013744 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## Word count script similar to the wc binary. ## ## The logic below does not support multi-byte characters. The focus is ## demonstrating Many-Core Engine for Perl. Use this script for large ## file(s). ## ## The usage description was largely ripped off from the wc man page. ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; sub INIT { ## Provide file globbing support under Windows similar to Unix. @ARGV = <@ARGV> if ($^O eq 'MSWin32'); } use IPC::Open2; use MCE; ############################################################################### ## ---------------------------------------------------------------------------- ## Display usage and exit. ## ############################################################################### sub usage { print <<"::_USAGE_BLOCK_END_::"; NAME $prog_name -- word, line, and character count SYNOPSIS $prog_name [-clw] [file ...] DESCRIPTION The $prog_name utility displays the number of lines, words, and bytes contained in each input file, or standard input (if not file is specified) to the standard output. A line is defined as a string of characters delimited by a character. The following options are available: --max-workers MAX_WORKERS Specify number of workers for MCE -- default: auto --chunk-size CHUNK_SIZE Specify chunk size for MCE -- default: 2 MiB -c Display the number of bytes -l Display the number of lines -w Display the number of words When an option is specified, $prog_name, only reports the information requested by that option. The order of output always takes the form of line, word, byte, and file name. The default action is equivalent to specifying the -c -l and -w options. If no files are specified, the standard input is used and no file name is displayed. The prompt will accept input until receiving EOF, or [^D] in most environments. EXIT STATUS The $prog_name utility exits 0 on success, and >0 if an error occurs. EXAMPLES Count the number of bytes, words and lines in each of the files report1 and report2 as well as the totals for both: $prog_name -c -w -l report1 report2 $prog_name -cwl report1 report2 $prog_name report1 report2 Count the number of lines: (pass -- to treat following args as files) $prog_name -l -- -filename_with_dash ::_USAGE_BLOCK_END_:: exit 1 } ############################################################################### ## ---------------------------------------------------------------------------- ## Define defaults and process command-line arguments. ## ############################################################################### my $flag = sub { 1; }; my $isOk = sub { (@ARGV == 0 or $ARGV[0] =~ /^-/) ? usage() : shift @ARGV; }; my $chunk_size = '2m'; my $max_workers = 'auto'; my $skip_args = 0; my $c_flag = 0; my $l_flag = 0; my $w_flag = 0; my @files = (); while ( my $arg = shift @ARGV ) { unless ($skip_args) { if ($arg eq '-') { push @files, $arg; next; } if ($arg =~ m/^-[clw]+$/) { while ($arg) { my $a = chop($arg); $c_flag = $flag->() and next if ($a eq 'c'); $l_flag = $flag->() and next if ($a eq 'l'); $w_flag = $flag->() and next if ($a eq 'w'); } next; } $skip_args = $flag->() and next if ($arg eq '--'); $max_workers = $isOk->() and next if ($arg =~ /^--max[-_]workers$/); $chunk_size = $isOk->() and next if ($arg =~ /^--chunk[-_]size$/); if ($arg =~ /^--max[-_]workers=(.+)/) { $max_workers = $1; next; } if ($arg =~ /^--chunk[-_]size=(.+)/) { $chunk_size = $1; next; } usage() if ($arg =~ /^-/); } push @files, $arg; } if ($c_flag + $l_flag + $w_flag == 0) { $c_flag = $l_flag = $w_flag = 1; } my ($wc_cmd, $wc_args); if ($^O ne 'cygwin' && $^O ne 'MSWin32') { $wc_cmd = (-x '/usr/bin/wc') ? '/usr/bin/wc' : ((-x '/bin/wc') ? '/bin/wc' : undef); } if (defined $wc_cmd && ($l_flag || $w_flag)) { $wc_args = '-'; $wc_args .= 'l' if ($l_flag); $wc_args .= 'w' if ($w_flag); } ############################################################################### ## ---------------------------------------------------------------------------- ## Configure Many-Core Engine. ## ############################################################################### ## Called once per file (prior to chunking) -- think of awk BEGIN { ... } sub user_begin { my ($mce) = @_; $mce->{wk_lines} = 0; $mce->{wk_words} = 0; $mce->{wk_bytes} = 0; use vars qw($wc_pid $wc_out $wc_in); our ($wc_pid, $wc_out, $wc_in); if (defined $wc_cmd && ($l_flag || $w_flag)) { $wc_pid = open2($wc_out, $wc_in, "$wc_cmd $wc_args"); } return; } ## Called once per chunk of data -- think of forchuck { ... } sub user_func { my ($mce, $chunk_ref, $chunk_id) = @_; my $line_count; if ($l_flag || $w_flag) { if (defined $wc_cmd) { syswrite($wc_in, $$chunk_ref); } else { open my $_MEM_FH, '<', $chunk_ref; binmode $_MEM_FH; 1 while <$_MEM_FH>; $line_count = $.; close $_MEM_FH; $mce->{wk_lines} += $line_count; if ($w_flag) { if (index($$chunk_ref, ' ') >= 0 || index($$chunk_ref, "\t") >= 0) { my $words = 0; $words++ while ($$chunk_ref =~ m!\S+!mg); $mce->{wk_words} += $words; } else { $mce->{wk_words} += $line_count; } } } } $mce->{wk_bytes} += length($$chunk_ref) if ($c_flag); return; } ## Called once per file (after chunking) -- think of awk END { ... } sub user_end { my ($mce) = @_; if (defined $wc_cmd && ($l_flag || $w_flag)) { close $wc_in; my $result = <$wc_out>; chomp $result; if ($result) { if ($l_flag && $w_flag) { if ($result =~ m/(\d+)\s+(\d+)/) { $mce->{wk_lines} = $1; $mce->{wk_words} = $2; } } elsif ($l_flag) { $mce->{wk_lines} = $result; } else { $mce->{wk_words} = $result; } } waitpid($wc_pid, 0); } my %subtotal = ( 'lines' => $mce->{wk_lines}, 'words' => $mce->{wk_words}, 'bytes' => $mce->{wk_bytes} ); $mce->do('main::aggregate_result', \%subtotal); return; } ## Instantiate Many-Core Engine and spawn workers. my $mce = MCE->new( user_begin => \&user_begin, ## Called prior to chunking user_func => \&user_func, ## Think of forchunk { ... } user_end => \&user_end, ## Called after chunking chunk_size => $chunk_size, max_workers => $max_workers, use_slurpio => 1 ); ############################################################################### ## ---------------------------------------------------------------------------- ## Word, line, and character count. ## ############################################################################### my ($f_lines, $f_words, $f_bytes) = (0, 0, 0); my ($t_lines, $t_words, $t_bytes) = (0, 0, 0); my $exit_status = 0; sub aggregate_result { my $subtotal_ref = shift; $f_lines += $subtotal_ref->{'lines'}; $f_words += $subtotal_ref->{'words'}; $f_bytes += $subtotal_ref->{'bytes'}; $t_lines += $subtotal_ref->{'lines'}; $t_words += $subtotal_ref->{'words'}; $t_bytes += $subtotal_ref->{'bytes'}; return; } sub display_result { my ($lines, $words, $bytes, $file) = @_; my $result = ''; $result .= sprintf " %7d", $lines if ($l_flag); $result .= sprintf " %7d", $words if ($w_flag); $result .= sprintf " %7d", $bytes if ($c_flag); $result .= sprintf " %s", $file if (defined $file); print $result, "\n"; return; } ## Process files, otherwise read from standard input. if (@files > 0) { for my $file (@files) { if (! -e $file) { print {*STDERR} "$prog_name: $file: No such file or directory\n"; $exit_status = 2; } elsif (-d $file) { print {*STDERR} "$prog_name: $file: Is a directory\n"; $exit_status = 1; } else { if ($c_flag && ($l_flag + $w_flag == 0)) { $f_bytes = -s $file; $t_bytes += $f_bytes; } else { $mce->process($file); } display_result($f_lines, $f_words, $f_bytes, $file); $f_lines = $f_words = $f_bytes = 0; } } if (@files > 1) { display_result($t_lines, $t_words, $t_bytes, 'total'); } } else { $mce->process(\*STDIN); display_result($f_lines, $f_words, $f_bytes); } ## Shutdown Many-Core Engine and exit. $mce->shutdown; exit $exit_status; MCE-1.608/examples/tbray/0000755000076400007640000000000012511673554014114 5ustar mariomarioMCE-1.608/examples/tbray/tbray_baseline1.pl0000644000076400007640000000070212511671246017510 0ustar mariomario#!/usr/bin/env perl ## ## usage: tbray_baseline1.pl < logfile ## use strict; use warnings; use Time::HiRes qw(time); my $start = time; my $rx = qr|GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+) |o; my %count; while (<>) { next unless $_ =~ $rx; $count{$1}++; } my $end = time; print "$count{$_}\t$_\n" for ( sort { $count{$b} <=> $count{$a} } keys %count )[ 0 .. 9 ]; printf "\n## Compute time: %0.03f\n\n", $end - $start; MCE-1.608/examples/tbray/tbray_baseline2.pl0000644000076400007640000000107012511671246017510 0ustar mariomario#!/usr/bin/env perl ## ## usage: tbray_baseline2.pl < logfile ## ## This runs faster than baseline1 -- regex optimization is in effect here ## whereas not the case in baseline1. ## use strict; use warnings; use Time::HiRes qw(time); my $start = time; my $rx = qr|GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+) |; my %count; while (<>) { next unless $_ =~ /$rx/o; $count{$1}++; } my $end = time; print "$count{$_}\t$_\n" for ( sort { $count{$b} <=> $count{$a} } keys %count )[ 0 .. 9 ]; printf "\n## Compute time: %0.03f\n\n", $end - $start; MCE-1.608/examples/tbray/wf_mce2.pl0000644000076400007640000000253012511671246015767 0ustar mariomario#!/usr/bin/env perl -s ## ## Part 2 of 3 with slurpio => 1 (omitted user_begin & user_end). ## ## usage: ## perl -s wf_mce2.pl -J=$N -C=$N $LOGFILE ## ## where $N is the number of processes, $C is the chunk size, ## and $LOGFILE is the target ## ## defaults: -J=8 -C=2000000 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use Time::HiRes qw(time); use MCE; our $C ||= 2000000; our $J ||= 8; my $logfile = shift; my %count = (); ## Callback function for aggregating total counted. sub store_result { my $count_ref = shift; $count{$_} += $count_ref->{$_} for (keys %$count_ref); return; } ## Parallelize via MCE. my $start = time; my $mce = MCE->new( chunk_size => $C, max_workers => $J, input_data => $logfile, use_slurpio => 1, user_func => sub { my ($self, $chunk_ref, $chunk_id) = @_; my $rx = qr{GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+) }; my %count = (); $count{$1}++ while ( $$chunk_ref =~ /$rx/go ); $self->do('store_result', \%count); } ); $mce->run; my $end = time; ## Display the top 10 hits. print "$count{$_}\t$_\n" for (sort { $count{$b} <=> $count{$a} } keys %count)[ 0 .. 9 ]; printf "\n## Compute time: %0.03f\n\n", $end - $start; MCE-1.608/examples/tbray/wf_mmap.pl0000644000076400007640000000365512511671246016104 0ustar mariomario#!/usr/bin/env perl -s ## ## wf.pl -- an implementation of the "wide finder" benchmark. ## Sean O'Rourke, 2007, public domain. ## ## Usage: perl -s wf.pl -J=$N $LOGFILE ## where $N is the number of processes, and $LOGFILE is the target. ## ## This code depends on Sys::Mmap, which is available on CPAN. ## use strict qw(subs refs); use warnings; ## no critic (InputOutput::ProhibitBarewordFileHandles) ## no critic (InputOutput::ProhibitTwoArgOpen) use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use Time::HiRes qw(time); use Sys::Mmap; $J ||= 8; my $file = shift; open IN, '<', $file or die $!; my $str; mmap $str, 0, PROT_READ, MAP_SHARED, IN; my %h; my $n = 0; my $start = time; unless ($J) { ## serial $h{$1}++ while $str =~ m{GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+) }g; } else { local $|=1; ## parallel -- ugh. my $size = -s IN; my $nperj = int(($size + $J - 1) / $J); my @fhs; use Storable qw(store_fd fd_retrieve); for my $i (0..$J-1) { my $pid = open my $fh, "-|"; die unless defined $pid; if ($pid) { push @fhs, $fh; } else { pos($str) = $i ? rindex($str, "\n", $nperj * $i) || 0 : 0; my $end = ($i+1) * $nperj; $h{$1}++ while pos($str) < $end && $str =~ m{GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+) }g; store_fd \%h, \*STDOUT or die "$i can't store!\n"; exit 0; } } for (0..$#fhs) { my $h = fd_retrieve $fhs[$_] or die "I can't load $_\n"; while (my ($k, $v) = each %$h) { $h{$k} += $v; } close $fhs[$_] or warn "$_ exited weirdly."; } } my $end = time; for (sort { $h{$b} <=> $h{$a} } keys %h) { print "$h{$_}\t$_\n"; last if ++$n >= 10; } printf "\n## Compute time: %0.03f\n\n", $end - $start; MCE-1.608/examples/tbray/wf_mce3.pl0000644000076400007640000000272012511671246015771 0ustar mariomario#!/usr/bin/env perl -s ## ## Part 3 of 3 with slurpio => 1. ## ## usage: ## perl -s wf_mce3.pl -J=$N -C=$N $LOGFILE ## ## where $N is the number of processes, $C is the chunk size, ## and $LOGFILE is the target ## ## defaults: -J=8 -C=2000000 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use Time::HiRes qw(time); use MCE; our $C ||= 2000000; our $J ||= 8; my $logfile = shift; my %count = (); ## Callback function for aggregating total counted. sub store_result { my $count_ref = shift; $count{$_} += $count_ref->{$_} for (keys %$count_ref); return; } ## Parallelize via MCE. my $start = time; my $mce = MCE->new( chunk_size => $C, max_workers => $J, input_data => $logfile, use_slurpio => 1, user_begin => sub { my $self = shift; $self->{wk_count} = {}; $self->{wk_rx} = qr{GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+) }; }, user_func => sub { my ($self, $chunk_ref, $chunk_id) = @_; $self->{wk_count}{$1}++ while ( $$chunk_ref =~ /$self->{wk_rx}/go ); }, user_end => sub { my $self = shift; $self->do('store_result', $self->{wk_count}); } ); $mce->run; my $end = time; ## Display the top 10 hits. print "$count{$_}\t$_\n" for (sort { $count{$b} <=> $count{$a} } keys %count)[ 0 .. 9 ]; printf "\n## Compute time: %0.03f\n\n", $end - $start; MCE-1.608/examples/tbray/README0000644000076400007640000000715512511671246015000 0ustar mariomario ## ## The idea to create this directory came from the MooseX-POE module. ## What a great idea. :) ## ## Back in 2007, Tim Bray created the wonderful Wide-Finder site. I came ## across this site 2 months ago when searching for problems to solve using ## MCE. At the time, MCE lacked the slurpio option. MCE wasn't fast enough ## as seen with the first wf_mce1.pl example. Perl allows the possibility to ## slurp an entire file into a scalar. All I needed was an if statement to ## not convert the chunk to an array via the use_slurpio option. ## ## http://www.tbray.org/ongoing/When/200x/2007/09/20/Wide-Finder ## http://www.tbray.org/ongoing/When/200x/2007/10/30/WF-Results ## http://www.tbray.org/ongoing/When/200x/2007/11/12/WF-Conclusions ## ## It requires the data at http://www.tbray.org/tmp/o10k.ap ## To create o1000k.ap, take o10k.ap and concatenate it 99 more times. ## ## Scripts are normalized to use Time::HiRes for computing time to run. ## tbray_baseline1.pl Baseline script for Perl. Regex optimization is not working as expected. tbray_baseline2.pl Regex optimization is now working as expected. wf_mce1.pl MCE by default passes a reference to an array containing the chunk data. wf_mce2.pl Enabling slurpio causes MCE to pass the reference of the scalar containing the raw chunk data. Essentially, MCE does not convert the chunk to an array. That is the only difference between slurpio => 0 (default) and slurpio => 1. wf_mce3.pl Count data is sent once to the main process by each worker. wf_mmap.pl Code from Sean O'Rourke, 2007, public domain. Modified to default to 8 workers if -J is not specified. ## ## Times below are reported in number of seconds to compute. ## ## Benchmarked under Linux -- CentOS 6.2 (RHEL6), Perl 5.10.1 ## Hardware: 2.0 GHz (4 Cores -- 8 logical processors), 7200 RPM Disk ## Scripts wf_mce1/2/3 and wf_mmap are benchmarked with -J=8. ## Log file tested was o1000k.ap (1 million rows). ## ## Cold cache -- sync; echo 3 >/proc/sys/vm/drop_caches ## Warm cache -- log file is read from FS cache ## Script....: baseline1 baseline2 wf_mce1 wf_mce2 wf_mce3 wf_mmap Cold cache: 1.674 1.370 1.252 1.182 1.174 3.056 Warm cache: 1.236 0.923 0.277 0.106 0.098 0.092 MCE is on the heals of MMAP IO performance levels. MCE performs sequential IO (only a single worker reads at any given time). For MMAP IO, many workers are reading simultaneously (essentially random IO), which is not noticeable when reading from FS cache. MMAP IO is seen wanting nearly 3x the time when reading directly from disk. That came as a surprise to me actually. The result helps clarify a decision I made with MCE. Sequential IO is always thought to be the fastest out there in various benchmark reviews (even SSDs). Therefore, I designed MCE to follow a bank-teller queuing model when reading input data. ## ## Q. Why does MCE follow a bank-teller queuing model for input data? ## The main reason was for maximizing on all available cores from start to end. In essence, a core should begin to go idle towards the end of the job such as reaching the EOF. A worker requiring 1.5x the time to process a given chunk should not impact other workers processing other chunks. ## ## Q. Why chunking? ## The biggest reason for chunking is to reduce the overhead as in the number of trips between workers and the main process. Hence, less IPC. Chunking also helps enable the power-of-randomness. There's a less chance for NFS to choke when workers acquire enough input data to last 10 ~ 15 minutes of compute time. MCE-1.608/examples/tbray/wf_mce1.pl0000644000076400007640000000301112511671246015761 0ustar mariomario#!/usr/bin/env perl -s ## ## Parallizing baseline code via MCE -- Part 1 of 3. ## ## usage: ## perl -s wf_mce1.pl -J=$N -C=$N $LOGFILE ## ## where $N is the number of processes, $C is the chunk size, ## and $LOGFILE is the target ## ## defaults: -J=8 -C=200000 ## use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../../lib'; use Time::HiRes qw(time); use MCE; our $C ||= 200000; our $J ||= 8; my $logfile = shift; my %count = (); ## Callback function for aggregating total counted. sub store_result { my $count_ref = shift; $count{$_} += $count_ref->{$_} for (keys %$count_ref); return; } ## Parallelize via MCE. my $start = time; my $mce = MCE->new( chunk_size => $C, max_workers => $J, input_data => $logfile, user_begin => sub { my $self = shift; $self->{wk_count} = {}; $self->{wk_rx} = qr{GET /ongoing/When/\d\d\dx/(\d\d\d\d/\d\d/\d\d/[^ .]+) }; }, user_func => sub { my ($self, $chunk_ref, $chunk_id) = @_; my $rx = $self->{wk_rx}; for ( @$chunk_ref ) { next unless $_ =~ /$rx/o; $self->{wk_count}{$1}++; } }, user_end => sub { my $self = shift; $self->do('store_result', $self->{wk_count}); } ); $mce->run; my $end = time; ## Display the top 10 hits. print "$count{$_}\t$_\n" for (sort { $count{$b} <=> $count{$a} } keys %count)[ 0 .. 9 ]; printf "\n## Compute time: %0.03f\n\n", $end - $start; MCE-1.608/examples/egrep.pl0000755000076400007640000004161312511671246014436 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## Egrep script (Perl implementation) similar to the egrep binary. ## Look at bin/mce_grep for a wrapper script around the grep binary. ## ## This script supports egrep's options [ceHhiLlmnqRrsv]. The focus is ## demonstrating Many-Core Engine for Perl. Use this script against large ## file(s). ## ## This script was created to show how output order can be preserved even ## though there are only 4 shared socket pairs in MCE no matter the number ## of workers. ## ## Which to choose (examples/egrep.pl or bin/mce_grep). ## ## Examples/egrep.pl is a pure Perl implementation with fewer options. ## Bin/mce_grep is a wrapper script for the relevant binary. ## ## The wrapper script is good for expensive pattern matching -- especially ## for agrep and tre-agrep. It also supports more options due to being ## passed to the binary. The wrapper supports 2 levels of chunking via the ## --chunk-level={auto|file|list} option. For large files, choose file. ## ## The usage description was largely ripped off from the egrep man page. ## ############################################################################### use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my ($prog_name, $prog_dir); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $ENV{PATH} .= ($^O eq 'MSWin32' ? ';' : ':') . $prog_dir; } sub INIT { ## Provide file globbing support under Windows similar to Unix. @ARGV = <@ARGV> if ($^O eq 'MSWin32'); } use Scalar::Util qw( looks_like_number ); use MCE; ############################################################################### ## ---------------------------------------------------------------------------- ## Display usage and exit. ## ############################################################################### sub usage { my ($exit_status) = @_; $exit_status = 0 unless defined $exit_status; print <<"::_USAGE_BLOCK_END_::"; Options for Many-Core Engine: --max-workers=NUM override max workers (default auto) e.g. auto, auto-2, 4 --chunk-size=NUM[KM] override chunk size (default 4M) minimum: 200K; maximum: 20M Usage: $prog_name [OPTION]... PATTERN [FILE] ... Search for PATTERN in each FILE or standard input. Example: $prog_name -i 'hello world' menu.h main.c Regexp selection and interpretation: -e, --regexp=PATTERN use PATTERN as a regular expression -i, --ignore-case ignore case distinctions Miscellaneous: -s, --no-messages suppress error messages -v, --invert-match select non-matching lines --help display this help and exit Output control: -m, --max-count=NUM stop after NUM matches -n, --line-number print line number with output lines -H, --with-filename print the filename for each match -h, --no-filename suppress the prefixing filename on output -q, --quiet, --silent suppress all normal output -R, -r, --recursive equivalent to --directories=recurse --include=PATTERN files that match PATTERN will be examined --exclude=PATTERN files that match PATTERN will be skipped --exclude-from=FILE files that match PATTERN in FILE will be skipped --exclude-dir=PATTERN directories that match PATTERN will be skipped requires a recent egrep binary for --exclude-dir -L, --files-without-match only print FILE names containing no match -l, --files-with-matches only print FILE names containing matches -c, --count only print a count of matching lines per FILE With no FILE, or when FILE is -, read standard input. If less than two FILEs given, assume -h. Exit status is 0 if match, 1 if no match, and 2 if trouble. ::_USAGE_BLOCK_END_:: exit $exit_status; } ############################################################################### ## ---------------------------------------------------------------------------- ## Define defaults and process command-line arguments. ## ############################################################################### my $flag = sub { 1 }; my $isOk = sub { (@ARGV == 0 or $ARGV[0] =~ /^-/) ? usage(1) : shift @ARGV; }; my ($c_flag, $H_flag, $h_flag, $i_flag, $n_flag, $q_flag, $r_flag, $v_flag); my (@r_patn, $arg, @files, @patterns, $re, $skip_args, $w_filename); my ($L_flag, $l_flag, $f_list); my $max_workers = 'auto'; my $chunk_size = 4194304; ## 4M my $max_count = 0; my $no_msg = 0; ## Option parsing step 1. while ( @ARGV ) { $arg = shift @ARGV; $arg =~ s/ /\\ /g; if ($skip_args) { push @files, $arg; next; } if (substr($arg, 0, 2) eq '--') { ## --OPTION $skip_args = $flag->() and next if ($arg eq '--'); $no_msg = $flag->() and next if ($arg eq '--no-messages'); $c_flag = $flag->() and next if ($arg eq '--count'); $i_flag = $flag->() and next if ($arg eq '--ignore-case'); $L_flag = $flag->() and next if ($arg eq '--files-without-match'); $l_flag = $flag->() and next if ($arg eq '--files-with-match'); $n_flag = $flag->() and next if ($arg eq '--line-number'); $q_flag = $flag->() and next if ($arg eq '--quiet'); $q_flag = $flag->() and next if ($arg eq '--silent'); $r_flag = $flag->() and next if ($arg eq '--recursive'); $v_flag = $flag->() and next if ($arg eq '--invert-match'); if ($arg eq '--help') { usage(0); } if ($arg eq '^--regexp=(.+)') { push @patterns, $1; next; } if ($arg =~ m/^--include=.+/) { push @r_patn, $arg; next; } if ($arg =~ m/^--exclude=.+/) { push @r_patn, $arg; next; } if ($arg =~ m/^--exclude-from=.+/) { push @r_patn, $arg; next; } if ($arg =~ m/^--exclude-dir=.+/) { push @r_patn, $arg; next; } if ($arg eq '--with-filename') { $H_flag = 1; $h_flag = 0; next; } if ($arg eq '--no-filename') { $H_flag = 0; $h_flag = 1; next; } $max_count = $isOk->() and next if ($arg =~ /^--max-count$/); $max_workers = $isOk->() and next if ($arg =~ /^--max[-_]workers$/); $chunk_size = $isOk->() and next if ($arg =~ /^--chunk[-_]size$/); if ($arg =~ /^--max-count=(.+)/) { $max_count = $1; next; } if ($arg =~ /^--max[-_]workers=(.+)/) { $max_workers = $1; next; } if ($arg =~ /^--chunk[-_]size=(.+)/) { $chunk_size = $1; next; } usage(2); } elsif (substr($arg, 0, 1) eq '-') { ## -OPTION if ($arg eq '-') { push @files, $arg; next; } if ($arg =~ m/^-([cHhiLlmnqRrsv]+)$/) { my $t_arg = reverse $1; while ($t_arg) { my $a = chop($t_arg); $no_msg = $flag->() and next if ($a eq 's'); $c_flag = $flag->() and next if ($a eq 'c'); $i_flag = $flag->() and next if ($a eq 'i'); $n_flag = $flag->() and next if ($a eq 'n'); $q_flag = $flag->() and next if ($a eq 'q'); $r_flag = $flag->() and next if ($a eq 'R'); $r_flag = $flag->() and next if ($a eq 'r'); $v_flag = $flag->() and next if ($a eq 'v'); if ($a eq 'H') { $H_flag = 1; $h_flag = 0; } elsif ($a eq 'h') { $H_flag = 0; $h_flag = 1; } elsif ($a eq 'L') { $L_flag = 1; $l_flag = 0; } elsif ($a eq 'l') { $L_flag = 0; $l_flag = 1; } elsif ($a eq 'm') { if (substr($arg, -1) eq 'm') { $max_count = shift @ARGV; } elsif ($arg =~ /m(\d+)$/) { $max_count = $1; } } } next; } if ($arg eq '-e') { my $pattern = shift; push @patterns, $pattern if (defined $pattern); next; } usage(2); } push @files, $arg; ## FILE } ## Option parsing step 2. { if (defined $max_count) { unless (looks_like_number($max_count) && $max_count >= 0) { print {*STDERR} "$prog_name: invalid max count\n"; exit 2; } } if ($max_workers !~ /^auto/) { unless (looks_like_number($max_workers) && $max_workers > 0) { print {*STDERR} "$prog_name: invalid max workers\n"; exit 2; } } if ($chunk_size =~ /^(\d+)K/i) { $chunk_size = $1 * 1024; } elsif ($chunk_size =~ /^(\d+)M/i) { $chunk_size = $1 * 1024 * 1024; } if (looks_like_number($chunk_size) && $chunk_size > 0) { $chunk_size = 20_971_520 if $chunk_size > 20_971_520; ## 20M $chunk_size = 204_800 if $chunk_size < 204_800; ## 200K } else { print {*STDERR} "$prog_name: invalid chunk size\n"; exit 2; } } ## Option parsing step 3. $f_list = ($L_flag || $l_flag); push @patterns, shift @files if (@patterns == 0 && @files > 0); $w_filename = 1 if ((!$h_flag && @files > 1) || (!$h_flag && $r_flag) || $H_flag); usage(2) if (@patterns == 0); if (@patterns > 1) { $re = '(?:' . join('|', @patterns) . ')'; } else { $re = $patterns[0]; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE callback functions. ## ############################################################################### my ($file, %result, $abort_all, $abort_job, $found_match); my $exit_status = 0; my $total_found = 0; my $total_lines = 0; my $order_id = 1; keys(%result) = 4000; sub aggregate_count { my ($wk_count) = @_; $total_found += $wk_count; $found_match = 1 if ($total_found); return; } sub display_result { my ($chunk_id, $result) = @_; return if ($abort_job); $result{$chunk_id} = $result; while (1) { last unless exists $result{$order_id}; my $r = $result{$order_id}; if (!$abort_job && $r->{found_match}) { $found_match = 1; if ($q_flag) { MCE->abort; $abort_all = $abort_job = 1; last; } for my $i (0 .. @{ $r->{matches} } - 1) { $total_found++; unless ($c_flag) { printf "%s:", $file if ($w_filename); printf "%d:", $r->{lines}[$i] + $total_lines if ($n_flag); print $r->{matches}[$i]; } if ($max_count && $max_count == $total_found) { MCE->abort; $abort_job = 1; last; } } } $total_lines += $r->{line_count} if ($n_flag); delete $result{$order_id++}; } return; } sub report_match { if (!$abort_job) { MCE->abort; $abort_all = 1 if $q_flag; $abort_job = $total_found = 1; } return; } ############################################################################### ## ---------------------------------------------------------------------------- ## MCE user functions. ## ############################################################################### sub user_begin { my ($mce) = @_; if ($c_flag) { use vars qw($match_re $eol_re $count); our $match_re = $re . '.*' . $/; our $eol_re = $/; our $count = 0; } return; } sub user_end { my ($mce) = @_; if ($c_flag) { MCE->do('aggregate_count', $count) if ($count); } return; } sub user_func { my ($mce, $chunk_ref, $chunk_id) = @_; my ($found_match, @matches, $line_count, @lines); ## Count and return immediately if -c was specified. if ($c_flag && !$f_list) { my $match_count = 0; if ($i_flag) { $match_count++ while ( $$chunk_ref =~ /$match_re/img ); } else { $match_count++ while ( $$chunk_ref =~ /$match_re/mg ); } if ($v_flag) { unless ($eol_re eq "\n") { $line_count = 0; $line_count++ while ( $$chunk_ref =~ /$eol_re/g ); } else { $line_count = ( $$chunk_ref =~ tr/\n// ); } $count += $line_count - $match_count; } else { $count += $match_count; } return; } ## Quickly determine if a match is found. if (!$v_flag || $f_list) { for (0 .. @patterns - 1) { if ($i_flag) { if ($$chunk_ref =~ /$patterns[$_]/im) { $found_match = 1; last; } } else { if ($$chunk_ref =~ /$patterns[$_]/m) { $found_match = 1; last; } } } } if ($f_list) { MCE->do('report_match') if (($l_flag && $found_match) || ($L_flag && !$found_match)); return; } ## Obtain file handle to slurped data. ## Collect matched data if slurped chunk data contains a match. open my $_MEM_FH, '<', $chunk_ref; binmode $_MEM_FH, ':raw'; if (!$v_flag && !$found_match) { if ($n_flag) { 1 while (<$_MEM_FH>); } } else { if ($v_flag) { if ($i_flag) { while (<$_MEM_FH>) { if ($_ !~ /$re/i) { push @matches, $_; push @lines, $. if ($n_flag); } } } else { while (<$_MEM_FH>) { if ($_ !~ /$re/) { push @matches, $_; push @lines, $. if ($n_flag); } } } } else { if ($i_flag) { while (<$_MEM_FH>) { if ($_ =~ /$re/i) { push @matches, $_; push @lines, $. if ($n_flag); } } } else { while (<$_MEM_FH>) { if ($_ =~ /$re/) { push @matches, $_; push @lines, $. if ($n_flag); } } } } } $line_count = $.; close $_MEM_FH; ## Send results to the manager process. my %result = ( 'found_match' => scalar @matches, 'line_count' => $line_count, 'matches' => \@matches, 'lines' => \@lines ); MCE->do('display_result', $chunk_id, \%result); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Process routines. ## ############################################################################### sub display_matched { if (!$q_flag && $f_list) { print "$file\n" if $total_found; } elsif (!$q_flag && $c_flag) { printf "%s:", $file if $w_filename; print "$total_found\n"; } $total_found = $total_lines = 0; $abort_job = undef; $order_id = 1; return; } sub process_file { ($file) = @_; if ($file eq '-') { open(STDIN, '<', ($^O eq 'MSWin32') ? 'CON' : '/dev/tty') or die $!; process_stdin(); } elsif (! -e $file) { $exit_status = 2; print {*STDERR} "$prog_name: $file: No such file or directory\n" unless $no_msg; } elsif (-d $file) { $exit_status = 1; } else { MCE->process($file); display_matched(); } return; } sub process_stdin { $file = "(standard input)"; MCE->process(\*STDIN); display_matched(); return; } ############################################################################### ## ---------------------------------------------------------------------------- ## Run. ## ############################################################################### MCE->new( max_workers => $max_workers, chunk_size => $chunk_size, use_slurpio => 1, user_begin => \&user_begin, user_func => \&user_func, user_end => \&user_end ); if ($r_flag && @files > 0) { my ($list_fh, $list); MCE->spawn; if ($^O eq 'MSWin32') { $list = `egrep -lsr @r_patn ^ @files`; open $list_fh, '<', \$list; } else { open $list_fh, '-|', 'egrep', '-lsr', @r_patn, '^', @files; } while (<$list_fh>) { chomp; process_file($_); last if $abort_all; } close $list_fh; } elsif (@files > 0) { foreach (@files) { process_file($_); last if $abort_all; } } else { process_stdin(); } ############################################################################### ## ---------------------------------------------------------------------------- ## Finish. ## ############################################################################### MCE->shutdown; if (!$q_flag && $exit_status) { exit($exit_status); } else { exit($found_match ? 0 : ($exit_status ? $exit_status : 1)); } MCE-1.608/examples/step_demo.pl0000755000076400007640000000311712511671246015310 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; use MCE::Step; ## In the demonstration below, one may call ->gather or ->step any number ## of times although ->step is not allowed in the last sub-block. Data is ## gathered to @arr which may likely be out-of-order. Gathering data is ## optional. All sub-blocks receive $mce as the first argument. ## First, defining 3 sub-tasks. sub task_a { my ($mce, $chunk_ref, $chunk_id) = @_; if ($_ % 2 == 0) { MCE->gather($_); # MCE->gather($_ * 4); ## Ok to gather multiple times } else { MCE->print("a step: $_, $_ * $_\n"); MCE->step($_, $_ * $_); # MCE->step($_, $_ * 4 ); ## Ok to step multiple times } } sub task_b { my ($mce, $arg1, $arg2) = @_; MCE->print("b args: $arg1, $arg2\n"); if ($_ % 3 == 0) { ## $_ is the same as $arg1 MCE->gather($_); } else { MCE->print("b step: $_ * $_\n"); MCE->step($_ * $_); } } sub task_c { my ($mce, $arg1) = @_; MCE->print("c: $_\n"); MCE->gather($_); } ## Next, pass MCE options, using chunk_size 1, and run all 3 tasks ## in parallel. Notice how max_workers can take an anonymous array, ## similarly to task_name. my @arr = mce_step { task_name => [ 'a', 'b', 'c' ], max_workers => [ 2, 2, 2 ], chunk_size => 1 }, \&task_a, \&task_b, \&task_c, 1..10; ## Finally, sort the array and display its contents. @arr = sort { $a <=> $b } @arr; print "\n@arr\n\n"; MCE-1.608/examples/pipe2.pl0000755000076400007640000002316512511671246014355 0ustar mariomario#!/usr/bin/env perl ############################################################################### ## ---------------------------------------------------------------------------- ## Process STDIN or FILE via an external command in parallel. ## ## This is by no means a complete script, but rather a "how-to" for folks ## wanting to create their own parallel script. ## ## For UNIX, the Perl interpretor does "not" sub-shell when running command. ## ############################################################################### use strict; use warnings; ## no critic (InputOutput::ProhibitBarewordFileHandles) ## no critic (InputOutput::ProhibitTwoArgOpen) use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; my ($prog_name, $prog_dir); BEGIN { $prog_name = $0; $prog_name =~ s{^.*[\\/]}{}g; $prog_dir = abs_path($0); $prog_dir =~ s{[\\/][^\\/]*$}{}; $ENV{PATH} = $prog_dir .($^O eq 'MSWin32' ? ';' : ':'). $ENV{PATH}; } use Getopt::Long qw( :config bundling pass_through no_ignore_case no_auto_abbrev ); use Scalar::Util qw( looks_like_number ); use Fcntl qw( O_RDONLY ); use MCE::Signal qw( -use_dev_shm ); use MCE::Loop; ############################################################################### ## ---------------------------------------------------------------------------- ## Display usage and exit. ## ############################################################################### sub usage { print {*STDERR} <<"::_USAGE_BLOCK_END_::"; NAME $prog_name -- process STDIN or FILE via an external command in parallel SYNOPSIS $prog_name [script_options] command [command_options] [FILE] DESCRIPTION The $prog_name script processes STDIN or FILE in parallel. STDIN is read unless FILE is specified. Specifing more than 1 file will error. The following options are available: --RS RECORD_SEPARATOR Input record separator -- default: newline --abort-on-err Notify MCE to abort if command exited with a non-zero exit status. Workers will stop taking new work causing MCE to terminate early. --chunk-size CHUNK_SIZE Specify chunk size for MCE -- default: auto Can also take a suffix; K/k (kilobytes) or M/m (megabytes). Less than or equal to 8192 is the number of records. Greater than 8192 is the number of bytes. The maximum is 24m by MCE internally. --max-workers MAX_WORKERS Specify number of workers for MCE -- default: 8 --parallel-io Enable parallel IO for FILE. This is not recommended if running on several nodes simultaneously and reading from the same shared storage. EXIT STATUS $prog_name exits 0 on success, and >0 if an error occurs. EXAMPLES The "command" receives data from STDIN. Notice the map and reduce idiom for wc -l below. Process STDIN (workers request the next chunk from the manager process). cat infile | $prog_name --chunk-size=2k cat >out 2>err $prog_name --chunk-size=2k cat < infile >out 2>err $prog_name --chunk-size=8m wc -l < largefile | \ awk '{ lines = lines + \$1 } END { print lines }' Process FILE (workers communicate the next offset among themselves). $prog_name --chunk-size=2k cat infile >out 2>err $prog_name --parallel-io --chunk-size=8m wc -l largefile | \ awk '{ lines = lines + \$1 } END { print lines }' $prog_name --chunk-size=8m wc -l largefile | \ awk '{ lines = lines + \$1 } END { print lines }' ::_USAGE_BLOCK_END_:: exit 1; } ############################################################################### ## ---------------------------------------------------------------------------- ## Define defaults and process command-line arguments. ## ############################################################################### my $RS = $/; my $abort_on_err = 0; my $chunk_size = 'auto'; my $max_workers = 8; my $parallel_io = 0; { local $SIG{__WARN__} = sub { }; GetOptions( 'RS=s' => \$RS, 'abort-on-err|abort_on_err' => \$abort_on_err, 'chunk-size|chunk_size=s' => \$chunk_size, 'max-workers|max_workers=s' => \$max_workers, 'parallel-io|parallel_io' => \$parallel_io ); if ($max_workers !~ /^auto/) { unless (looks_like_number($max_workers) && $max_workers > 0) { print {*STDERR} "$prog_name: $max_workers: invalid max workers\n"; exit 2; } } if ($chunk_size !~ /^auto/) { if ($chunk_size =~ /^(\d+)K/i) { $chunk_size = $1 * 1024; } elsif ($chunk_size =~ /^(\d+)M/i) { $chunk_size = $1 * 1024 * 1024; } if (!looks_like_number($chunk_size) || $chunk_size < 1) { print {*STDERR} "$prog_name: $chunk_size: invalid chunk size\n"; exit 2; } } } usage() unless @ARGV; usage() if $ARGV[0] =~ /^-/; ############################################################################### ## ---------------------------------------------------------------------------- ## Exit if "command" is not found. Determine input stream. ## ############################################################################### my $is_mswin32 = $^O eq 'MSWin32'; my ($cmd_name, $cmd_path); $cmd_name = shift @ARGV; { my $pth_sep = $is_mswin32 ? ";" : ":"; my $dir_sep = $is_mswin32 ? "\\" : "/"; for ( split ${pth_sep}, $ENV{'PATH'} ) { if (-e "$_${dir_sep}${cmd_name}" || -e "$_${dir_sep}${cmd_name}.exe") { $cmd_path = "$_${dir_sep}${cmd_name}"; last; } } } if (! defined $cmd_path) { print {*STDERR} "$prog_name: $cmd_name: command not found\n"; exit 2; } if (! $is_mswin32 && ! -x $cmd_path) { print {*STDERR} "$prog_name: $cmd_name: command is not executable\n"; exit 2; } my $input = \*STDIN; if (defined $ARGV[-1] && $ARGV[-1] !~ /^-/) { usage() if (defined $ARGV[-2] && $ARGV[-2] !~ /^-/); $input = pop(@ARGV); } if (ref $input eq '') { if (! -e $input) { print {*STDERR} "$prog_name: $input: No such file or directory\n"; exit 2; } if (-d $input) { print {*STDERR} "$prog_name: $input: Is a directory\n"; exit 2; } } ############################################################################### ## ---------------------------------------------------------------------------- ## Output function. Define the gather iterator for preserving output order. ## ############################################################################### my $buf = sprintf('%65536s', ''); ## Create a continuous buffer for the my $exit_status = 0; ## output routine. sub output { my ($file, $sendto_fh) = @_; my ($fh, $n_read); if (-s $file) { sysopen($fh, $file, O_RDONLY); while (1) { $n_read = sysread($fh, $buf, 65536); last if $n_read == 0; syswrite($sendto_fh, $buf, $n_read); } close $fh; } unlink $file; return; } sub gather_iterator { my ($out_fh, $err_fh) = @_; my %tmp; my $order_id = 1; return sub { my ($chunk_id, $path, $status) = @_; $tmp{$chunk_id} = $path; $exit_status = $status if ($status > $exit_status); MCE->abort if ($abort_on_err && $status); while (1) { last unless exists $tmp{$order_id}; $path = delete $tmp{$order_id++}; output("$path.err", $err_fh); output("$path.out", $out_fh); } }; } ############################################################################### ## ---------------------------------------------------------------------------- ## Configure MCE. Process STDIN in parallel afterwards. The mce_loop_f routine ## can take a GLOB reference or a scalar containing the path to the file. ## ############################################################################### MCE::Loop::init { RS => $RS, use_slurpio => 1, parallel_io => $parallel_io, chunk_size => $chunk_size, max_workers => $max_workers, gather => gather_iterator(\*STDOUT, \*STDERR) }; mce_loop_f { my ($mce, $chunk_ref, $chunk_id) = @_; my $path = MCE->tmp_dir .'/'. $chunk_id; local ($!, $?); if ($is_mswin32) { $path =~ s{/}{\\\\}g; open my $out_fh, "+>:raw", "$path.in"; print $out_fh $$chunk_ref; close $out_fh; system("$cmd_path @ARGV < $path.in > $path.out 2> $path.err"); unlink "$path.in"; } else { ## Borrowed bits from IPC::Run3 for STDOUT/ERR. For STDIN, went with ## open $cmd_fh, '|-', ... due to lesser overhead behind the scene. local (*STDOUT_SAVE, *STDERR_SAVE); my ($out_fh, $err_fh, $cmd_fh); open STDOUT_SAVE, '>&STDOUT'; open $out_fh, '+>:raw', "$path.out"; open STDOUT, '>&' . fileno $out_fh; open STDERR_SAVE, '>&STDERR'; open $err_fh, '+>:raw', "$path.err"; open STDERR, '>&' . fileno $err_fh; ## Seeing "maximal count of pending signals (NUM) exceeded" message. ## Therefore using syswrite instead of print below. open $cmd_fh, '|-', $cmd_path, @ARGV; ## Spawn external command syswrite $cmd_fh, $$chunk_ref; ## Write to external's STDIN close $cmd_fh; open STDOUT, '>&STDOUT_SAVE'; close $out_fh; open STDERR, '>&STDERR_SAVE'; close $err_fh; } MCE->gather($chunk_id, $path, $? >> 8); } $input; ############################################################################### ## ---------------------------------------------------------------------------- ## Cleanup and exit. ## ############################################################################### MCE::Loop::finish; exit $exit_status; MCE-1.608/examples/files_flow.pl0000755000076400007640000000304712511671246015464 0ustar mariomario#!/usr/bin/env perl use strict; use warnings; use Cwd 'abs_path'; ## Insert lib-path at the head of @INC. use lib abs_path($0 =~ m{^(.*)[\\/]} && $1 || abs_path) . '/../lib'; ## Same logic as in files_mce.pl, but with the MCE::Flow model. ## usage: ./files_flow.pl [ startdir [0|1] ] use Time::HiRes 'sleep'; use MCE::Flow; use MCE::Queue; my $D = MCE::Queue->new(queue => [ $ARGV[0] || '.' ]); my $F = MCE::Queue->new(fast => defined $ARGV[1] ? $ARGV[1] : 1); my $providers = 3; my $consumers = 8; MCE::Flow::init { task_end => sub { my ($mce, $task_id, $task_name) = @_; $F->enqueue((undef) x $consumers) if $task_name eq 'dir'; } }; ## Override any MCE options and run. Notice how max_workers and ## task_name take an anonymous array to configure both tasks. mce_flow { max_workers => [ $providers, $consumers ], task_name => [ 'dir', 'file' ] }, sub { ## Dir Task. Allow time for wid 1 to enqueue any dir entries. ## Otherwise, workers (wid 2+) may terminate early. sleep 0.1 if MCE->task_wid > 1; while (defined (my $dir = $D->dequeue_nb)) { my (@files, @dirs); foreach (glob("$dir/*")) { if (-d $_) { push @dirs, $_; next; } push @files, $_; } $D->enqueue(@dirs ) if scalar @dirs; $F->enqueue(@files) if scalar @files; } }, sub { ## File Task. while (defined (my $file = $F->dequeue)) { MCE->say($file); } }; ## Workers persist in models. This may be ommitted. It will run ## automatically during exiting if not already called. MCE::Flow::finish; MCE-1.608/LICENSE0000644000076400007640000004366112511671246012170 0ustar mariomarioThis software is copyright (c) 2012-2015 by Mario Roy. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2012-2015 by Mario Roy. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2012-2015 by Mario Roy. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End MCE-1.608/README0000644000076400007640000001212412511672532012030 0ustar mariomario ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### This document describes MCE version 1.608. Many-Core Engine (MCE) for Perl helps enable a new level of performance by maximizing all available cores. MCE spawns a pool of workers and therefore does not fork a new process per each element of data. Instead, MCE follows a bank queuing model. Imagine the line being the data and bank-tellers the parallel workers. MCE enhances that model by adding the ability to chunk the next n elements from the input stream to the next available worker. MCE uses domain sockets for IPC versus pipes. One day, a physical box will have hundreds of cores. MCE can spawn 960 workers (via fork) for ulimit re- porting 1024 allowed for max user processes. Other parallel implementations using pipes cannot do this (only about half of ulimit -u). Chunking in MCE. Input data is optional in MCE. Thus, input data is not required to run MCE. MCE iterates over input data in chunks. Workers can be spawned prior to creating or obtaining data. MCE is a chunking engine. It does not divide the input equally by the number of workers. Instead, it chunks from start till end. Imagine input data as a highway. Now think of MCE (automobile) on the highway. Basically, data can be larger than physical memory allows. The chunking nature of MCE makes this possible. input_data => '/path/to/file', ## process a file in parallel input_data => \@array, ## process an array in parallel input_data => \*FileHandle, ## process a filehandle in parallel input_data => \$scalar, ## treated like a memory-based file input_data => \&iterator, ## allows for custom input iterator MCE can iterate over a sequence of numbers mathematically if all you need is a numeric iterator. Check out forseq.pl and seq_demo.pl. sequence => { begin => 1, end => 100, step => 2 }, chunk_size => 20, ## Worker receives the next 20 sequences Use Cases for MCE. 1. Perl is not compiled with threads support and wanting something similar to Thread::Queue. Check out MCE::Flow and MCE::Queue. 2. Wanting multiple roles and each role uniquely configured with number of workers and function to execute. Check out flow_demo.pl and step_demo.pl. 3. Wanting to process rows from a database in parallel. Check out sampledb under the examples directory. 4. MCE can be used to process a single (large) file or process many (small) files in parallel. Take bin/mce_grep, a wrapper around the grep binary, for a test drive. MCE also includes egrep.pl for a pure Perl grep implementation. 5. A large telecom company wanting to poll millions of devices via SNMP. This is possible with MCE, AnyEvent::SNMP, and Net::SNMP. Think of having 200 workers and each worker connecting to 300 devices with AnyEvent. Each worker processes 300 devices simultaneously. 6. Wanting to access many hosts in parallel via SSH or TCP/IP. ... ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### INSTALLATION To install this module type the following: MCE_INSTALL_TOOLS=1 perl Makefile.PL (e.g. bin/mce_grep) (or) perl Makefile.PL make make test make install DEPENDENCIES This module requires Perl 5.8.0 or later to run. MCE spawns child processes by default, not threads. However, MCE supports threads via 2 threading libraries if threads is desired. The use of threads in MCE requires that you include threads support prior to loading MCE. The use_threads option defaults to 1 when a thread library is loaded. Threads is loaded automatically for $^O eq 'MSWin32'. use threads; use forks; use threads::shared; (or) use forks::shared; use MCE; use MCE; MCE utilizes the following modules: bytes constant Carp Fcntl File::Path IO::Handle Scalar::Util Socket Storable 2.04+ Symbol Test::More 0.45+ (for testing only via make test) Time::HiRes COPYRIGHT AND LICENCE Copyright (C) 2012-2015 by Mario E. Roy This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See L for more information. USAGE The source is hosted at http://code.google.com/p/many-core-engine-perl/ https://metacpan.org/module/MCE::Signal https://metacpan.org/module/MCE ############################################################################### # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # ############################################################################### Enjoy MCE !!! - Mario