From a5665e6ee1daacd1e692a79be71cb09f6f87edde Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joaqu=C3=ADn=20M=20L=C3=B3pez=20Mu=C3=B1oz?=
+The following people have provided valuable feedback and suggestions during the
+development of the library: Ion Gaztañaga, Janek Kozicki, Tobias Schwinger,
+Pavel Voženílek. Simon Atanasyan contributed a workaround for a
+problem with Sun Studio compilers. Rosa Bernárdez has proofread the
+documentation from the first drafts up to its present form.
+
+The acceptance review of Boost.Flyweight took place between January 21st
+and February 3rd 2008. Many thanks to Ion Gaztañaga, the stalwart review manager,
+and to all the reviewers: Alberto Ganesh Barbati, Tim Blechmann,
+Vicente Juan Botet Escribá, Matías Capeletto, Neil Hunt, Marcus Lindblom,
+John Reid, David Sankel, Kevin Sopp, John Torjo, Markus Werle. Alberto identified
+important limitations of the initial design, which led to the
+introduction of key-value flyweights.
+
+Boost.Flyweight relies on the
+Boost MPL Library from
+Aleksey Gurtovoy. The
+free-order template
+parameter interface offered by the library has been implemented
+with the Boost Parameter
+Library from David Abrahams and Daniel Wallin. Ion Gaztañaga's
+Boost Interprocess Library
+is the core element behind the
+
+This work is dedicated to Jorge López, in the hope that past
+dire straits gentler oceans will lie.
+ Revised December 10th 2008 © Copyright 2006-2008 Joaquín M López Muñoz.
+Distributed under the Boost Software
+License, Version 1.0. (See accompanying file
+LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+
+See source code.
+
+Dummy program showing the basic capabilities of
+See source code.
+
+The program simulates the scenario described at the tutorial section on
+key-value flyweights: The class
+
+See source code.
+
+The composite
+design pattern revolves about the idea that a tree data structure
+can be easily constructed and manipulated by defining the tree node type
+polymorphically so that either is a leaf node or else contains a list of
+pointers to their child nodes.
+This way, a tree is the exact same entity as its root node, which allows
+for very simple recursive tree-handling algorithms. Large composite trees
+having a high degree of duplication of nodes and subtrees (as for instance
+those generated when parsing a computer program) are a natural fit for the
+flyweight idiom: simply turning the node type into a flyweight
+automatically deals with duplication at the node and subtree level.
+
+The example program parses Lisp-like lists of the form
+
+the resulting data structure implicitly detects the duplicated
+occurrences of
+See source code.
+
+A classic example of application of the flyweight pattern is that of a
+text processor which handles characters with rich formatting information,
+like font type, size, color and special options (boldness, italics, etc.)
+Coding the formatting information of each character takes considerable
+space, but, given the high degree of repetition typical in a document,
+maintaining formatted characters as flyweight objects drastically reduces
+memory consumption.
+
+The example program parses, manipulates and stores HTML documents following
+flyweight-based representation techniques. Given the hierarchical nature
+of HTML markup, a crude approximation to the formatting options of a given
+character is just to equate them with the stack of tag contexts to which
+the character belongs, as the figure shows.
+
+
+HTML documents are then parsed as arrays of (character, format)
+pairs, where the format is the tag context as described above. The very high
+degree of redundancy in formatting information is taken care of by the
+use of Boost.Flyweight. This character-based representation makes it
+easy to manipulate the document: transposition and elimination of
+portions of text are trivial operations. As an example, the program
+reverses the text occupying the central portion of the document.
+Saving the result in HTML reduces to traversing the array of formatted
+characters and emitting opening/closing HTML tags as the context of adjacent
+characters varies.
+
+For the sake of brevity, the HTML parsing capabilities of this program
+are coarse: for instance, elements without end-tag (like <BR>), character
+enconding and HTML entities (e.g. "©" for ©) are not properly
+handled. Improving the parsing code is left as an exercise to the reader.
+
+See source code.
+
+Memoization
+is an optimization technique consisting in caching
+the results of a computation for later reuse; this can dramatically
+improve performance when calculating recursive numerical functions,
+for instance. Key-value flyweights
+can be used to implement memoization for a numerical function f
+by modeling a memoized invocation of the function as a value of
+type
+The
+See source code.
+
+This program measures the time and space performances of a simple
+string type against several differently configured
+See source code.
+
+The example shows how to write and use a custom factory class. This
+"verbose" factory outputs messages tracing the invocations of its public interface
+by Boost.Flyweight, so helping the user visualize factory usage patterns.
+ Revised December 2nd 2008 © Copyright 2006-2008 Joaquín M López Muñoz.
+Distributed under the Boost Software
+License, Version 1.0. (See accompanying file
+LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+
+New functionalities can be included into future releases of Boost.Flyweight
+to meet the demands of users and to leverage upcoming C++0x features
+and new Boost libraries. The following is a list of candidate additions.
+
+Currently there is no way to access the internal components of a
+
+When constructing a
+The nature of the flyweight pattern implies that most accesses
+to the internal flyweight factory do not cause new insertions and can
+thus be considered read-only. This hints at the convenience of using
+a locking policy based on read/write locks such as those provided by
+Boost.Thread.
+Implementing a locking policy will also require extending the
+
+Recently accepted Boost libraries like
+Boost.Functional/Forward
+and Boost.Functional/Factory
+might be used in the future to replace some internal machinery of
+Boost.Flyweight.
+ Revised September 1st 2008 © Copyright 2006-2008 Joaquín M López Muñoz.
+Distributed under the Boost Software
+License, Version 1.0. (See accompanying file
+LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+ ab%!L&lkL(L?sXCli+!+qaQ1l>qpu-%bsGPi1=uTn ?yTsAW{%uNzBf2GY^{v+
zJH792+1^g64eAmeY?D6u2v|8@wO7je|5i$Q^Q|?ab41vgr%St6-rV9;B3b!}GjWz%
zTT!ZRp+Cd9a|)~s0xV4z{>#hC?%KIIcy99qk
+Flyweights are small-sized handle classes granting constant access to shared
+common data, thus allowing for the management of large amounts of entities
+within reasonable memory limits. Boost.Flyweight makes it easy to use this
+common programming idiom by providing the class template
+
+Learning to use Boost.Flyweight can be accomplished in a matter of minutes.
+When special needs arise, however, an extensive customization interface
+is provided which allows the user to control and extend the following aspects:
+ Revised September 3rd 2008 © Copyright 2006-2008 Joaquín M López Muñoz.
+Distributed under the Boost Software
+License, Version 1.0. (See accompanying file
+LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+
+We show how to estimate the memory reduction obtained by the usage of
+Boost.Flyweight in a particular scenario and study the impact on the execution
+time for the different functional areas of
+As we saw in the tutorial rationale,
+the flyweight pattern is based on two types of objects:
+
+The only data member of a
+The entries stored in the factory associated to
+For the current implementation of Boost.Flyweight, the following aspects
+contribute to overhead:
+
+
+
+
+
Boost.Flyweight Acknowledgements
+
+
+
+intermodule_holder
+component.
+
+
+
+
+
+
+
+
+
+
+
+
+
Boost.Flyweight Examples
+
+
+
+
+Contents
+
+
+
+
+Example 1: basic usage
+
+flyweight
+explained at the tutorial.
+Example 2: key-value flyweights
+
+texture manages some texture rendering data stored in
+a file whose location is given at construction time. The program
+handles large quantities of objects of this class by encapsulating
+them into key-value flyweights keyed by filename. Observe how the
+execution of the program results in no extra constructions or copies
+of objects of type texture except those absolutely
+necessary.
+Example 3: flyweights and the composite pattern
+
+(a1 ... an) where each
+ai is a terminal string or a list. The parsed
+data structure is a composite type defined using Boost.Flyweight in conjunction
+with the recursive facilities of
+Boost.Variant. So, given the list
+
+
+
+(= (tan (+ x y))(/ (+ (tan x)(tan y))(- 1 (* (tan x)(tan y)))))
+
+, x, y,
+tan, (tan x) and (tan y).
+Example 4: formatted text processing
+
+
+Fig. 1: Formatting contexts of characters in an HTML document.
+Example 5: flyweight-based memoization
+
+flyweight<key_value<int,compute_f> >, where
+compute_f is a type that does the computation of
+f(n) at its compute_f::compute_f(int) constructor.
+For instance, the Fibonacci
+numbers can be computed with memoization like this:
+
+
+
+typedef flyweight<key_value<int,compute_fibonacci>,no_tracking> fibonacci;
+
+struct compute_fibonacci
+{
+ compute_fibonacci(int n):
+ result(n==0?0:n==1?1:fibonacci(n-2).get()+fibonacci(n-1).get())
+ {}
+
+ operator int()const{return result;}
+ int result;
+};
+
no_tracking
+policy is used so that the memoized computations persist for future
+use throughout the program. The provided program develops this example in full.
+Example 6: performance comparison
+
+flyweight
+instantations as used in a conventional task involving parsing a file and
+doing some manipulations on the parsed text.
+Memory consumption is computed by instrumenting the relevant
+components (the string type itself, flyweight factories, etc.) with custom
+allocators that keep track of the allocations and deallocations requested.
+The program has been used to produce the experimental results given
+at the performance section.
+Example 7: custom factory
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Boost.Flyweight Future work
+
+
+
+
+Contents
+
+
+
+
+Introspection API
+
+flyweight instantiation (factory, holder, etc.) or even
+to know the types of these components. With such an API it would be
+possible to instrument and monitor the usage of Boost.Flyweight like in
+the following example:
+
+
+
+typedef flyweight<std::string> fw_type;
+...
+std::cout<<"factory used: "<<typeid(fw_type::factory_type).name()<<std::endl;
+std::cout<<"values stored: "<<fw_type::factory().size()<<std::endl;
+
Perfect forwarding
+
+flyweight<T> object, some spurious copies
+of objects of type T are incurred in the process of moving the value
+into the internal factory. So-called perfect
+forwarding, i.e. performing the move without generating temporary
+copies, will be solved in an optimum manner by a new
+type of rvalue references to be included in the next revision of the
+C++ standard. Boost.Flyweight will take advantage of this feature as
+compilers begin to provide it.
+Read/write locking policy
+
+Factory concept
+to allow for pure lookup operations. Tim Blechmann has provided a
+preliminary implementation
+of this idea. Before committing to this library extension it is
+necessary to do a profiling study to determine whether read/write
+locking actually improves performance.
+Integration with new Boost libraries
+
+
+
+
+
+
+
+
+
+
+iXjaNTfOisP==|AJz7-KdsZ&2_N;)RXLc%cT}-HE*%-
z;;or`E1|3LT%~YvpUH0Dx0^J$_pmN_6H~;%=BTjiC2!UL-DWy!wR1mRp4-}y(Nw~+
z$4{)ttN3=?;u2|-yBWEwLQB|I{&IbLs4ilbRYa@%bSgJ@+>spl
zBE{pKW{C6Ez!^P<-PL&7!UKC|8a!I`K`g}T&N`{tobiD-_)qnJ{
+
+
+
+
Boost Flyweight Library
+
+
+
+flyweight<T>, which acts as a drop-in replacement for
+const T.
+
+
+Contents
+
+
+
+
+
+
+
+
+
+
+
+
+KYmvnwpwgT3XuL+B!Nqy1Kf0dV2c$
z`UVCDhK7blMn=ZQ#wI2vrlzK5W@hH*<`xzfmX?-QR#w*5);2aawzjr*c6Rpm_6`mX
zj*gB_PEO9w&Mq!4uCA_bZf@@G?j9Z
+
+
+
+
Boost.Flyweight Performance
+
+
+
+
+Contents
+
+
+
+
+
+
+
+
+
+
+ Introduction
+
+flyweight.
+Some experimental results are provided.
+Memory consumption
+
+
+
+The overall memory consumption is then a function of the size of the
+flyweight objects, the size of the entry objects and the degree of
+value redundancy.
+Flyweight size
+
+flyweight object is a so-called
+handle, an opaque object of small size provided by the internal
+flyweight factory to refer to the entries it stores. For the default
+hashed_factory,
+this handle is merely a pointer, so sizeof(flyweight<T>)=sizeof(void*),
+4 bytes in typical 32-bit architectures.
+For other types of factories, the handle is an iterator to an internal
+container used in the implementation of the factory: again, its size
+is typically that of a pointer.
+Entry size
+
+flyweight<T,...>
+need not only hold a value of T, but also contain additional
+information related to the internal implementation of
+flyweight<T,...>:
+
+entry =
+
+sizeof(T) + overhead.
+
+
+The table summarizes the separate contributions to overhead introduced
+by the different components taking part of the definition of
+a flyweight instantiation. Values are given in words,
+i.e. the size of a pointer, which is 4 bytes in a typical 32-bit architecture.
+Alignment may introduce additional overhead.
+
+
| component | +overhead (words) | +|
|---|---|---|
key_value |
+ with key extractor | +1(1) | +
| without key extractor | + 1 + sizeof(Key) |
+|
| factory | + hashed_factory |
+ ~2.5 | +
set_factory |
+ 4(2) | +|
assoc_container_factory |
+ depends on the container used | +|
| tracking mechanism | + refcounted |
+ 1(3) | +
no_tracking |
+ 0 | +|
sizeof(Key)<=sizeof(Value).std::set this overhead reduces to 3.
+For instance, for the default configuration parameters of flyweight,
+overhead is typically 2.5(hashed_factory) + 1(refcounted)
+= 3 words.
+
+Consider a scenario where there are N different objects of type T
+jointly taking M different values. The objects consume then
+S = N·T bytes, where T is defined as the
+average size of T (sizeof(T) plus dynamic
+memory allocated by T objects).
+If we now replace T by some instantiation
+flyweight<T,...>, the resulting memory consumption
+will be
+
+SF = +N·P + M·(T + overhead), ++ +
+where P is sizeof(flyweight<T,...>), typically
+equal to sizeof(void*), as seen before.
+The ratio SF / S is then
+
+SF / S = +(P / T)+ (M / N)(1 + overhead / T). ++ +
+SF / S tends to its minimum, P / T,
+as M / N tends to 0, i.e. when the degree of value redundancy
+among T objects grows higher. On the other hand, the worst possible case
+SF / S = 1 + (P + overhead) / T
+happens when M / N = 1, that is, if there is no value redundancy at all; in this situation there is
+no point in applying the flyweight pattern in the first place.
+
+
+Fig. 1: Relative memory consumption of Boost.Flyweight as a function of value diversity.
+
+The introduction of the flyweight pattern involves an extra level of indirection +that, in general, results in some execution overhead when accessing the values. On +the other hand, manipulation of flyweight objects is considerably faster than +moving around the heavy values they stand for. We analyze qualitatively the +execution overheads or improvements associated to the different usage contexts +of Boost.Flyweight. +
+ +
+As compared with the initialization an object of type T, constructing
+a flyweight<T> performs important extra work like looking
+up the value in the flyweight factory and inserting it if it is not present.
+So, construction of flyweights (other than copy construction, which is
+cheap), is expected to be noticeably slower than the construction of the
+underlying type T. Much of the time spent at constructing
+the associated T value proper can be saved, however, by
+using key-value flyweights.
+
+Assignment of flyweight objects is extremely fast, as it only involves
+assigning an internal handle type used to refer to the shared value. Moreover,
+assignment of flyweight objects never throws. Assignment time
+is influenced by the type of tracking
+policy used; in this regard,
+no_tracking
+is the fastest option.
+
+Comparing two flyweight objects for equality reduces to
+checking that the addresses of the values they are associated to
+are equal; in general, this operation is much faster than comparing the
+underlying values. This aspect is of particular relevance when the flyweight
+objects stand for complex values like those arising in the application of
+the composite pattern.
+
+The conversion from flyweight<T> to const T&
+relies on a level of indirection relating the flyweight objects to the
+values they are associated to; so, value access is expected to be slower
+when using Boost.Flyweight as compared to using the associated values
+directly. This overhead, however, can be masked by an indirect improvement
+resulting from locality and cache effects: as the set of different T
+values handled by an instantiation of flyweight<T> is
+generally much smaller than the equivalent family of T objects
+when Boost.Flyweight is not used, active values can fit better
+into the processor cache.
+
+A profiling program was devised to test
+the space and time efficiency of different instantiations of flyweight
+against a base situation not using Boost.Flyweight. The profiled scenarios are:
+
std::string.flyweight<std::string> with default configuration aspects
+ (hashed_factory,
+ refcounted tracking,
+ simple_locking).
+ flyweight<std::string,no_tracking>.flyweight<std::string,set_factory>.flyweight<std::string,set_factory,no_tracking>.+Actually the types tested are not exactly those listed above, but instrumented +versions that keep track of the allocated memory for profiling purposes. +The program parses a text file into an array of words and then perform various +manipulations involving the different context usages of Boost.Flyweight discussed +previously. As our text file we have used the +plain text +version of Project Gutenberg edition of Don +Quijote (2.04 MB). +
+ +
+The program was built with default release settings and _SECURE_SCL=0.
+Tests were run under Windows XP in a machine equipped with an Intel Core 2 Duo T5500
+processor and 1 GB of RAM.
+
+
+Fig. 2: Memory consumption, MSVC++ 8.0. Values in MB.
+
+The results show the memory consumption figures for the different profiled
+scenarios.
+The standard library implementation of MSVC++ 8.0 features the so-called
+small buffer optimization for strings, by which std::string
+objects hold a small buffer that can be used when the string is short,
+thus avoding dynamic allocations. This results in sizeof(std::string)
+being quite high, 28 bytes. In our particular test strings are almost always
+held in the small buffer, so the minimum SF / S
+achievable is 4/28 = 14.3%, which is quite close to the experimental
+results, given that the memory devoted to storage of shared values
+is residual (around 3% of total memory) due to the high word redundancy
+of the text source.
+
+
+Fig. 3: Execution time, MSVC++ 8.0. Values in seconds.
+
+The figure displays execution times for the profiled scenarios in different
+usage contexts. In accordance with our previous
+qualitative analysis, initialization of flyweights
+carries an important overhead with respect to the base case scenario (between 10% and 40%
+of additional execution time), while the other usage contexts
+(assignment, equality comparison and value access) have performance gains,
+with speedup factors of up to 14 in some cases. The use of a
+refcounted
+tracking policy introduces penalties with respect to
+no_tracking
+in initialization and assignment, but has no effect in equality comparison
+and value access.
+
+The Cygwin/MinGW version of the compiler was used, with command options
+-ftemplate-depth-128 -O3 -finline-functions -DNDEBUG.
+Tests were run under a Cygwin terminal in the same machine as before.
+
+
+Fig. 4: Memory consumption, GCC 3.4.4. Values in MB.
+
+The standard library used by GCC 3.4.4 implements std::string
+using copy-on-write
+optimization techniques, which leads to very small value redundancy for
+some usage patterns. This explains why the memory reduction achieved
+by Boost.Flyweight is so poor in this case. Other contexts where assignment
+is much less used than direct construction will favor Boost.Flyweight
+over plain copy-on-write std::strings.
+
+
+Fig. 5: Execution time, GCC 3.4.4. Values in seconds.
+
+Relative performance figures are similar to those obtained for
+MSVC++ 8.0, although some of the
+speedups achieved by Boost.Flyweight are higher here (×25
+in equality comparison and up to ×100 in assignment when
+no_tracking
+is in effect).
+
+The introduction of Boost.Flyweight in application scenarios with very
+high value redundancy yields important reductions in memory consumption:
+this is especially relevant when data volume approaches the limits of
+physical memory in the machine, since Boost.Flyweight can avoid virtual
+memory thrashing thus making the application viable. We have shown
+how to estimate the achievable reduction in memory consumption from
+some basic value statistics and knowledge of the flyweight
+configuration aspects being used.
+
+Boost.Flyweight can also accelerate execution times in areas other than +object initialization, due to the fastest manipulation of small +flyweight objects and to locality and cache effects arising from the +drastic reduction of the set of allocated values. +
+ +Revised December 2nd 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/prev.gif b/doc/prev.gif new file mode 100644 index 0000000000000000000000000000000000000000..c35dfeec25d2397b8746300b0bf6a62caacd728e GIT binary patch literal 852 zcmZ?wbhEHb6krfw_|5
Boost.Flyweight
+Factories reference"boost/flyweight/factory_tag.hpp" synopsis
+
+ "boost/flyweight/hashed_factory_fwd.hpp" synopsis
+ "boost/flyweight/hashed_factory.hpp" synopsis
+
+ "boost/flyweight/set_factory_fwd.hpp" synopsis
+ "boost/flyweight/set_factory.hpp" synopsis
+
+ "boost/flyweight/assoc_container_factory_fwd.hpp" synopsis
+ "boost/flyweight/assoc_container_factory.hpp" synopsis
+
+
+Given a type Key and an
+Assignable
+type Entry implicitly convertible to const Key&, a
+factory of Entry elements (implicitly associated to
+Key) is a
+Default
+Constructible entity able to store and retrieve immutable elements of
+type Entry. A factory is governed by an associated equivalence
+relation defined on Key so that no two
+Entry objects convertible to equivalent Keys
+can be stored simultaneously in the factory. Different factory types can
+use different equivalence relations.
+
+In the following table, Factory is a factory of elements
+of type Entry, f denotes an object of type Factory,
+x is an object of type Entry and h is a
+value of Factory::handle_type.
+
+
| expression | +return type | +assertion/note pre/post-condition |
+
|---|---|---|
Factory::handle_type |
+ handle to elements of type T+ stored in the factory + |
+ handle_type is
+ Assignable
+ and its copy and+ assignment operations never throw an exception. + |
+
f.insert(x); |
+ handle_type |
+ Inserts a copy of x if there is no equivalent entry in f;+ returns a handle to the inserted or equivalent element. + |
+
f.erase(h); |
+ void |
+ Erases the element associated to h.+ This operation does not throw. + |
+
f.entry(h); |
+ const Entry& |
+ Returns a reference to the element associated to h.+ This operation does not throw. + |
+
+Additionally to the basic thread safety guarantee which is implicitly assumed
+for most classes including the majority of components of the
+C++ standard library, it is required that the member function entry
+can be invoked simultaneously from different threads, even in the presence
+of concurrent accesses to insert and erase (as long
+as the entry returned by entry is not the one which is being
+erased).
+
+A type S is said to be a factory specifier if:
+
is_factory<S>::type is
+ boost::mpl::true_,S is of the form factory<S'>.S, or S' if (b) applies, is an
+ MPL Lambda
+ Expression such that invoking it with types (Entry,
+ Key) resolves to a factory type of Entry elements
+ (implicitly associated to Key).
+ "boost/flyweight/factory_tag.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct factory_marker; + +template<typename T> +struct is_factory; + +template<typename T> +struct factory; + +} // namespace boost::flyweights + +} // namespace boost +
is_factory
+Unless specialized by the user, is_factory<T>::type is
+boost::mpl::true_
+if T is derived from factory_marker, and it is
+boost::mpl::false_
+otherwise.
+
factory
+factory<T> is a syntactic construct meant to indicate
+that T is a factory specifier without resorting to the
+mechanisms provided by the is_factory class template.
+
"boost/flyweight/hashed_factory_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +template< + typename Entry,typename Key, + typename Hash=implementation defined, + typename Pred=implementation defined, + typename Allocator=implementation defined +> +class hashed_factory_class; + +template< + typename Hash=implementation defined, + typename Pred=implementation defined, + typename Allocator=implementation defined +> +struct hashed_factory; + +} // namespace boost::flyweights + +} // namespace boost +
+hashed_factory_fwd.hpp forward declares the class templates
+hashed_factory_class
+and hashed_factory.
+
"boost/flyweight/hashed_factory.hpp" synopsishashed_factory_class
+hashed_factory_class is a Factory
+implemented with a hashed container.
+
+ ++template< + typename Entry,typename Key, + typename Hash,typename Pred,typename Allocator +> +class hashed_factory_class +{ +public: + typedef implementation defined handle_type; + + handle_type insert(const Entry& x); + void erase(handle_type h); + const Entry& entry(handle_type h); +}; +
+Hash is a
+Default
+Constructible
+Unary Function
+taking a single argument of type Key and returning a
+value of type std::size_t in the range
+[0, std::numeric_limits<std::size_t>::max()).
+Pred is a
+Default
+Constructible
+
+Binary Predicate inducing an equivalence relation
+on elements of Key. It is required that
+a Hash object return the same value for objects
+equivalent under Pred.
+The equivalence relation on Key associated to the factory is
+that induced by Pred.
+The default arguments for Hash and Pred are
+boost::hash<Key>
+and std::equal_to<Key>, respectively.
+Allocator must be an allocator of Entry objects
+satisfying the associated C++ requirements at [lib.allocator.requirements].
+The default argument is std::allocator<Entry>. The internal
+hashed container upon which hashed_factory_class is based is
+constructed with default initialized objects of type Hash,
+Pred and Allocator.
+
hashed_factory
+Factory Specifier for hashed_factory_class.
+
+ ++template<typename Hash,typename Pred,typename Allocator> +struct hashed_factory; +
+hashed_factory<Hash,Pred,Allocator> is an
+MPL Metafunction
+Class such that the type
+
+ ++boost::mpl::apply< + hashed_factory<Hash,Pred,Allocator>, + Entry,Key +>::type +
+is the same as +
+ ++ ++boost::mpl::apply< + hashed_factory_class<boost::mpl::_1,boost::mpl::_2,Hash,Pred,Allocator>, + Entry,Key +>::type +
+This implies that Hash, Pred and Allocator
+can be
+MPL
+Placeholder Expressions resolving to the actual types used by
+hashed_factory_class.
+
"boost/flyweight/set_factory_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +template< + typename Entry,typename Key, + typename Compare=implementation defined, + typename Allocator=implementation defined +> +class set_factory_class; + +template< + typename Compare=implementation defined, + typename Allocator=implementation defined +> +struct set_factory; + +} // namespace boost::flyweights + +} // namespace boost +
+set_factory_fwd.hpp forward declares the class templates
+set_factory_class
+and set_factory.
+
"boost/flyweight/set_factory.hpp" synopsisset_factory_class
+set_factory_class is a Factory
+implemented on top of an orderded associative container.
+
+ ++template< + typename Entry,typename Key, + typename Compare,typename Allocator +> +class set_factory_class +{ +public: + typedef implementation defined handle_type; + + handle_type insert(const Entry& x); + void erase(handle_type h); + const Entry& entry(handle_type h); +}; +
+Compare is a
+Default
+Constructible
+
+Strict Weak Ordering on Key. Two Keys
+x and y are considered equivalent if
+!c(x,y)&&!c(y,x) for c of type Compare.
+The default argument of Compare is std::less<Key>
+Allocator must be an allocator of Entry objects
+satisfying the associated C++ requirements at [lib.allocator.requirements].
+The default argument is std::allocator<Entry>. The internal
+container upon which set_factory_class is based is
+constructed with default initialized objects of type Compare
+and Allocator.
+
set_factory
+Factory Specifier for set_factory_class.
+
+ ++template<typename Compare,typename Allocator> +struct set_factory; +
+set_factory<Compare,Allocator> is an
+MPL Metafunction
+Class such that the type
+
+ ++boost::mpl::apply< + set_factory<Compare,Allocator>, + Entry,Key +>::type +
+is the same as +
+ ++ ++boost::mpl::apply< + set_factory_class<boost::mpl::_1,boost::mpl::_2,Compare,Allocator>, + Entry,Key +>::type +
+This implies that Compare and Allocator
+can be
+MPL
+Placeholder Expressions resolving to the actual types used by
+set_factory_class.
+
"boost/flyweight/assoc_container_factory_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +template<typename Container> +class assoc_container_factory_class; + +template<typename ContainerSpecifier> +struct assoc_container_factory; + +} // namespace boost::flyweights + +} // namespace boost +
+assoc_container_factory_fwd.hpp forward declares the class templates
+assoc_container_factory_class
+and assoc_container_factory.
+
"boost/flyweight/assoc_container_factory.hpp" synopsisassoc_container_factory_class
+assoc_container_factory_class wraps a suitable associative container
+to provide a Factory interface.
+
+ ++template<typename Container> +class assoc_container_factory_class +{ +public: + typedef typename Container::iterator handle_type; + + handle_type insert(const typename Container::value_type& x); + void erase(handle_type h); + const typename Container::value_type& entry(handle_type h); +}; +
+Container must be an (ordered or unordered) associative container
+such that
+
Container::key_type is the same as
+ Container::value_type (which is the entry type associated to
+ the factory).
+ Container is stable, i.e. its iterators are not
+ invalidated upon insert or erase operations.assoc_container_factory_class
+is the one induced by Container. If equivalence of elements
+of Container::value_type is determined solely on the basis of a
+type value_type' to which value_type is
+implicitly convertible, then assoc_container_factory_class is
+a factory of entries of type value_type implicitly associated to
+value_type'. For example, the instantiation
+
+
++ ++assoc_container_factory_class< + std::set<derived,std::less<base> > // derived inherits from base +> +
+is a factory of derived elements implicitly associated to
+base.
+
assoc_container_factory
+Factory Specifier for assoc_container_factory_class.
+
+ ++template<typename ContainerSpecifier> +struct assoc_container_factory; +
+ContainerSpecifier must be an
+MPL Lambda
+Expression resolving, when invoked with (Entry,
+Key), to a type Container such that
+assoc_container_factory_class<Container> is a factory
+of Entry elements implicitly associated to Key.
+
Revised August 13th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/reference/flyweight.html b/doc/reference/flyweight.html new file mode 100644 index 0000000..e0cf998 --- /dev/null +++ b/doc/reference/flyweight.html @@ -0,0 +1,630 @@ + + + + + +
Boost.Flyweight
+flyweight reference"boost/flyweight/flyweight_fwd.hpp" synopsis"boost/flyweight/flyweight.hpp" synopsis
+
+ "boost/flyweight/flyweight_fwd.hpp"
+synopsis
++ ++namespace boost{ + +namespace flyweights{ + +template< + typename T, + typename Arg1=implementation defined, + typename Arg2=implementation defined, + typename Arg3=implementation defined, + typename Arg4=implementation defined, + typename Arg5=implementation defined +> +class flyweight; + +// comparison: + +// OP is any of ==,<,!=,>,>=,<= + +template< + typename T1,typename Arg11,...,typename Arg15, + typename T2,typename Arg21,...,typename Arg25 +> +bool operator OP( + const flyweight<T1,Arg11,...,Arg15>& x, + const flyweight<T2,Arg21,...,Arg25>& y); + +template< + typename T1,typename Arg11,...,typename Arg15, + typename T2 +> +bool operator OP(const flyweight<T1,Arg11,...,Arg15>& x,const T2& y); + +template< + typename T1, + typename T2,typename Arg21,...,typename Arg25 +> +bool operator OP(const T1& x,const flyweight<T2,Arg21,...,Arg25>& y); + +// specialized algorithms: + +template<typename T,typename Arg1,...,typename Arg5> +inline void swap( + flyweight<T,Arg1,...,Arg5>& x,flyweight<T,Arg1,...,Arg5>& y); + +template< + typename ElemType,typename Traits, + typename T,typename Arg1,...,typename Arg5 +> +inline std::basic_ostream<ElemType,Traits>& operator<<( + std::basic_ostream<ElemType,Traits>& out, + const flyweight<T,Arg1,...,Arg5>& x); + +template< + typename ElemType,typename Traits, + typename T,typename Arg1,...,typename Arg5 +> +inline std::basic_ostream<ElemType,Traits>& operator>>( + std::basic_istream<ElemType,Traits>& in, + flyweight<T,Arg1,...,Arg5>& x); + +} // namespace boost::flyweights + +using flyweights::flyweight; + +} // namespace boost +
+flyweight_fwd.hpp forward declares the class template
+flyweight and its associated global functions.
+
"boost/flyweight/flyweight.hpp"
+synopsis
+flyweight
+
+Objects of type flyweight<...> provide access to immutable
+values of type flyweight<...>::value_type, with the following advantages over using
+plain value_type objects:
+
value_type object).
+ sizeof(value_type).
+ flyweight
+for value_type results in a reduction in memory usage.
+
+
+
+flyweight is parameterized according to some aspects:
+
key_value and value_type
+ (possibly equal), where key_type serves as a
+ key type to lookup and construct internal shared instances of
+ objects of value_type.
+ flyweight
+ class template.
+ flyweight instantiation in the following manner:
+flyweight internally owns
+ a unique factory object and a unique synchronization
+ mutex object, both of which
+ are created through the use of an associated holder type.
+ Entry that is implicitly convertible to
+ const key_type& and also stores a subobject of
+ value_type. Every flyweight object is associated
+ to a value_type subobject of some Entry
+ stored in the factory.
+ Handle. Handle and
+ the Entry type referred to above are obtained
+ from invocations to the associated tracking policy, in the
+ manner described for this concept.
+ key_type equivalence
+refers to the equivalence relationship induced by the factory class used.
+Also, two values of value_type are considered equivalent
+if they are constructed from equivalent keys, or are copies of
+objects constructed from equivalent keys.
+
+
++ ++template< + typename T, + typename Arg1,typename Arg2,typename Arg3,typename Arg4,typename Arg5 +> +class flyweight +{ +public: + typedef dependent on T key_type; + typedef dependent on T value_type; + + // static data initialization: + + static bool init(); + class initializer{public:initializer();}; + + // construct/copy/destroy: + + flyweight(); + flyweight(const flyweight& x); + flyweight(flyweight& x); + + // forwarding constructors: + // n is implementation defined. All combinations of constant + // and non constant reference arguments are provided. + + template<typename T0> + explicit flyweight([const] T0& t0); + template<typename T0,typename T1> + flyweight([const] T0& t0,[const] T1& t1); + ... + template<typename T0,...,typename Tn-1> + flyweight([const] T0& t0,...,[const] Tn-1& tn-1); + + flyweight& operator=(const flyweight& x); + flyweight& operator=(const value_type& x); + + // convertibility to underlying type: + + const key_type& get_key()const; + const value_type& get()const; + operator const value_type&()const; + + // modifiers: + + void swap(flyweight& x); +}; +
+T can be either:
+
key_value<Key,Value[,KeyFromValue]>.key_type and value_type
+are both equal to T. In the second case, key_type=Key,
+value_type=Value; we say then that the instantiation
+of flyweight is a key-value flyweight.
+value_type is the type of the values flyweight objects give access to,
+while value lookup is based on associated key_type values.
+key_value must be
+Assignable
+and value_type must be constructible from key_type;
+additionally, key_value must
+conform to any extra requirements imposed by the type of factory used.
+For key-value flyweights, it is guaranteed that the creation or assignment of a flyweight
+object results in at most one construction (or copy construction in some
+particular cases) of an object
+of value_type, and this construction only occurs in the case that no
+equivalent value existed previously in the flyweight factory.
+
+
+
+The types Arg1, ... , Arg5, if provided, must be any
+of the following, in no particular order:
+
flyweight instantiation is obtained through use of the
+corresponding specifier; for instance, the factory results from a
+certain (MPL) invocation of the given factory specifier, the internal
+mutex from the given locking policy, etc.
+The default configuration arguments are:
+hashed_factory<>,static_holder,simple_locking,refcounted tracking policy.
+The static data internal to a given flyweight instantiation
+(factory instance, etc.) is constructed during the dynamic initialization
+phase of the program and always before the first program-wide use of the
+instantiated class. The following utilities can be
+used when more control about the moment of construction is required.
+
static bool init();
+
++Effects: After execution of this function the static data associated +to the instantiation of+ +flyweightis guaranteed to be +constructed.
+Note: Concurrent execution of this function is not thread safe. +
initializer::initializer();
+
+
+Effects: Executes init().
+
+
+flyweight();
+
++Requires:+ +key_typeis +Default +Constructible.
+Effects: Constructs aflyweightobject associated +with valuevalue_type(key_type()). +
flyweight(const flyweight& x);
+flyweight(flyweight& x);
+
++Effects: Constructs a+ + +flyweightobject with value +x.get().
+Exception safety:nothrow. +
template<typename T0>
+explicit flyweight([const] T0& t0);
+template<typename T0,typename T1>
+flyweight([const] T0& t0,[const] T1& t1);
+...
+template<typename T0,...,typename Tn-1>
+flyweight([const] T0& t0,...,[const] Tn-1& tn-1);
+
++Effects: Constructs a+ +flyweightobject with value +value_type(key_type(t0,...,ti)), up to an implementation defined number +of arguments.
+Note: In this implementation, the maximum number of arguments +can be globally configured by the user. +
flyweight& operator=(const flyweight& x);
+
++Effects: Associates the+ +flyweightobject with the same value +asx.
+Returns:*this.
+Exception safety:nothrow. +
flyweight& operator=(const value_type& x);
+
++Requires: If+ +flyweightis key-value, +value_typeis +Assignable+and the +Key Extractor+KeyFromValuemust have been supplied as part of the +key_value<>construct.
+Effects: Associates theflyweightobject with a +copy ofxor with avalue_typeconstructed +from a key equivalent to that associated tox. For non-key-value +flyweights,xis its own key; for key-value flyweights, +the key is extracted through use of an object of typeKeyFromValue.
+Returns:*this.
+
const key_type& get_key()const;
+
++Return: A copy of the key used to construct the ++ +value_typeassociated to theflyweight+object.
+Exception safety: Ifflyweightis not key-value or +ifKeyFromValuewas not provided,nothrow. +
const value_type& get()const;
+ operator const value_type&()const;
+
++Return: The value associated to the+ +flyweight+object.
+Exception safety:nothrow. +
void swap(flyweight& x);
+
++Effects: Swaps the associations to+ +value_types each +flyweight object has. No swapping ofkey_typeor +value_typeobjects is done.
+Exception safety:nothrow. +
template<
+ typename T1,typename Arg11,...,typename Arg15,
+ typename T2,typename Arg21,...,typename Arg25
+>
+bool operator ==(
+ const flyweight<T1,Arg11,...,Arg15>& x,
+ const flyweight<T2,Arg21,...,Arg25>& y);
+
++Returns: If+ +xandyare of the same type, returns +trueif and only if they are associated to the same value; if +xandyhave different types, returns +x.get()==y.get().
+Exception safety: Ifxandyare of the same type, +nothrow. +
template<
+ typename T1,typename Arg11,...,typename Arg15,
+ typename T2
+>
+bool operator ==(const flyweight<T1,Arg11,...,Arg15>& x,const T2& y);
+
+
+Returns: x.get()==y.
+
+
+template<
+ typename T1,
+ typename T2,typename Arg21,...,typename Arg25
+>
+bool operator ==(const T1& x,const flyweight<T2,Arg21,...,Arg25>& y);
+
+
+Returns: x()==y.get().
+
+
+template<
+ typename T1,typename Arg11,...,typename Arg15,
+ typename T2,typename Arg21,...,typename Arg25
+>
+bool operator <(
+ const flyweight<T1,Arg11,...,Arg15>& x,
+ const flyweight<T2,Arg21,...,Arg25>& y);
+
+
+Returns: x.get()<y.get().
+
+
+template<
+ typename T1,typename Arg11,...,typename Arg15,
+ typename T2
+>
+bool operator <(const flyweight<T1,Arg11,...,Arg15>& x,const T2& y);
+
+
+Returns: x.get()<y.
+
+
+template<
+ typename T1,
+ typename T2,typename Arg21,...,typename Arg25
+>
+bool operator <(const T1& x,const flyweight<T2,Arg21,...,Arg25>& y);
+
+
+Returns: x()<y.get().
+
+
+
+template<
+ typename T1,typename Arg11,...,typename Arg15,
+ typename T2,typename Arg21,...,typename Arg25
+>
+bool operator OP(
+ const flyweight<T1,Arg11,...,Arg15>& x,
+ const flyweight<T2,Arg21,...,Arg25>& y);
+template<
+ typename T1,typename Arg11,...,typename Arg15,
+ typename T2
+>
+bool operator OP(const flyweight<T1,Arg11,...,Arg15>& x,const T2& y);
+template<
+ typename T1,
+ typename T2,typename Arg21,...,typename Arg25
+>
+bool operator OP(const T1& x,const flyweight<T2,Arg21,...,Arg25>& y);
+
+
+(OP is any of !=, >,
+>=, <=.)
+
+Returns:+ +trueif and only if +++!(x==y)(OPis!=),
+y< x(OPis>),
+!(x< y)(OPis>=),
+!(y< x)(OPis<=). +
template<typename T,typename Arg1,...,typename Arg5>
+inline void swap(
+ flyweight<T,Arg1,...,Arg5>& x,flyweight<T,Arg1,...,Arg5>& y);
+
+
+Effects: x.swap(y).
+
+
+template<
+ typename ElemType,typename Traits,
+ typename T,typename Arg1,...,typename Arg5
+>
+inline std::basic_ostream<ElemType,Traits>& operator<<(
+ std::basic_ostream<ElemType,Traits>& out,
+ const flyweight<T,Arg1,...,Arg5>& x);
+
++Effects:+ +out<<x.get().
+Returns:out. +
template<
+ typename ElemType,typename Traits,
+ typename T,typename Arg1,...,typename Arg5
+>
+inline std::basic_ostream<ElemType,Traits>& operator>>(
+ std::basic_istream<ElemType,Traits>& in,
+ flyweight<T,Arg1,...,Arg5>& x);
+
++Requires: If+ +flyweightis key-value, +value_typeis +Assignable+and the +Key Extractor+KeyFromValuemust have been supplied as part of the +key_value<>construct.
+Effects: Reads an object of typevalue_typefromin+and assigns it tox.
+Returns:in. +
BOOST_FLYWEIGHT_LIMIT_PERFECT_FWD_ARGS
+
+
+Effects: Globally define this macro to set the maximum number of
+arguments accepted by flyweight
+forwarding constructors, which by default
+is 5.
+
+
+Revised December 2nd 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/reference/holders.html b/doc/reference/holders.html new file mode 100644 index 0000000..c8e910d --- /dev/null +++ b/doc/reference/holders.html @@ -0,0 +1,243 @@ + + + + + +
Boost.Flyweight
+Holders reference"boost/flyweight/holder_tag.hpp" synopsis
+
+ "boost/flyweight/static_holder_fwd.hpp" synopsis
+ "boost/flyweight/static_holder.hpp" synopsis
+
+ "boost/flyweight/intermodule_holder_fwd.hpp" synopsis
+ "boost/flyweight/intermodule_holder.hpp" synopsis
+
+
+Given a type C, a type Holder is said to be a holder
+of C if the expression Holder::get() returns
+a reference to a default initialized C object unique to
+Holder. No invocation of Holder::get(), except possibly
+the first one in the program, does throw.
+flyweight
+privately uses a holder to instantiate a factory
+and some additional data (like a mutex for
+internal synchronization) unique to each instantiation type of the class template.
+
+A type S is a holder specifier if:
+
is_holder<S>::type is
+ boost::mpl::true_,S is of the form holder<S'>.S, or S' if (b) applies, is an
+ MPL Lambda
+ Expression such that invoking it with type C resolves to
+ a holder of C.
+ "boost/flyweight/holder_tag.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct holder_marker; + +template<typename T> +struct is_holder; + +template<typename T> +struct holder; + +} // namespace boost::flyweights + +} // namespace boost +
is_holder
+Unless specialized by the user, is_holder<T>::type is
+boost::mpl::true_
+if T is derived from holder_marker, and it is
+boost::mpl::false_
+otherwise.
+
holder
+holder<T> is a syntactic construct meant to indicate
+that T is a holder specifier without resorting to the
+mechanisms provided by the is_holder class template.
+
"boost/flyweight/static_holder_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +template<typename C> +struct static_holder_class; + +struct static_holder; + +} // namespace boost::flyweights + +} // namespace boost +
+static_holder_fwd.hpp forward declares
+static_holder_class
+and static_holder.
+
"boost/flyweight/static_holder.hpp" synopsisstatic_holder_class
+static_holder_class<C> keeps its unique instance of C as a
+local static object.
+
static_holder
+Holder Specifier for static_holder_class.
+
"boost/flyweight/intermodule_holder_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +template<typename C> +struct intermodule_holder_class; + +struct intermodule_holder; + +} // namespace boost::flyweights + +} // namespace boost +
+intermodule_holder_fwd.hpp forward declares
+intermodule_holder_class
+and intermodule_holder.
+
"boost/flyweight/intermodule_holder.hpp" synopsisintermodule_holder_class
+intermodule_holder_class<C> maintains a C
+instance which is unique even across different dynamically linked modules of
+the program using this same type. In general, this guarantee is not provided by
+static_holder_class, as most
+C++ implementations are not able to merge duplicates of static variables stored
+in different dynamic modules of a program.
+
intermodule_holder
+Holder Specifier for intermodule_holder_class.
+
Revised August 11th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/reference/index.html b/doc/reference/index.html new file mode 100644 index 0000000..a9754a1 --- /dev/null +++ b/doc/reference/index.html @@ -0,0 +1,158 @@ + + + + + +
Boost.Flyweight Reference"boost/flyweight.hpp" synopsisflyweight+Boost.Flyweight comprises the following public headers: +
"boost/flyweight.hpp"flyweight
+
+ +Boost.Flyweight is a header-only library, requiring no additional +object modules. +
+ +"boost/flyweight.hpp"
+synopsis
++ ++#include <boost/flyweight/flyweight.hpp> +#include <boost/flyweight/hashed_factory.hpp> +#include <boost/flyweight/refcounted.hpp> +#include <boost/flyweight/simple_locking.hpp> +#include <boost/flyweight/static_holder.hpp> +
+This convenience header includes the main class template
+flyweight along with
+the default components used by flyweight.
+
Revised December 2nd 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/reference/key_value.html b/doc/reference/key_value.html new file mode 100644 index 0000000..d1d0364 --- /dev/null +++ b/doc/reference/key_value.html @@ -0,0 +1,124 @@ + + + + + +
Key-value flyweights reference"boost/flyweight/key_value_fwd.hpp" synopsis
+ "boost/flyweight/key_value.hpp" synopsis
+
+
+Let Key be a type with some implicit equivalence relationship
+and Value a type constructible from Key.
+A Default
+Constructible type KeyFromValue is said
+to be a key extractor from Value to Key if
+
kfv(cv) is defined and have type const Key&,kfv(cv) is equivalent to kfv(Value(cv)),kfv(Value(k)) is equivalent to k,kfv of type const KeyFromValue,
+cv of type const Value and
+k of type Key.
+
+
+"boost/flyweight/key_value_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct no_key_from_value; + +template< + typename Key,typename Value, + typename KeyFromValue=no_key_from_value +> +struct key_value; + +} // namespace boost::flyweights + +} // namespace boost +
"boost/flyweight/key_value.hpp" synopsiskey_value
+In flyweight instantiations
+of the form flyweight<T,...>, the associated
+key_type and value_type are both equal to T.
+Instantiations of the form flyweight<key_value<Key,Value[,KeyFromValue]>,...>
+allow to specify these types separately. Key and Value
+must be different types. When provided, KeyFromValue
+must be a Key Extractor from
+Value to Key.
+
Revised September 15th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/reference/locking.html b/doc/reference/locking.html new file mode 100644 index 0000000..f8369a6 --- /dev/null +++ b/doc/reference/locking.html @@ -0,0 +1,287 @@ + + + + + +
Boost.Flyweight
+Locking policies reference"boost/flyweight/locking_tag.hpp" synopsis
+
+ "boost/flyweight/simple_locking_fwd.hpp" synopsis
+ "boost/flyweight/simple_locking.hpp" synopsis
+
+ "boost/flyweight/no_locking_fwd.hpp" synopsis
+ "boost/flyweight/no_locking.hpp" synopsis
+ no_locking
+A mutex is a type whose objects can be in either of two states, called
+locked and unlocked, with the property that when a thread A has locked a
+mutex m and a different thread B tries to lock m,
+B is blocked until A unlocks m. Additionally, a mutex is said to
+support recursive locking if a thread can succesfully invoke the locking
+operation for a mutex already locked by this same thread; in this case, it is
+required that the thread execute as many unlock operations as lock
+operations it has performed for the mutex to become effectively unlocked.
+A scoped lock is a
+type associated to some mutex type whose objects do the locking/unlocking
+of a mutex on construction/destruction time.
+
+In the following table, Mutex is a mutex type, m
+is an object of type Mutex, Lock is a scoped lock
+associated to Mutex and lk is a value of
+Lock.
+
+
+
| expression | +return type | +assertion/note pre/post-condition |
+
|---|---|---|
Mutex m; |
+ + | Post: m is unlocked.
+ |
+
(&m)->~Mutex(); |
+ void |
+ Pre: m is unlocked. |
+
Lock lk(m); |
+ + | Associates m to lk and locks m. |
+
(&lk)->~Lock(); |
+ void |
+ Unlocks the mutex associated to lk. |
+
+These concepts are very similar, but not entirely equivalent, to +the homonym ones described in the +Boost Thread +Library. +
+ +
+Locking policies describe a mutex type and an associated
+scoped lock type.
+flyweight uses a given locking
+policy to synchronize the access to its internal
+factory.
+
+A type Locking is a locking policy if:
+
is_locking<Locking>::type is
+ boost::mpl::true_,Locking is of the form
+ locking<Locking'>.Locking::mutex_type (or
+ Locking'::mutex_type if (b) applies) is a
+ model of Mutex
+ and supports recursive locking.
+ Locking::lock_type (or
+ Locking'::lock_type if (b) applies) is a
+ Scoped Lock of
+ the mutex referred to above.
+ "boost/flyweight/locking_tag.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct locking_marker; + +template<typename T> +struct is_locking + +template<typename T> +struct locking; + +} // namespace boost::flyweights + +} // namespace boost +
is_locking
+Unless specialized by the user, is_locking<T>::type is
+boost::mpl::true_
+if T is derived from locking_marker, and it is
+boost::mpl::false_
+otherwise.
+
locking
+locking<T> is a syntactic construct meant to indicate
+that T is a locking policy without resorting to the
+mechanisms provided by the is_locking class template.
+
"boost/flyweight/simple_locking_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct simple_locking; + +} // namespace boost::flyweights + +} // namespace boost +
+simple_locking_fwd.hpp forward declares the class
+simple_locking.
+
"boost/flyweight/simple_locking.hpp" synopsissimple_locking
+Locking Policy that specifies a basic
+mutex type based on the simplest synchronization mechanisms provided by
+the environment; When no threading capabilities are available,
+simple_locking specifies a dummy type without actual
+synchronization capabilities.
+
"boost/flyweight/no_locking_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct no_locking; + +} // namespace boost::flyweights + +} // namespace boost +
+no_locking_fwd.hpp forward declares the class
+no_locking.
+
"boost/flyweight/no_locking.hpp" synopsisno_locking
+Null Locking Policy: it specifies a dummy
+type that satisfies the formal requirements for the
+Mutex concept but does not perform
+thread blocking. no_locking should only be used in single-threaded
+environments.
+
Revised August 13th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/reference/tags.html b/doc/reference/tags.html new file mode 100644 index 0000000..c58ab86 --- /dev/null +++ b/doc/reference/tags.html @@ -0,0 +1,107 @@ + + + + + +
Boost.Flyweight
+Tags reference
+A tag is a type of the form
+tag<T> for some arbitrary
+T.
+In the context of Boost.Flyweight, tags are syntactic artifacts used
+to differentiate instantiations of the class template
+flyweight which would
+otherwise be identical. Tagging a flyweight instantiation with
+a tag type local to a given context ensures that the global resources
+of that instantiation (for instance, the associated
+factory class) will not be unintentionally
+shared by other areas of the program.
+
"boost/flyweight/tag.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +template<typename T> +struct tag; + +} // namespace boost::flyweights + +} // namespace boost +
tag
+For any type T, tag<T> is a suitable
+tag for use in instantiations of
+flyweight.
+
Revised August 11th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/reference/tracking.html b/doc/reference/tracking.html new file mode 100644 index 0000000..b310bfc --- /dev/null +++ b/doc/reference/tracking.html @@ -0,0 +1,291 @@ + + + + + +
Boost.Flyweight
+Tracking policies reference"boost/flyweight/tracking_tag.hpp" synopsis
+
+ "boost/flyweight/refcounted_fwd.hpp" synopsis
+ "boost/flyweight/refcounted.hpp" synopsis
+ refcounted"boost/flyweight/no_tracking_fwd.hpp" synopsis
+ "boost/flyweight/no_tracking.hpp" synopsis
+
+
+A tracking policy helper provides access to some of the functionality
+of a factory so as to be used
+in the implementation of an associated Tracking Policy.
+In the following table, TrackingHelper is a tracking policy
+helper associated to a factory type
+Factory of elements of type Entry, h
+is a value of
+Factory::handle_type associated to a Factory f
+and check is a value of
+a Predicate
+type Checker with argument of type Factory::handle_type.
+
+
| expression | +return type | +assertion/note pre/post-condition |
+
|---|---|---|
TrackingHelper::entry(h); |
+ const Entry& |
+ Returns f.entry(h). |
+
TrackingHelper::erase(h,check); |
+ void |
+ If check(h), invokes f.erase(h). |
+
+The execution of TrackingHelper::erase (including the
+invocation of check(h)) is done in a
+synchronized manner so as to prevent any other thread of execution from
+simultaneously accessing the factory's insertion or deletion facilities.
+
+A tracking policy defines the strategy to be followed by a
+flyweight instantiation when
+all the flyweight objects associated to a given value are destroyed.
+The tracking policy contributes some type information necessary for the
+definition of the flyweight internal
+factory.
+
+A type Tracking is a tracking policy if:
+
is_tracking<Tracking>::type is
+ boost::mpl::true_,Tracking is of the form
+ tracking<Tracking'>.Tracking::entry_type (or
+ Tracking'::entry_type if (b) applies) is an
+ MPL Lambda
+ Expression that resolves, when invoked with different types
+ (Value,Key) such that Value is
+ Assignable
+ and implicitly convertible to const Key&, to an
+ Assignable
+ type Entry implicitly convertible to both const Value&
+ and const Key&.
+ Tracking::handle_type (or
+ Tracking'::handle_type if (b) applies) is an
+ MPL Lambda
+ Expression; this expression, when invoked with types
+ (InternalHandle,TrackingHelper),
+ with InternalHandle being
+ Assignable
+ and providing the nothrow guarantee for copy and assignment,
+ resolves to an
+ Assignable
+ type Handle which also provides the nothrow guarantee for
+ copy and assignment and is constructible from and implicitly
+ convertible to InternalHandle.
+ TrackingHelper is an incomplete type at the time of
+ invocation of Tracking::handle_type.
+ Tracking::handle_type is parameterized by a helper that provides
+access to some of the functionality of the factory associated to the
+tracking policy. This factory's associated entry and handle types are the types
+Entry and Handle defined above, respectively.
+
+
+"boost/flyweight/tracking_tag.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct tracking_marker; + +template<typename T> +struct is_tracking + +template<typename T> +struct tracking; + +} // namespace boost::flyweights + +} // namespace boost +
is_tracking
+Unless specialized by the user, is_tracking<T>::type is
+boost::mpl::true_
+if T is derived from tracking_marker, and it is
+boost::mpl::false_
+otherwise.
+
tracking
+tracking<T> is a syntactic construct meant to indicate
+that T is a tracking policy without resorting to the
+mechanisms provided by the is_tracking class template.
+
"boost/flyweight/refcounted_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct refcounted; + +} // namespace boost::flyweights + +} // namespace boost +
+refcounted_fwd.hpp forward declares the class
+refcounted.
+
"boost/flyweight/refcounted.hpp" synopsisrefcounted
+Tracking Policy providing
+flyweight
+instantiations with reference counting semantics: when all the flyweight objects
+associated to a given value are destroyed, the corresponding entry is
+erased from flyweight's internal
+factory.
+
"boost/flyweight/no_tracking_fwd.hpp" synopsis+ ++namespace boost{ + +namespace flyweights{ + +struct no_tracking; + +} // namespace boost::flyweights + +} // namespace boost +
+no_tracking_fwd.hpp forward declares the class
+no_tracking.
+
"boost/flyweight/no_tracking.hpp" synopsisno_tracking
+Null Tracking Policy: elements inserted
+into flyweight's internal factory
+are not erased until program termination.
+
Revised August 18th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/release_notes.html b/doc/release_notes.html new file mode 100644 index 0000000..54a5868 --- /dev/null +++ b/doc/release_notes.html @@ -0,0 +1,70 @@ + + + + + +
Boost.Flyweight Release notes+
Revised August 27th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/style.css b/doc/style.css new file mode 100644 index 0000000..99e7da3 --- /dev/null +++ b/doc/style.css @@ -0,0 +1,54 @@ +/* Copyright 2003-2004 Joaquín M López Muñoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +pre{ + BORDER-RIGHT: gray 1pt solid; + PADDING-RIGHT: 2pt; + BORDER-TOP: gray 1pt solid; + DISPLAY: block; + PADDING-LEFT: 2pt; + PADDING-BOTTOM: 2pt; + BORDER-LEFT: gray 1pt solid; + MARGIN-RIGHT: 32pt; + PADDING-TOP: 2pt; + BORDER-BOTTOM: gray 1pt solid; + FONT-FAMILY: "Courier New", Courier, mono; + background-color: #EEEEEE; +} + +table{ + PADDING-RIGHT: 2pt; + BORDER-TOP: gray 1pt solid; + DISPLAY: block; + PADDING-LEFT: 2pt; + PADDING-BOTTOM: 2pt; + BORDER-LEFT: gray 1pt solid; + MARGIN-RIGHT: 32pt; + PADDING-TOP: 2pt; + background-color: #EEEEEE; +} +td{ + BORDER-STYLE: solid; + BORDER-WIDTH: 1pt; + BORDER-LEFT: ; + BORDER-RIGHT: gray 1pt solid; + BORDER-TOP: ; + BORDER-BOTTOM: gray 1pt solid; +} +th{color: #ffffff; background-color: #000000;} +.odd_tr{background-color: #ffffff;} + +.keyword{color: #0000FF;} +.identifier{} +.comment{font-style: italic; color: #008000;} +.special{color: #800040;} +.preprocessor{color: #3F007F;} +.string{font-style: italic; color: #666666;} +.literal{font-style: italic; color: #666666;} + +.prev_link{width: 30%; float: left; text-align: left;} +.up_link{width: 39.9%; float: left; text-align: center;} +.next_link{width: 30%; float: left; text-align: right;} diff --git a/doc/tests.html b/doc/tests.html new file mode 100644 index 0000000..3da7f22 --- /dev/null +++ b/doc/tests.html @@ -0,0 +1,114 @@ + + + + + +
Boost.Flyweight Tests+The Boost.Flyweight test suite exercises the whole spectrum of +functionalities provided by the library. Although the tests are not meant +to serve as a learning guide, the interested reader may find it +useful to inspect the source code to gain familiarity with the usage +of Boost.Flyweight. +
+ ++
| Program | +Description | +
|---|---|
test_assoc_cont_factory.cpp |
+ assoc_container_factory
+ factory specifier. |
+
test_basic.cpp |
+ Exercises the default components of flyweight. |
+
test_custom_factory.cpp |
+ Creates a user-defined factory class and specifier. | +
test_init.cpp |
+ Boost.Flyweight static + data initialization facilities. | +
test_intermod_holder.cpp+ intermod_holder_dll.cpp |
+ Exercises intermodule_holder. |
+
test_multictor.cpp |
+ Tests flyweight multiple
+ argument constructors. |
+
test_no_locking.cpp |
+ no_locking policy. |
+
test_no_tracking.cpp |
+ no_tracking policy. |
+
test_set_factory.cpp |
+ set_factory
+ factory specifier. |
+
Revised December 2nd 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/time_gcc_344.png b/doc/time_gcc_344.png new file mode 100644 index 0000000000000000000000000000000000000000..86f878aec542f8b29f40c6d711c88887b98f250d GIT binary patch literal 12600 zcmeAS@N?(olHy`uVBq!ia0y~yU^Zc3V07VNV_;zTc)of*0|NtRfk$L90|Va?5N4dJ z%_q&kz`&C3=h2`9WZ&6jhbh^HrpEJ`e
zY6E1DkWmP<)}$okg1gYi&?8G$$b#y0P mCZ}En<+h0!iUz*&$^_be7>1-|!J#4n|n;5H150Yc%3v}SPI(<@i
zM&!Tk5xIdfao?9k-8S56HTSg3r
+Suppose we are writing a massive multiplayer online game
+which has to maintain hundreds of thousands or millions of instances
+of the following class in memory:
+
+In this kind of environments memory resources are precious, so we are seeking
+ways to make
+Boost.Flyweight automatically performs the optimization just described behind
+the scenes, so that the net effect of this change is that the memory
+usage of the program decreases by a factor proportional to the level of
+redundancy among user names.
+
+
+Besides,
+The most important restriction to take into account when replacing a class
+with an equivalent flyweight is the fact that flyweights are not
+mutable: since several flyweight objects can share the same representation
+value, modifying this value is not admissible. On the other hand, flyweight
+objects can be assigned new values:
+
+In general,
+For
+As we have seen, equality and hash requirements on Revised December 2nd 2008 © Copyright 2006-2008 Joaquín M López Muñoz.
+Distributed under the Boost Software
+License, Version 1.0. (See accompanying file
+LICENSE_1_0.txt or copy at
+http://www.boost.org/LICENSE_1_0.txt)
+
+Most of the time,
+The
+or like this:
+
+or in any other order; only
+The example code shown at the introductory section
+uses the
+
+When using components other than these, their specific headers must be
+explicitly included.
+
+Consider the following two types:
+
+Although technically both types are identical, this is so by virtue of
+coincidence, as there is no sensible relation between names and IP addresses.
+Internally, the fact that
+Now,
+
+where
+A given
+This specifier, which Boost.Flyweight takes by default, controls the usage of a
+factory internally based in a hash container. Values are determined to be
+equivalent by means of the
+
+is equivalent to
+
+where
+
+is equivalent to
+
+Usual tradeoffs arising in the comparison of ordered and hashed containers
+also apply when choosing between
+This specifier can be seen as a generalization of
+PaD_Y!Sq@<+{kMPOacr^8X6WJ+#Q?Huq
zub=K_kI(-vm~>w&wB%xAmgqeXYCBe?v&~dnI+-D;d$nMP9=oylYze!
+
+
+
+
Boost.Flyweight Tutorial: Basics
+
+
+
+Contents
+
+
+
+Introduction
+
+
+
+
+struct user_entry
+{
+ std::string first_name;
+ std::string last_name;
+ int age;
+ ...
+};
+
user_entry as compact as possible. Typically, there
+exists a very high level of repetition of first and last names among
+the community users, so an obvious optimization consists in moving
+user_entry::first_name and user_entry::last_name
+objects to a common repository where duplicates are avoided, and leaving
+references to these inside user_entry. This is precisely what
+Boost.Flyweight does in the simplest possible way for the programmer:
+
+
+
+#include <boost/flyweight.hpp>
+
+struct user_entry
+{
+ flyweight<std::string> first_name;
+ flyweight<std::string> last_name;
+ int age;
+ ...
+};
+
flyweight<std::string> behaves in many ways like
+std::string; for instance, the following code works
+unchanged after the redefinition of user_entry:
+
+
+
+// flyweight<T> can be constructed in the same way as T objects can,
+// even with multiple argument constructors
+
+user_entry::user_entry(const char* f,const char* l,int a,...):
+ first_name(f),
+ last_name(l),
+ age(a),
+ ...
+{}
+
+// flyweight classes have relational operators replicating the
+// semantics of the underyling type
+
+bool same_name(const user_entry& user1,const user_entry& user2)
+{
+ return user1.first_name==user2.first_name &&
+ user1.last_name==user2.last_name;
+}
+
+// flyweight<T> provides operator<< and operator>> internally
+// forwarding to T::operator<< and T::operator>>
+
+std::ostream& operator<<(std::ostream& os,const user_entry& user)
+{
+ return os<<user.first_name<<" "<<user.last_name<<" "<<user.age;
+}
+
+std::istream& operator>>(std::istream& is,user_entry& user)
+{
+ return is>>user.first_name>>user.last_name>>user.age;
+}
+
flyweight<T> is convertible to
+const T&, either implicitly or through the get
+member function:
+
+
+
+std::string full_name(const user_entry& user)
+{
+ std::string full;
+
+ full.reserve(
+ user.first_name.get().size()+ // get() returns the underlying
+ user.last_name.get().size()+1); // const std::string&
+
+ full+=user.first_name; // implicit conversion is used here
+ full+=" ";
+ full+=user.last_name;
+
+ return full;
+}
+
+
+
+void change_name(
+ user_entry& user,
+ const std::string& f,const std::string& l)
+{
+ user.first_name=f;
+ user.last_name=l;
+}
+
flyweight<T> interface is designed to make
+the transition from plain T as straightforward as possible.
+Check the reference for
+further details on the interface of the class template flyweight.
+The examples section explores
+some common usage scenarios of Boost.Flyweight.
+Flyweight requirements
+
+flyweight<T> to be instantiable, T must
+be Assignable,
+Equality
+Comparable and must interoperate with
+Boost.Hash.
+The first requirement is probably met without any extra effort by the user,
+not so the other two, except for the most common basic types of C++
+and the standard library. Equality and hashing of T are used
+internally by flyweight<T> internal factory to maintain the
+common repository of unique T values referred to by the flyweight
+objects. Consult the Boost.Hash documentation
+section on extending
+that library for custom data types.
+T are
+imposed by the particular type of flyweight factory internally used by
+flyweight<T>. We will see later how the user can customize
+this factory to use equality and hash predicates other than the default,
+or even switch to an entirely different kind of factory which may impose
+another requirements on T, as described in the section on
+configuring Boost.Flyweight.
+
+
+
+
+
+
+
+
+
+
+
+
+
Boost.Flyweight Tutorial: Configuring Boost.Flyweight
+
+
+
+Contents
+
+
+
+
+
+
+ simple_lockingno_locking
+
+ refcountedno_trackingConfigurable aspects of Boost.Flyweight
+
+flyweight default configuration is just good
+enough and the user need not care about further tuning of her flyweight
+instantiations; however, when the necessity for more control over Boost.Flyweight
+behavior arises, comprehensive mechanisms are provided to select, configure and
+even extend the following implementation aspects:
+
+
+flyweight objects refer to.
+ Free-order template parameter interface
+
+flyweight class template features a "smart" specification
+interface by which the configuration aspects can be provided as optional template arguments
+in whatever order the user pleases. For instance, a tagged flyweight
+of std::strings with a set-based factory and
+no tracking can be specified like this:
+
+
+
+flyweight<std::string, tag<label_t>, set_factory<>, no_tracking >
+
+
+
+flyweight<std::string, no_tracking, tag<label_t>, set_factory<> >
+
std::string is required to occupy
+the first place in the specification.
+Header inclusion
+
+"boost/flyweight.hpp"
+convenience header, which simply includes the headers for the class template
+flyweight and its default configuration components:
+
+
+
+#include <boost/flyweight/flyweight.hpp> // class template flyweight
+#include <boost/flyweight/hashed_factory.hpp> // hashed flyweight factory
+#include <boost/flyweight/static_holder.hpp> // regular factory instantiation
+#include <boost/flyweight/simple_locking.hpp> // simple locking policy
+#include <boost/flyweight/refcounted.hpp> // refcounting tracking policy
+
Tagging
+
+
+
+
+typedef flyweight<std::string> name_t;
+typedef flyweight<std::string> ip_address_t;
+
name_t and ip_address_t
+are the same flyweight type causes values of both classes to be stored together
+in the same flyweight factory, although their respective ranges
+are not expected to overlap. Tagging can be used to turn these
+into really different types:
+
+
+
+struct name_tag{};
+typedef flyweight<std::string,tag<name_tag> > name_t;
+
+struct ip_address_tag{};
+typedef flyweight<std::string,tag<ip_address_tag> > ip_address_t;
+
name_t and ip_address_t are different
+flyweight classes having separate factories each. Tags are a purely syntactic
+device: any type can be used for tagging inside the tag
+construct, though good style recommends using tag classes with
+descriptive names which are local to the context where the flyweight type
+is being defined.
+Factory specification
+
+flyweight uses a type of internal component called
+factory whose purpose is to store and retrieve the different values
+flyweight objects refer to at a given time. By default, a factory based on
+a hashed container is used, so that flyweight<T> is
+actually equivalent to
+
+
+
+flyweight<T,hashed_factory<> >
+
hashed_factory is a so-called factory specifier.
+Boost.Flyweight provides several predefined factory specifiers, which not
+only let the user select the specific type of factory used, but also
+accept their own template arguments to customize each factory.
+Types involved in the configuration of factories
+
+flyweight instantiation has associated
+flyweight::key_type
+and flyweight::value_type types (which are equal in the case
+of regular flyweights or different if key-value
+flyweights
+are used). Also, there is an internal Entry type which
+corresponds to the type of the objects actually stored in the factory:
+Entry contains the shared value_type objects
+of flyweight as well a some internal bookkeeping information;
+also, Entry is implicitly convertible to
+const key_type&, so that factories can rely on
+key_type to look up Entries. Since
+Entry is internal to the implementation of flyweight,
+it cannot be directly referred to by the user in the configuration of
+factories. Instead, the proxy
+placeholder
+type boost::mpl::_1 can be used.
+
+
+hashed_factory
+Header:
+
+"boost/flyweight/hashed_factory.hpp"
+Syntax: hashed_factory<[Hash[,Pred[,Allocator]]]>
+Binary
+Predicate Pred, and indexed into the factory container
+using Hash, which is assumed to be a hash function,
+i.e. a
+Unary
+Function assigning to each value a hash identifier of
+type std::size_t. The Allocator parameter is
+used by the factory container for its memory allocation needs. The default
+types for these parameters are such that the expression
+
+
+
+flyweight<T,hashed_factory<> >
+
+
+
+flyweight<
+ T,
+ hashed_factory<
+ boost::hash<key_value>,
+ std::equal_to<key_value>,
+ std::allocator<boost::mpl::_1>
+ >
+>
+
key_type is the key type of the flyweight and
+boost::mpl::_1, as explained above, stands for the
+internal Entry type of the elements stored in the factory.
+Suppose we would like to configure hashed_factory for
+a std::string flyweight with
+a special hash predicate special_hash and a custom allocator
+custom_allocator; this would be specified as follows:
+
+
+
+flyweight<
+ std::string,
+ hashed_factory<
+ special_hash<std::string>,
+ std::equal_to<key_value>,
+ custom_allocator<boost::mpl::_1>
+ >
+>
+
+
+set_factory
+Header:
+
+"boost/flyweight/set_factory.hpp"
+Syntax: set_factory<[Compare[,Allocator]]>
+set_factory resorts to an std::set-like ordered
+container for the implementation of the flyweight factory.
+Compare must be a
+Strict
+Weak Ordering on the value type flyweight is
+acting upon; as is customary with STL ordered containers, two values
+are considered equivalent if none is less than the other according to Pred.
+Allocator is an allocator type passed along to the factory
+internal container for its memory-related tasks. When default parameters are
+used, the expression
+
+
+
+flyweight<T,set_factory<> >
+
+
+
+flyweight<
+ T,
+ set_factory<std::less<key_type>,std::allocator<boost::mpl::_1> >
+>
+
set_factory and
+hashed_factory:
+so, set-based lookup and insertion of values are generally slower than those based on hashing,
+but the latter can be affected by pathological worst-case scenarios with very
+poor performance.
+
+
+assoc_container_factory
+Header:
+
+"boost/flyweight/assoc_container_factory.hpp"
+Syntax: assoc_container_factory<ContainerSpecifier>
+hashed_factory and set_factory where the user
+supplies the exact type of container on which the factory is based.
+The way in which the container is specified might seem at first a little
+daunting to those unfamiliar with the
+Boost MPL Library:
+ContainerSpecifier must be an
+MPL Lambda
+Expression such that, when invoked with the
+types Entry and key_type
+explained above, it produces the type of
+a container of Entry elements satisfying the following
+requirements:
+
+
+Unique
+ Associative Container where equivalence of Entrys
+ is determined by the key_type values the entries are convertible
+ to .
+
+Let us see what a container specifier looks like with an example. +Suppose we have our own ordered container like the following: +
+ ++ ++template< + typename Elem, + typename Compare=std::less<Elem>, + typename Allocator=std::allocator<Elem> +> +class ultrafast_set +{ + ... +}; +
+Then ultrafast_set can be plugged into
+assoc_container_factory like this:
+
+ ++typedef flyweight< + std::string, + assoc_container_factory< + // MPL lambda expression follows + ultrafast_set<mpl::_1,std::less<std::string> > + > +> flyweight_string; +
+As has been explained, mpl::_1 is a so-called MPL
+placeholder standing as a "slot" to be replaced with
+Entry by the internal machinery of Boost.Flyweight.
+Note that we have not
+relied on the default argument of ultrafast_set for
+Compare and instead we have provided a fixed
+instantiation for std::string: this is so because
+requirements state that the type with which ContainerSpecifier
+will be filled in internally is convertible to const key_type&
+(here const std::string&), and it is based on
+key_type that lookup and equivalence of entries
+should be determined. On the other hand,
+the default argument for the Allocator parameter works
+just fine, as is more apparent if we write it down explicitly:
+
+ ++typedef flyweight< + std::string, + assoc_container_factory< + ultrafast_set< + mpl::_1, + std::less<std::string>, + std::allocator<mpl::_1> + > + > +> flyweight_string; +
+Each flyweight type, that is, each distinct instantiation of the class
+template flyweight, is associated with exactly one factory
+object. In most cases, how this factory object is created is of little
+importance to the user of Boost.Flyweight, but there are special
+circumstances where control of this aspect is necessary. An internal
+component called holder is in charge of instantiating the
+factory class and some other internal information; this component is
+stipulated by means of a holder specifier, static_holder
+being the default one.
+
static_holder+Header:+ +"boost/flyweight/static_holder.hpp"
+Syntax:static_holder+
+This the default holder specifier of Boost.Flyweight, and produces +holders where the unique factory lives as a local static variable of the +program. +
+ +intermodule_holder+Header:+ +"boost/flyweight/intermodule_holder.hpp"
+Syntax:intermodule_holder+
+In most C++ environments, static variables do not mix well with +dynamically loaded modules in the sense that instances of the same +static variable can be duplicated across different modules, even +though by definition the variable should be unique. In many +cases, this duplication goes unnoticed if the modules do not communicate +between each other using the affected types, but consider this +case where such communication does happen: +
+ ++ ++// module 1 + +typedef flyweight<std::string> flyweight_string; + +// produce_string is exported so that it can be dynamically +// linked + +flyweight_string produce_string() +{ + return flyweight_string("boost"); +} +
+ ++// main program + +typedef flyweight<std::string> flyweight_string; + +int main() +{ + ... // import module 1 + + flyweight_string str1=produce_string(); + flyweight_string str2("boost"); + assert(str1==str2); +} +
+In many environments, this program results in an assertion
+failure because the flyweight factory object used
+by flyweight_string as seen within module 1 is
+not the same factory object as seen within the main program: hence
+the value representations internally pointed to by str1
+and str2 will differ and will be mistakenly
+considered as not equal. Many other problems might arise
+due to factory duplication, including undefined behavior.
+
+intermodule_holder specifies a factory holder which
+is capable of avoiding the duplication problem and ensuring that
+all modules of a program are using the same factory instance.
+To fix the example above, it suffices to redefine
+flyweight_string in both modules as:
+
+ ++typedef flyweight<std::string,intermodule_holder> flyweight_string; +
+intermodule_holder is considerably more onerous than
+static_holder in terms of compilation times and
+introduces a non-negligible overhead at program start-up, so its use
+should be reserved to the situations where it is really necessary.
+
+The internal factory associated to each flyweight
+type is a shared resource and as such access to it must be properly
+synchronized in multithreaded environments. A locking policy
+specifies the synchronization mechanisms to be used for this purpose.
+
simple_locking+Header:+ +"boost/flyweight/simple_locking.hpp"
+Syntax:simple_locking+
+This is the default locking policy. It specifies the simplest native +synchronization primitives provided by the operating system, whenever +available. +
+ +no_locking+Header:+ +"boost/flyweight/no_locking.hpp"
+Syntax:no_locking+
+No synchronization is enforced so that irrestricted internal access
+to the implementation shared resources is allowed.
+Selecting no_locking results in somewhat faster execution than
+the default simple_locking, but it renders the type
+thread-unsafe, which can have catastrophic consequences.
+This policy should not be used except in single-threaded environments or
+when there is an absolute guarantee that the particular flyweight
+type will not be used in a concurrent scenario.
+
+A tracking policy controls the lifetimes of the flyweight
+objects and can act based on this information. For instance, a suitable
+tracking mechanism can determine when a given value stored in the factory
+can be safely erased because it is no longer referenced by any
+flyweight; this is precisely what the default tracking policy,
+refcounted, does.
+
refcounted+Header:+ +"boost/flyweight/refcounted.hpp"
+Syntax:refcounted+
+This tracking policy determines that values stored in the factory be
+equipped with reference counting mechanisms so that a factory entry is
+erased when the last flyweight object associated to it
+is destroyed.
+
no_tracking+Header:+ +"boost/flyweight/no_tracking.hpp"
+Syntax:no_tracking+
+No flyweight tracking is done when this policy is selected, which implies
+that the values stored in the factory remain in it until program termination.
+As compared with refcounted, no_tracking presents
+advantages and drawbacks. The benefits are:
+
no_tracking include:
+Revised November 8th 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/tutorial/extension.html b/doc/tutorial/extension.html new file mode 100644 index 0000000..b829673 --- /dev/null +++ b/doc/tutorial/extension.html @@ -0,0 +1,562 @@ + + + + + +
Boost.Flyweight Tutorial: Extending Boost.Flyweight
+Boost.Flyweight provides public interface specifications of
+its configurable aspects so that the user
+can extend the library by implementing her own components and providing them to
+instantiations of the flyweight class template.
+
+In most cases there are two types of entities involved in extending a given +aspect of Boost.Flyweight: +
flyweight
+ instantiation.
+ static_holder
+is a holder specifier which is used by flyweight to generate
+actual holder classes, in this case instantiations of the class
+template
+static_holder_class.
+Note that static_holder is a concrete type while
+static_holder_class is a class template, so a specifier can be
+seen as a convenient way to provide access to a family of related concrete
+components (the different possible instantiations of the class template):
+flyweight internally selects the particular component
+appropriate for its internal needs.
+
+
+
+In a way, factories resemble unique associative containers like std::set,
+though their expected interface is much more concise:
+
+ ++// example of a possible factory class template + +template<typename Entry,typename Key> +class custom_factory_class +{ +public: + typedef ... handle_type; + + handle_type insert(const Entry& x); + void erase(handle_type h); + const Entry& entry(handle_type h); +}; +
+Factories are parameterized by Entry and Key:
+the first is the type of the objects stored, while the second is the public
+key type on which flyweight operates (e.g. the std::string
+in flyweight<std::string> or
+flyweight<key_value<std::string,texture> >). An entry holds a
+shared value to which flyweight objects are associated as well as internal bookkeeping information, but from the
+point of view of the factory, though, the only fact known about Entry
+is that it is implicitly convertible to const Key&, and it is
+based on their associated Key that entries are to be considered
+equivalent or not. The factory insert()
+member function locates a previously stored entry whose
+associated Key is equivalent to that of the Entry
+object being passed (for some equivalence relation on Key germane to
+the factory), or stores the new entry if no equivalent one is found. A
+handle_type to the equivalent or newly inserted entry is returned;
+this handle_type is a token for further access to an entry via
+erase() and entry(). Consult the
+reference for the formal
+definition of the Factory concept.
+
+Let us see an actual example of realization of a custom factory class. Suppose
+we want to trace the different invocations by Boost.Flyweight of the
+insert() and erase() member functions: this can be
+done by using a custom factory whose member methods emit trace messages
+to the program console. We base the implementation of the repository
+functionality on a regular std::set:
+
+
+ ++template<typename Entry,typename Key> +class verbose_factory_class +{ + typedef std::set<Entry,std::less<Key> > store_type; + + store_type store; + +public: + typedef typename store_type::iterator handle_type; + + handle_type insert(const Entry& x) + { + std::pair<handle_type, bool> p=store.insert(x); + if(p.second){ /* new entry */ + std::cout<<"new: "<<(const Key&)x<<std::endl; + } + else{ /* existing entry */ + std::cout<<"hit: "<<(const Key&)x<<std::endl; + } + return p.first; + } + + void erase(handle_type h) + { + std::cout<<"del: "<<(const Key&)*h<<std::endl; + store.erase(h); + } + + const Entry& entry(handle_type h) + { + return *h; + } +}; +
+The code deserves some commentaries: +
Entry
+ and Key, as these types are provided internally by Boost.Flyweight
+ when the factory is instantiated as part of the machinery of flyeight;
+ but there is nothing to prevent us from having more template parameters for
+ finer configuration of the factory type: for instance, we could extend
+ verbose_factory_class to accept some comparison predicate rather than
+ the default std::less<Key>, or to specify the allocator
+ used by the internal std::set.
+ Entry is convertible to const Key&
+ (which is about the only property known about Entry) is
+ exploited in the specification of std::less<Key> as
+ the comparison predicate for the std::set of Entrys
+ used as the internal repository.
+ handle_type we are simply using an iterator to the
+ internal std::set.
+
+In order to plug a custom factory into the specification of a flyweight
+type, we need an associated construct called the factory specifier.
+A factory specifier is a
+Lambda
+Expression accepting the two argument types Entry
+and Key and returning the corresponding factory class:
+
+ ++// Factory specifier (metafunction class version) + +struct custom_factory_specifier +{ + template<typename Entry,Key> + struct apply + { + typedef custom_factory_class<Entry,Key> type; + } +}; + +// Factory specifier (placeholder version) + +typedef custom_factory_class< + boost::mpl::_1, + boost::mpl::_2 +> custom_factory_specifier; +
+There is one last detail: in order to implement flyweight
+free-order template
+parameter interface, it is necessary to explicitly tag a
+factory specifier as such, so that it can be distinguised from other
+types of specifiers. Boost.Flyweight provides three different mechanisms
+to do this tagging:
+
factory_marker.
+ Note that this mechanism cannot be used with placeholder expressions.
+++#include <boost/flyweight/factory_tag.hpp> + +struct custom_factory_specifier: factory_marker +{ + template<typename Entry,Key> + struct apply + { + typedef custom_factory_class<Entry,Key> type; + } +}; +
is_factory:
+++#include <boost/flyweight/factory_tag.hpp> + +struct custom_factory_specifier{}; + +namespace boost{ +namespace flyweights{ + +template<> struct is_factory<custom_factory_specifier>: boost::mpl::true_{}; + +} +} +
factory
+ construct:
+++#include <boost/flyweight/factory_tag.hpp> + +typedef flyweight< + std::string, + factory<custom_factory_specifier> +> flyweight_string; +
+Example 7 in the examples section develops
+in full the verbose_factory_class case sketched above.
+
+A holder is a class with a static member function get() giving
+access to a unique instance of a given type C:
+
+ ++// example of a possible holder class template + +template<typename C> +class custom_holder_class +{ +public: + static C& get(); +}; +
+flyweight internally uses a holder to create its associated
+factory as well as some other global data. A holder specifier is a
+Lambda
+Expression accepting the type C upon which
+the associated holder class operates:
+
+ ++// Holder specifier (metafunction class version) + +struct custom_holder_specifier +{ + template<typename C> + struct apply + { + typedef custom_holder_class<C> type; + } +}; + +// Holder specifier (placeholder version) + +typedef custom_holder_class<boost::mpl::_1> custom_factory_specifier; +
+As is the case with factory specifiers, holder
+specifiers must be tagged in order to be properly recognized when
+provided to flyweight, and there are three available mechanisms
+to do so:
+
+ ++// Alternatives for tagging a holder specifier + +#include <boost/flyweight/holder_tag.hpp> + +// 1: Have the specifier derive from holder_marker + +struct custom_holder_specifier: holder_marker +{ + ... +}; + +// 2: Specialize the is_holder class template + +namespace boost{ +namespace flyweights{ + +template<> struct is_holder<custom_holder_specifier>: boost::mpl::true_{}; + +}} + +// 3: use the holder<> wrapper when passing the specifier +// to flyweight + +typedef flyweight< + std::string, + holder<custom_holder_specifier> +> flyweight_string; +
+A custom locking policy presents the following simple interface: +
+ ++ ++// example of a custom policy + +class custom_locking +{ + typedef ... mutex_type; + typedef ... lock_type; +}; +
+where lock_type is used to acquire/release mutexes according to
+the scoped lock idiom:
+
+ ++mutex_type m; +... +{ + lock_type lk(m); // acquire the mutex + // zone of mutual exclusion, no other thread can acquire the mutex + ... +} // m released at lk destruction +
+Formal definitions for the concepts
+Mutex and
+Scoped Lock
+are given at the reference. To pass a locking policy as a template argument of
+flyweight, the class must be appropriately tagged:
+
+ ++// Alternatives for tagging a locking policy + +#include <boost/flyweight/locking_tag.hpp> + +// 1: Have the policy derive from locking_marker + +struct custom_locking: locking_marker +{ + ... +}; + +// 2: Specialize the is_locking class template + +namespace boost{ +namespace flyweights{ + +template<> struct is_locking<custom_locking>: boost::mpl::true_{}; + +}} + +// 3: use the locking<> wrapper when passing the policy +// to flyweight + +typedef flyweight< + std::string, + locking<custom_locking> +> flyweight_string; +
+Note that a locking policy is its own specifier, i.e. there is no +additional class to be passed as a proxy for the real component as is +the case with factories and holders. +
+ +
+Tracking policies contribute some type information to the process of
+definition of the internal flyweight factory, and are given access
+to that factory to allow for the implementation of the tracking
+code. A tracking policy Tracking is defined as a class with
+the following nested elements:
+
Tracking::entry_type.Tracking::handle_type.Tracking::entry_type is a
+ Lambda
+ Expression accepting two different types named
+ Value and Key such that
+ Value is implicitly convertible to
+ const Key&. The expression is expected
+ to return
+ a type implicitly convertible to both const Value&
+ and const Key&.
+ Tracking::entry_type corresponds to the actual
+ type of the entries stored into the
+ flyweight factory:
+ by allowing the tracking policy to take part on the definition
+ of this type it is possible for the policy to add internal
+ tracking information to the entry data in case this is needed.
+ If no additional information is required,
+ the tracking policy can simply return Value as its
+ Tracking::entry_type type.
+ Lambda
+ Expression Tracking::handle_type is invoked
+ with types InternalHandle and TrackingHandler
+ to produce a type Handle, which will be used as the handle
+ type of the flyweight factory.
+ TrackingHandler
+ is passed as a template argument to Tracking::handle_type
+ to offer functionality supporting the implementation of the tracking
+ code.
+ fw_t of flyweight, Tracking::entry_type
+is invoked with an internal type Value implicitly convertible
+to const fw_t::key_type& to obtain the entry type for the factory,
+which must be convertible to both const Value& and
+const fw_t::key_type&.
+Then, Tracking::handle_type is fed an internal handle
+type and a tracking policy helper to produce the factory handle type.
+The observant reader might have detected an apparent circularity:
+Tracking::handle_type produces the handle type of
+the flyweight factory, and at the same time is passed a tracking helper
+that grants access to the factory being defined!
+The solution to this riddle comes from the realization of the fact that
+TrackingHandler is an incomplete
+type by the time it is passed to Tracking::handle_type:
+only when Handle is instantiated at a later stage will this
+type be complete.
+
+
+
+In order for a tracking policy to be passed to flyweight,
+it must be tagged much in the same way as the rest of specifiers.
+
+ ++// Alternatives for tagging a tracking policy + +#include <boost/flyweight/tracking_tag.hpp> + +// 1: Have the policy derive from tracking_marker + +struct custom_tracking: tracking_marker +{ + ... +}; + +// 2: Specialize the is_tracking class template + +namespace boost{ +namespace flyweights{ + +template<> struct is_tracking<custom_tracking>: boost::mpl::true_{}; + +}} + +// 3: use the tracking<> wrapper when passing the policy +// to flyweight + +typedef flyweight< + std::string, + tracking<custom_tracking> +> flyweight_string; +
+Tracking policies are their own specifiers, that is, they are provided directly
+as template arguments to the flyweight class template.
+
Revised December 2nd 2008
+ +© Copyright 2006-2008 Joaquín M López Muñoz. +Distributed under the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +
+ + + diff --git a/doc/tutorial/flyweight_rep.png b/doc/tutorial/flyweight_rep.png new file mode 100644 index 0000000000000000000000000000000000000000..860728a09f016c5f71bfbcc99696adcc477555ef GIT binary patch literal 53631 zcmeAS@N?(olHy`uVBq!ia0y~yU|hk#!05oi#K6FicD%lVfq{Xuz$3Dlfq`2Xgc%uT z&5>YWU|>mi^mSxl*w|O|J8&`s1A{`cN02WALzOB6LqjtI!_WT=3=Jws zIc4&bbs?^QNBxBH07kp@=zif146 zmuAQObB^WCS=!W=Eg{+VLWKPq_fbZs4zA>Bm-bw$omYFm{!fP8#jl@kXPc>oo!BJW z#8G2ldr7eFfhAwUFN+^f3jTL$+T5|J&8uB=rg?Ssk0+OfTUR`udb*q{VIK+kQ*u z<&@-@OfyMKPGeQuH-CS ^%J2&rT5{@ z^AnGrJyrScYU{Pfwhj+p{=2N3qQe%NaJRbte0Asd#?$O?w!HnO_HN~dwIWZ2{{DM? z`}_U-b^l%WTl|oJ^ytC*15OtrFIIYgnU;3{+4lJRY %KD4>Ru9CkW4) z+9~Ssu42RP_g|*)-?!UVx%KFaqdco7RQ>*&`{C3B@9+9YqmKK0nsMg%x!+Hpi!W*q z{8#()=gij5rN{QUd50LM)@EJl(pVRv7jJt1qEn&@L&ABtNQar7y)W;z|Gr;eo1T`m zYz6OE`75>FXFa>TJJT4}&-(FOUW?C6Y>LMJfB)S(MIAox|NHabkJG|zVQiLhJNN%@ zzr8Vpfzf4a6Msa}*^jf8k7{4tv)Zz_JaLcTl>L*$?XCZ>-@pIg!-(5w80NiY5@UDR z%WrQVRa1NPTy*tKiyLAsx*ga1<#UovW=0$9-`O|e;RL&jwM-k#SjAE%usH2e-2d;- zpTFP3CsZvcm{M@z(}N?Xoz;`Szv_P7KaKma9D}uqP>fK~pQ7(}cYc3)t-Rsu_vxD$ zEfS{v`uA3PL&dN24LhE(ZTc^M>7;7CzJW^ezw{p;e;+^G-#_E#4ACE=|NhA4ZQjMe z=<>9*)3?v}^rGpUMVx0XHyY&n|63LP+V0< =ZvD(1y@2`^omk+nkmy55j|NLjQ_y2wW#Knd6 zH*20ZJ1_QBNH!<2 ue21iEIxPc zW@Y;GiR$WocPtrqEt=fRSQV&laOz-s$Hq?^qE1FuudY1)>bTcuFaC1|S$0?cd^~)- zY=4-s=j48S`=rprCpn|me0Y9dK8@|z{rz>qQde2NDen`XDVno$ dHMwd`K#V`1&-+%X&?A=b9Gk!jbS?$Utcd @Nm|hK!0AP0Kpc)>r56_Wu2J`gDI=+tRmRzy6kg_xBI?EjQ*jKef2E zUU$E~ed@LrrzY>Sua5lob}_Y;TcZg#X{ji;S1|p64@D;I-V^*yTNd?q_-sV zMFHLguHxeVfBaBBaB2SRsnQW25;+~iritsvsT^BUQ}N&Ne8BtH<`?ER9*H@}^v0-K zyV~EvsPNs-ogaUFT iPON^{?)&oXcM(-SyJ&!lK>YyamDf@EsqyGGT zY1U6XK6*Izg-fI{C^XD)3s$KscyN%>%+AbSbbkH<&ffhmUfhr8i@O4s *-U6bH|fJY+uL_V26t^-$Lk)t##KfB$yw z+%>ne_2sFj+R^5XWl1sn%WBWw+G_v*kJ_hcFYf=ZE8bMFOLK?8pA?JFU(RG?@K=9$ zuAgBVKOyVF?(+BjcGiq I(R4~Bmj=Fd8P;?v*z;b&Any!^a;LWe`5;KCy( zt}N@T;?ECHSo-t#`TPrwk~%sE;tYg^FR#D9|KRe}1W$+k|NcyQIyEsReb4uQ?=mv@ zH_kbH p7%a_U$W)8X@Vwx2aimT zk6*WM@2^L1x!w5O)WVj{6o2-rD aAa2vJoqrW;EF-;c>~2 z+>5Qu;OUbOj2>MntaHsjKbP;@Eb+gg@9 #~{Ilz#!u~uo@OT{yqGAm?f zteVfI!{}W;?c3a$HYYc-moPka$Zumci*39+OH(}UPGyhW!oogi27Xzm7o?15SJUsu|7-StEz!ZJcOs52tgrX`w!9s? zjs_guaB2_N44wA-i&9e _v=e4y$}oD%-5H=?rI|F;5m|H+Jv#J-sr0`Lo!FHEIhU zDI|aY`u%#NR`!kvOMYA1OO=cICpT|CyRLWsT=^pB69#Myj3&2^9AJO*^ljyj%J+49 zzVp`9tDW1b`CnnF(hmg%x3=Z}^O!2r{pWr!=lZzen{WTgf8V}JYfGPwe-a#ia94!H zjP-kV9{Mo(pwF!wlP&FH47|UM{`@@s;rsXh_wE1xW0{s3AsE2b*WfC*r{>Sc@9j40 z|Abs$`R2@<()MV6TgyX754kQ){?al3C@W*0*gEd@`pkDf{C+?GrQGGWsc+hASF*zj z5+)fYsYO!e_wW7s@>zIC%He$mPx*hGT5Vrn*Uazs`~Tb9-@m`V_x|QK^VJ$_^TWh9 zOwjhx|NW1* RfJSBSDzIia;ud6SfTt54$>FekB<9pq2_uE(> zJA3LRhh=ELUdKenh*yR4s%sa%^Y}bnK3;97KUXlnH0M$|0S3@)WjjB^Zrxo;R`U1i zZT`Js(Kdb^e (Tai)Aj{AoP2O=dVG9SX!G)FpWpwUyWecQ z^|gFkfx%`&V*}^k0$Z|Q|7DInbL`}yjju26TT~mWZ@2eV!Ap-zo>B+inayPc7b+|c zJ$XFL+=ri4-~Qha{`~J@hW&MaMW%~*p6Qu7ZR($|pP#?C-{0!Ox^stxrRB~2_3Ya| z?<@LTv~km>1~Ic{aoxiQ;>*;oDf{==R{gj1)A#EL&M;Zwo^w-tz3l!o^^)?|@y9Rj z`xadLzP|23!m|do$^WmoB^)?*giR$qJ}#ho2WteM-{)D=vS)DTH1b`4dTIs(Xinem z(BYa}Mz@a3&D-(mn-;h5=llQb4a)BR`~CIz_v8KY^?%vsn*RO$U7u64P}ceF%fFlN z@2&m+`uh5%pTi43e^`;B_5a6L;dR^@pPt9ZU%P+r$bn-5OGHhi&MEJC^Evoi^vnH0 zkuH^=r@md N=-&7LiqiyF))<`_|NVBWZSUE bmBfB*8nQE0GQ^}leDef_^b$v>utZj^ zt-0X)jezYpOuer?4^8{P_2J*YztXGT+>hr=`?j~AFQ%sCrq{_+5k*_(TVFc8&wsz& zO^@3-d^g{$eqR0j>fS}op|9WP{rvo%Uwy}q<~w|ToJ?yY6H}ffJY<+uE%#pABB1ck zy-e%S-MR~-_lWGZ`}fDfscJ_7s318V$-$fuUh?zl(`vo + z&Ghg|Gq)C9dRTjWf6c$7wA3}r*8X~X+x*8{jyGrC^xNCdEKKAxmM&f&t* U_bm;WP8=)A#@Xn{ZK-by|te1K-m4 zx_aC1uO98%J=@RL>hys#6H=#!=7;j<{N+6zyqtpxTt9ShD637Ayl4C-?$%k$-2sjM z_k!=e{hZH~`SkYp_cniQ7$@F4kn;WY_56gD-{nh;#a>)eGE3X`zJ8y%Tz>tRPlc27 zFDLAFDi+~U0Cnq(m5W;|TNlrHykzrav%5D!{;!FR3Ao~R>XPd<$BF*)Cbu$&F#Y?> zywjT_+mgfHmhnbpVrtUQuivkKUa$Yn)~@8i%Zcpya*V6v#6zbuU1q9|tWG%p`1pPK z!wKek2N%g-?gzI`1X-uFP7!C-FPi`UUge(t$ulp9o%*Dm;=@ut>*rVLh(~{&USDFU z-T|7Ew!T{Y>)cJ2+bvPIZ(ZCV%X;F0{)+|2 zfJw^y>GMibEB%O zC(m24ZQX`x`#So%8id%5**DxjaOA`f@tXNh_W%Ds(_yxY!}9$Mrw_~5*E3z*y#9Qn z?;h{`^>J0jPoGbZKNE9i-m7^VZ*83CHP2!1f|Wgj*Y>)C%IK&Mg4}aN=ccos^@^Mx zRJvi`1(VzE7nx_=sQdSubxo-xXWT}{b#;xMUoSpC9-FQ 2!U z4}zN_`1!$0Qu XO$`6tM#(#c)eQ!RmySZ_$ctTe7 z-@j)UE_WA?i~aTQ@o}c x`rU7WjfF+=>v@A6T9GC49|biPWJO1oEE|NTq%W%YS; zcYJ(zY%YJf-ku#iTGu5Oh;M7+3u;{An8sG-$Cp~CDlKW9otYi%8EndE>d1G{{{Mf6 zvKbfF_n&0h6(HteWN6%c!^cNK=!!$@oXTJPCcYdg&EjI0EGrm63zL+2S+d!>k9CK= zS#_hR^!T^myKO}8)`;Al`a`sFt>p)=>Gc~D#S@k{3l{Y~e*HfGyRF@hcjuOc=ilq- z?DFNEIV*LRsnn#!4JRJ)Z@Syt+vyt~a^2;UN+kc5FHbJ7_uqd1{=QGYPR)LwU-#$l z$EVlB{lfV2#PaOS>`D_$6HfEp5PFjG6jTySE7|}5-@i}4x#x0OUr6ENHkOuFHvarx zKmK3M|9_tkx36Cx_qXQ1+=(9_|Jercwl7fF-M}DF*xk92n`=`+!G@2Ym} YgIq6L=+>w?gL`u|Mc4gm)b9Z@=@sdQ5J zrOtDnUCTcG-*5l_AA_}+z_<7H`x{kdTRy&4m%Uwn=F9;lo{XQ Nyzbt%=-YjEc4gl_vIg`w3(72gP$<=Af59V6EV%D# zxwny*(L^Ij-d%wXmC^!5E4epp_ & zKU3`aqX!kAKUg3utNZ-L2~C|p&yV-l*Z;e4;w-pqnaHnFp|UDxb%W}Iujbd!+u8np zb$hk?{P{M&K5gVwK)LYr`yw=`)tCP}{JFxyZdaTjat96PX1;{8#?4m|fUcV`-gv zX(=oBy!icmImb06W=B+2orsF^l9b-~`6Kh}#vNg27>c Gkr^6}@*&*fz8>&kC$ zn{8bdbAO|=lc%S-qNBZTZ*Twm^X9X1mN0{BxS#_^PAt(~#p|-z(U8kcBq1%GeOktW zZ+APtecP?>x53!p1v|@(v^0$y)*Ci#cHU*nb761Mf5oFRMV*`rBP?!2TBwHX-P3Sl z-L6;v?$thjc6Rr^yu0Ui?TVH*j|vOZs;v0KT=uCxaOZhGP{S9rSZr~_lWB6&`tOfh zm-Ah}8XLR&n>=%KW=e}|-M%$DEd$=Yzkk2J?r+M0+Z=!Uw;n%pYSFW|ye{X@&$qVB z{IrslJFLCsMQi(Q6SGg6TL1q2nc3xQ-@qUh8)yhxoT#w$LULN#(?`2DCmzm{pI@AP zT`MBuNPdL;rks^)cCv2R^ZW1T+yCwB9jd$k|5q3M`}_NI{(#@} z3ywE@ukwsh@^M)q>A>(sG-J_^>+Ap5|2NvWiOGBZ op)liurE LS8`Zrcio7(7j>q3CK+OvD-=1wnv8)R(C8@0>BCG_v}>;60w9-A*{RoR+7 zJ%0bcfB$rI^ThU69^Q51N3i#_Kfk`5%>7l8AS@m6EilQ*f#Kq+2fsf(Iy!TvvA%x% zzc0^x6{b(`_P%uC!hr(~;Zd{PeR^7(lTDuYG%dXM==SFH^S}A!Q&Lk)OG^s2{n%jY z;F0^0H|6~F&X>=BfB# #RR64Wo>IDftT8;@V&Lizdj2c)XD#Q1#vfB*b> z`JJk&hpw%;HECjL*4th6`|B$8<9Kq~|1C~fC9cr0BL2hQf4{y)_@7@n(>UI){@m$9 zNiuSI0^;hqS*J9WjNb4ckYrWnlKJ=X@%zc@f7sY=$;s7s$~{(2bLn58SfcwYB9gbb z$gI6R{>~=X)91_*9@>cMt&5M}*Cc!NghRh-%m2ouT+;IE@6VZe;NH8Vvep*dTrtc1 zQd5$Ys_Ug47y@@bs4jha>e-nytKIuLK79UdW}4aF)|Jr{CvMHvrpR!2(x1odhu6pd z-&g r-yk$ &@Ms5d&z~v8i z@6Nt{>q$yk-n$L0+@{UV?!jjgnC ud%TzFRwduuE>U>!nv ^V)22);Dm=N#`}DMzYU 2h6v&Yg+z1y4c;2TfA>t zRWt9NorTHSdGDUuR=c>g{M~I8x_1w&$dj8pDi@?a+Q0um$PrBq4F&b}T(`LX{r=o7 zu757x#xnEKNm1>*_jg|2yqY>=l95!8varF%4RhvNd_4R5x}NLLL$9(HWZr4CW@F?1 z^Y3GGc+{;$2Iax2cUQj4TkqPf5MC*m&*Z=m81*4GX2XLo&(2OuOspy_T%4I2&=@`2 zY^}F%?dMlh3mf)Fr!U_y%j$*R=llEX|Nmgyr_QpHt96Y^a>}EL0^#3327W9Eem8%^ z?p B%TU~6`u)ZYyL-FKOHT>~CMNz}5|Qxxnf;1cGCGqJ4t)B4 zef#>ThYO#lo!z~&aPiBf-ivnaQZfC-z|^tGnQPy^s?r5ZT^nZK-nls0WG# R8a&CG0)4Tpe(kI1ui%l+riQd8yB>XTb;*Ky#gsP?sG z%QqJlw&_RbgvRjXJpKFYlVr%fzkh$ryF7ArDBWN8@5h(8dk6KB-fD?5|DBwDJ cLyCl^W@1aQ}$P{-4B-uO!a+m`g8`%jU7AZ z%h~;TaY@ybUp&!N)T8*l{S%Lijx7$oc5$AZb60FI&%b}~?v~D}v!(l&Ul3c(#3JyM z_5NXHce(i8%icXasIP53OMw0KjpP6SS8HqA*Zg}E61?)+^S^)ZMkjc`wO?WUyWNH5 z71RGee>%gXE|q^P;nY0&_wC#B=jKTAJ`@aEW_M%jYklVCJ 1>TttIPOe-`Xu4`v&RpgJ!9d2t9Wvek_RV;iHl3{-F3SjUH|{Teby|t zqL;5U7H~6|_3hhRS6hFaFX6N2p#u-jf0uvo>}ovM1$(!)|02>&cT*CwLp*$49ekd= z+?X6c&n`7BP3_Wb21b{(gY0j3ZEXvkCbYX>Jeg^-mS^6xdmrz`DI8w4Xh!Sg$NU@G zf4|pXGMW43bEc^blON0~l|FSkyGq^XAFI`k=k_s-VJte5^)){h$h}y-VtH}u=V$55 z{rlTK8|eRth>VZl`RUz3=0M-lILnv&_t*bbmu~pA`2WqD5&!@E bPfugC z-oMz #njc=m3Pcqs>d+v$M@_0 ziM*i+-tW&|)!x6qUj0)<8YH0pebe3W_=v8O(JVhU&L3ZYe*XIPHS3NYSD0$*YyY>| z1#)YBsf*uP_V?~@{-8-h_Vs@l%)U1i@=HBdPMRlvBm3FZi6I3WDt~`^db(TO-MRXP zSk;*``}fz?7XP(kXKvmw?U($8gcJ5ff-3@ceSe_1;mx0)udi>FHs4qG``C#e^O=}8 zDkt>2ICwVBp8fjj(eC|rwMj~Ob<7G4Cv<<@+spi`p?7}0$yy$v5C87%UH|@GXV$7h zPHx864qIk&skQx|Z&%CH=eMu$^|y lbg_58b*g}?Sx2fUe@ zZGIyna^=1?tTXH)cUb&LS!!=v_2u_*ex>Ld21b{6(@ZzKe6wfIV$ReF4gUV~nY>jU zLsQfEuG!z;x7c^zzn|Z`yC)|f-Lo>2^(j+LjjgV!Q&Us?fyWI8q$W+BI(6nu&Kui) zvwFSwzd>!T{r&x&uU|7sGo3%*r k}< zJrk;BPT#lZ(K&O*_Uu(p%hW%A_SpJ{3MDPFcZ|OOkMV6% zGOL#nGxOW)>+k !?H>(&|2}?v+|JG) z7qjOC&l%4!ZY< zX^GbKJow76->&}O_vic@_Ar~xva_!}q^WY^0E^wKX?Hf?{cre(T`c$1r%e}BmaqKv z_BMaQDVDH}XMB35ufJRI;YH_;6&;Q1!~&k2m-jkX%gAai%&k3v>Fz$am< U zV#>d__j_^qyX=*W%jVQeeEsu(KTpq@o;!<_`1|GG3yQ02UT>TxJE1P*NrIl5qH^e& z2hKe5h6Xkg3g!WZFD5*eexaWjnR(%(|AC0*4;btccK+nP(a+$Y`RC8a&-d@|Uoi9T z{{P*tqp#=RRn<7~|J&VaYwydMwr6GDyg88KnRfF#{}z^Z*%_Y>D&KWk=F`BS6!3x3 z`$BiO`23l+zYneqKETFmuB^O&&z_#0miy<(2y=69aQO4%!^79J&F9UtDE$26<6c?W zOHol`m6dIroM+y=S==gU%cxwTB!9)9alPZgs9%5D??&-QE&l#qU)=ook(sB>-rm@q zc(KIU_H6R ztGjTqbCG@bmuH;T^7j+=_3}$} zeSd%d|HsGd^78X%%#ir_HqvO<3YE;nwB%&< @OAfh6&&65bhG-6s97<)ij0lcCLG*!wRPsn_ Z0MMYhgBz9<7Oq@i`OF z`~P3lnm@mOeO-LmeO>;Ig3`lnqLPxr3|-G;+OkB4dlqBIySYo~N^vR@nb*UKe^S z?vtT|Sa9R?AB?lBF9{|6^%V_U# #Jg`pJVA1~l)%yAQ z^4rVxW!Jrwc+#wXpegaymdyF{?51A2TwGi%{lm~sLVQc-^wu*Qs}sV-Bg*&ftND4i z_w@m 8t7KlR+gVDQT(y&wu!S zU;e|TYy0aTEZbC2#G#?Zp{e=$_)$BXiVs)5ynG(Ec3y7&` o>4~?ifAsK~UHQjTTcgV_KXR(C z`Ty ^X~uTe8aMGrJ>+n!wd=L7eB;T=rZ57>UtX&%chifQB|iYl(FxS z;HHw!GpE{S{=EPG{(^IN*2gc{cJF_^$XwI^{88Wj{`%YAJC*f@-tTX3XD{A5bM@Y< z)7RhMvD0GR{av+RuI$|OM#L*Euwh%ntoZnSOp{fmFJ4~Dz<8v|)Q#WFEH6LnQBpep zi? bjCvaMy*+a_YTJ0yXSZFY^i(Z&slTk%%5*}GbF4pO~{IYQB}1p z?b-J0`G0?XYVGV5jc`s}S^xj<|9^jvm!Fe=u!?(2E60rp_Ovj$ygv4R`Q@TlBWJ#- zwz^sKmrdtKz-ixvc}YnXb b&owb2@z39{(igVIBrQ5{PGe$hYy-ogpbu}?ue^Eho}Qw QYl?@vG@>ZuXn7#bJ;5BPl zrv3h!%Cb)~?99zaUf8-E-eL0p_w>2b!y~+3A8TaJ&%YyPe(CS`$MQEnuYDl(Fy1dn zWW%n#HE*AN7GM8$>y`R{|Bs(N`t!lX$w&TVEts{`c-PIF5%vFmxo*?-@#&G5xB2zy z<>pySMK_#K`_xs~z~H3+W8J!#oy+&ehB(<2z4hW$J}fVP-`P)1eW}_X?klQ!E9CcO z{WSXjKzHiYCNHn3#KgrbyOuS~oO{>AxHz})<6B?r23C2CUxBOaPArHGjN%FSaeaL} z&jRU%k~?--^v|=I9d#=uJ@L- pyHxlVeR>g?SHiH% z&`_zV>3i9m|6iU+mP}V=`}Ozr_4oJpD=go(VUt1Zt}^A7D-`cs-@4pPUgPV-$II{A z*BdUF7sd6+$l%V+EuW9qvmKr?<>GJiHuE3fe{WyV#U;&r`1NY@y4t^=<~Q+fdvH1O zALD6L1?dPI#%|3pv1h;g=N(B4zY#g(%$t?ziD|z+JXBsG#Wt<_|A~zpPj=nkA0NAm zP3MHmmaYGV@5ZxnC`=K%zp$;1KYrhjt!7#~cmMnGp^x!*lY(1Bpf|&xA0Ohb9Xqsm zb**dAt%uXDec*lV9xUq1xP8V9$C^L$IECfjSuT;4{vy8RqL4Q)=li`zf70h|%sBhI ze?H$li|zk^+}yt)HH^njZ`;q#hxZ#Z1 08||+fyDBp`bH= 2g^>=-y$1DF!?rZKV`g}QN!;3@5c-3~7YwzCGr=~ND>GGR5 zla142B6s;6ygIFS{`2?xb2e8WsC&0Pzy43mnzcQp4Qm~KOxV!X^yYlL{ln9d4#z)k zGPobL`Rm=|=jB92_XGtkZnQZQT~q)6lZV$SYwP*(8y~&hI(v`Nqyvj4XnkMJ=-r)v zf1l;e8JVlJn10J15K}x7U@3XhzW)Co_4KD_>ea%Yo%f%=xBg4WsT(u= ++>5fBtqKuzA0~zJBJ- z8$HEFc`{Z-dDqrlZ0f(};Ng07*@lAZ>fbjtKcCe$QheT%w06FN>fGj5#aj!vE?9l7 z?r%y;OPtloLuV`VZ{Pdeecj8$dv>p$pA_eV|MhNSVyxlUH*1IOu_%u`c$aaLSiclo zvm1A=4{t zPR_EZ@^)QbVdC5+1}A=g+97o1d;a}`BhnUy9|~-(>%Mv17Eu$s_TXrDWon9Bck4uk zy87SCUOZD%)2%IkUN`rMftlUC|5tV=zdzpp-u}Z9%S*d<`t09VvvX^qy1Dl3sDcmj zi+=BPU;z8*;zZ^9|Nma*_{M(igS~z2v5?~j50<|>)Ovq^z5KnM7f(36|J^UYV5#|$ zEpz@GZutCZ#gB@2FXxt2|1P_{W{sot{R5Z&e_I#(z3knd?d9*c?_Y3EPFTn7U$ePE zk)g4+rM33X-K(oBJ2NtP{Ngth6|(9@eK~pc>ytmG`}fse_vcJcN&EG^`-tdTpPn^$ z=J@l?vAAUQ_4@hy2aVIUEUfybaVRf6`~5k8|CA|ZC13unTetl9xmCY*|5Yk?HSuNS zwwB(IR9E$NSzX488SC_3hy*QSbYO7g-dDaopE)vVS5$p{&Hw-3`K#{F>HK)>YPNaa z-NdA%s^9PK_C{}ilA78v-{I%)&&zw3YHBkdUa)uL!^el)kE{FHSXO=#PncqCbZW+I zZwW~*&D9?mZ(A%lT=}oA>d%wEe}A9%o4;#w_VuN)yDPL;pV_o|O?P+Y(xvPR%UMK3 zlD6kY3kw(LJ-zmR{e8(4*Wyl&e~fR1{<~)s3T5o(H@22GD0;u {|L^ZV_|!$sZGrN7y-04KvsRp(Jrfqc z?POzMJi_$x$zI#)ofZ}qsZ0MilTz|@!FMBp`o;qi4!`>({Go5yw zKR *k=P8HZytiD;z5Z`5Dzd#*{^j1s&0K0~M%S*Xn0{|y zP-t*qy?@<2KQ4Bk&Z4DkZ|*kmoBnFQbuanH*U$dt!Bdy)@c8~@ruB6_F>%qqpMR@w zxWMeSapT5~n>YUZ%O17%^Y{1mGJSuJpE|3$XyLafM`q^d-}}HjYoStt;08_8uP6Te z+#@5yDEoF< VUwJzZ!2=F0V5>o+fHwQE<97uM!}ajWu%nDzzF z(EauQfBt)Y_h9@ zp8g!>6+L^y=Z}95-?(HPASpiMZe884n! _6)k9Az+i7Ku0BEg#QD>Q&;Asyc_6>x% MFE3;6wx4%XyXA_=PF0s3F0D6zRyK4pm0bC8y8M3J{=XIfPquHDZ@joR zDK#}TV#T}X?tfBi7bpCfT6-WwH$8p1tWZtX(XQ3p;j=}SGG)78n90g~`yI3Sj))sK z*XcxjeeujTHauKZUOu6i|5EnB;13HqnOFo?Jox?ht@H~fUOByl%zyHG%4NMZ{`_OT zcj)AY-{18UiuqUc&F!sy_{o%auJBaRC-?UK-%#^**-hbFKPoEzCCte<@}qy^#F?$F z&9WKq-mi~m_uO=2(nLm`uRVWjYSR6liwXb#_|uj%lSx9%zW&6m2)F#3TU3=l-{=y} zxpQY<@iU%tJPNDUzO_tjs>^w^_3LZ-celU)`O_MH{r%sz-fxX&e;JbJPxN$%yLZMs z;OJRjZHtO6C!|>y%)51eznG}#_wVoa?#kQUvv%f=iQnG8x0m?GpR@SAJvaOD)!gCw zdN&nRl#;GrW3$@V^W~#^14B~dm(GU2Tk_Z6|HJrw@ LuE zvaxCj&4MMR!q(Q$6?RPg^zGYQ>sPOym|n46?#ps_IeXOf!yfmT&c?^@XQ *t48xtmXT+&yw6K7QYXbP)%J#;{e4(TQH?&dJN!{`~h& zc1P}ac5bG*b|3g}oNFxj-{iIZzZTb~H*3ythv#wl>k5eqc6|8$ %ku zySLci$G<<=A)!Hg 9nf8s^|xK^Zo ze|`OYynX$>`X3#wtuOEX?iXJdlg@U3THiA92-iLPN}irRe}B`oY0P%NE31Bex&8e7 z>z|d{yR82H`N0@bu~77Ky`#8YBcIokXOB|SlK%XDZvJf+L)!uOi+iqjc3vqi-dXnM z#>>phZpEiHB>Wg6rWbQ@{?v<9TD`Wo@NFOW<>`#v&Te99A13yFad2SJah8yf)IS$d zR(RJh()Vn`RPCKdj{cvtDU_L)0hD-zFJ55y_Ek>a;unAPN_Lm~`|FL(ujqO7rm@Q^ z{{1cgBO=SMEKJRm#rI`iY*-R8yOI`8J2y#4fa^%|Ws{|j3 Q-- z-CeGll$QGM^W) HSR za$cJ6YI*$a?eFgoG_6 fO&zUs$=-t$*yfSij}vy`~=4sE?lyFJJssSpU79((1Lc+fq00 zXgru-)c)-OgF?dsR{8BacDN-R*nK`;{>=@3DMyy+ks7kQ_Sf%kes|*+!yC?~%~mfs z4y?=MUZxlE^ZEJr)7QtJkJ#hp?i<-KT{=QXsg(J@(ME&2cXxe1d_6xZabb>)4YPD* zKc76CSzl=F-WfL{{{1_4shw4<__= %(`n|jobh3z0;s-{coM#n&v;VJ7)$S zJo!(2!^4^{FTjV7@EB~~R9Exi!NEr6Lz5=4DJq^{fA? -2l>Spe*|JJo`clC)j-b1ry?O(A`g-@YD;CRr@u+Z4M8y^1o zGd1X%+5+bt4u8| ;JXPzxQ?i z#Hn9jpAI(<%YXR%p`o9@{DZ_>_5bDMY&hOL?|Pm6|KGp2_S O&4TZOkc*-ovMtz2^C-@iYsZ{GZ6*Zr(qzmeO;dV@;pvxMKj{+ylbTfKhmt#c=i zyt#HGBQA(3XL4Y+qJaa0BA1 3TiG=_e=Myq27vo|w4PApP9KK0d~{ zxaKck_SgTPB`VIHo$= yQ6`nZ9x?G=C-Zp5a95j-CTb zeUdk%e}8>>`tRKKb-H)|e0%H8Sy}nLQ#kbR-^QQc&-cq;>9T8JaI&nrXJh~W)~-@> z{&`Pk@4cg-{Otbs_y7O>I~;3fT=V~5xU*-Ne=O$<&jjc9$G_(nXkOSRWY#CGZ9X@C zV`O0T?M+)!4g|kC&o5tB_eW*f>XT>A{o*>)coJ{_}Ky#kzS1JaS*pj+!?;F*A)N z=Kz!Srvoj$VV!?j;{)5oR{#HRuz~x-$Byp0nlC?|AMamt=g#7#hYi>kXs)rZcMzZS z=O6DHclJL{g-xvTmP-|0WGt#*uNSvxTg}gtn!z_LEcd3L6}otdDOd3KN9U8LwWFLL zUuoeK7uT^dOFp{x>+Y#GZ ~=ml)xgJ@madK`P6><`YEC*U$;84zm -3g2HGx%qS3ww}e?^Z2tFeC+4n+mrbJ z^K(x2 iKTNsJty6gK-fs4siyoSqJHLNrjE=j&YtO^8e}B!x zsS^ulo19cI{rYE3 _XxA&JS*I3RW&Js|Nk=B !I8Y$A6#yUO%qgA0NN-@4H)buTRgf`+fYt`P1q1rlic7s+yPb<^A`3kMGlV z-F9VQ;`m$TDOg&%KlAc5cb7Rc_Plt#db`y}$1`to>gy+M(3sxYD|%zbu3ZUlZ(3?w z?%e4trY|NZBQEss|L5oS6 S4w8v++4Kd$GyI};^JrO zi(Xwh%xxYivF#)0$B(BxKc49LeA26ki2(d5z7S2ST5J- DEWD7#R(-udnm; zvDsbp`nGg=Z*ekf$dmv7ez4x#+V=L_>Hi65SYFL%_C9&uKW@(aU0=?1ayuP5B)B?^ zci!aco2)DixdsXO-`~HN@2Gx%QL6U;zdy|PHCg9{GwG(KrKO$m?*9GlFZ+hI#}A#l zbi*{##Umgv?$veufcI=$%Tk!!o-cRz|LN*ttQRNo_7z|Lhv(nl?<{`4ui~SVxA*Cb z7bh-@jb$_1waaBk%ZY_PVS0LHb^q+`>k{tEfBSEr@uGL)#OR<)8ph7rMvtDha-Tmx zr?Te7h0BlC $Q75?p{5dH%i&J%|5U; z%%xxxgS69{|8;-=efsqEa`W>1HTVC%{HtCYAOHNA8>jQ(gOh_SSy>}3ciP!heEEGk zJgw~8{~OD6{xyrgYfyf!KjH8D^Z5yt&y$iFbB=#3D9pRH_5b1a^PknWd@3xiJ-)-T zVPfag=ga-$ |P+; zA|ElMh=c2swiegQRi93uF6Q81aBWEC&M=p7+`WR4fvJOaddrM;c}HfMdR<=jHb2X5 zbIRqdOJ}R}Y*9T^()hPbFVRs}g^6`hB(kzy12V{I~!7`TM?nI`nmY{Xfp`E>{27 z24~O6 wS|54)x)nJu zyZOIgz8diJhotw_<3~<>dU){g{QY&@r?h*fRUMZ5!+bu6L73~sr?2+)ZG{Kjqcneh ze?R~I{r?Ter!=@qPhYWOODpS!uOH;Mh_HlMG!=1aYYX2nRS- ;)O`5t p iD^X2K&)AMs~efe~0X+OVQUHxx1BWK+O3r?+9&|kQIy`X@? zs->(}7it8UEv2Q?*xHt>pIh-`SCjetKgT6@{8Uvske{EabKp2{inEv{^W)do&&!{_ zo?pJvbh?P>FD|QYLFHQ#lK=nx`~2#XsTvzwrt#*BCH7oyjMMBsFDT@8VAv?IlJADq zR1+g6B@bo!x*tNu($d1*rvo@EE32yuC2#(&?tih|j%}^!|JJ$yyA2c9tzKxde!HB6 z`-0#02akW*xM{<^+7B-tU5$>Md$s=GpMTuDmWh2W`S*VTukHN#w!8QL?`YZ5v#R}g zhLSw%;sgWc1_q^cDY=MC8;c4ZF9>LTZD{D79j&eN<#GS~?bq@Wlh|aBH9S4Scw%St z17!|HC5P7O>+e5%`tV9%@Sl&5kJtAI%SbpZ|6&juz3JQC AP%6 >PXkZFq4cLuQW8^H-}WRVNy#>(F>okMoA@m8 z^rJ`K+F>z~TU`&{^$Uo6_? ;i311&01Uh_~*;d z_1E9hQPj+g46&HC%8haFDeHv9;~Y9~{{8v+{pDr-g!-o!7yF-|UtjnA-Sg|~_uJR} z`1$+0y`Z3 dTxDflfg*>Cj(z>VMxT>CXBIGXED#L;@X^cD($s97hp(-D z?Z((>$sfBu?Eevdh&j6Qm{pdSkx _Gt}owsSIE_sGu&KM(y}fw(Jx{BwE6R$9U7Wjr@lP< zI{*H@zyJRHynbDOzntx#g1^UR_e!tNzf;lJ+IswCprVD#;iImOj*apWZU6rKEPukH z?7+}_q^W;m=gI6_Tf(oen;W^MueCYW_|)`?jUVOjB~RX8`@1dU@j(fnoHY)M*XcwQ zSN^UGO-?@FDJ @eV3==okvRbb$^sTy%S2%?w{HG_x0rI z)5X{A-uSy?(Zs+HmzJ(!u08WgL*f|2jgH^{{+>TI)%@12zPmd vL*_LKl~T3*lsb;PFq-++1PsX(w%Fz?#%IB{J8URf4I)xExE7XY6kP^$CoW# zbH~aq&Oc_(>)YGy|F;KT7tcP(F5sY}Y^KV&a^=z7`}@?aetkN(SN~l6<<5`OH=3tD ze#SI+IfvC}DR%{?>ODC*_51!;Z20|R>CfzY`+l!@{6Jt=66^LIKQf-lux?@S@#)#K zC_tPFw$e{i`drVP@+-MV-G=k}R27m+ 3lh%Gx36cm-hbF4gpsLZ=fZ`FN7OXU KaJW|g zzoGZv@8jq9+tqe NeKnOe53bxi*}XpR z=J_A5`=e|3exIT#)6}&1XYrE8`v3ptS{6V1{QUf*M~_aOI`u(KsC4_9jT;O$ZZteU zzg|@}_4M9q{kwa=KGWsx?7fuy{PB;E@89?D|NrkFv#id~{>BuRD?dKpw@*+#fADNt zs?JmCWoHkZ*p>ZzZRqNCjC+%kl~12K_2>Khf{gEs%)%1Z;p<{5zrUN?(b3V^$QZx^ z%2ySiQXfApefc7wsLbxhwX)v_8pUltw;XLb!fX0dWJ%?cBbKc{6_0z?SAYKZy4!rd zoLz0 #eapbA(0bbi<3=x3~NI%>nhge_NOD-(Ssb zDL30YWJ-KoTu5;6wM&zn9TH4-xH0-Nva^3a-oM|-s4VYh(M{U}8Mh8O)Y `(2h+rmg3gIOXyDo4b$S{(bw@)Ptu^HQnFOzkT&0M$nEyXZDywhqCV7 z<8d(Iay!WIwV?jbkJ(eVR-c~6J+Cn>csk3*VxxK|neV>03%9Sg|G!~_!PW0Oiau}p zYMRsCdv`-&@{A4+^$VVdH79_LlV)AquttyR%RCtlC!HAz =(=& zkfXtwHWl%`h%>mrI{D0*oo5KPDJ|;#X>wIs5bTb9q0%YumO>K0SSY*wtzC_u2Ub z^t|S-scP&CUb7%~4-e1TV4M5<{%uRX{BoJv#d~qn_t)3+&3; _z@0XX)pP#pX8Gf_3DbPXY$M^U5=UT__uTQ_d{r%4_QELf_4dOLNOywkh z{Jg27v&7a`R6nlf?)B=Jn5djXpAsZp51zkY&pU +AQk^V!TWetzcL+wy%4>r8*L&W@DT{pxn`0ZYz RFkYb)2`ym(>ik3VnOr|sVRf@9y`AG%8C?pmByznJQn_pZ;j zdePCNJL~^S@il{dropxE{r&$sF%@;Mt^|j#vtc{7rLS*IXXmzc>vpuT^fxfD8yjEF z;FD%;=Kb^YbNcIRy7~9_1qTEe7#T6e?___;KIzWY)TAVqv_@g|d1vQV|Gd6le AETjurl%1GaSk$Y!x!iMJK{pGf`zry3^v)|xli8uSgKj-WW z58WRPOmpU}F*CcAef`dl!x}=t8Q*xfB<5`3Szi0|r?$@NjK_<-Pfw3OaoX6>WQs)8 z>RB7#U1jB1z^L~l?Y!Oe>D!+k@6R_hyk=_p?bWM8i*II%@Nj *pMUGG7f26kO?Ccs>fO7^ z-qU_Kn>F&j1SQ7CH?G^Z$@zIlA3S_`SBmGCM@JnOFTJNBF|(ny_2t9E{@!BoqC(=I zd#?Ze#l1!K?fL)zn^_s9Ywy=vvt+N3)LI`?`Sa7$>2KaR?{j7oa9|GmaP`WY)hlnV zS*BL|A|R_|%e#zM@~p~Y48M0uo89^OnCH$t9u>WP^$L&NTOx~BY8uV@;(oA$^~{-`{ChR=J05UJ ztIu~{acuki1=Vx^H+^s8m;2KG(0;?k^e2lHl$7@+{5$&my#2;ad~Y}LD>Sej_*(t# z&g#{-H5C*#6r~=?_+BQ`WA^>q+uDlH*Mi#xbR|n4zF+W6?(qEj|Lmf_e)m7T{(k?T zM=|RrM(M}zy|v|~88`Rq34CP@42#MSet&)aepA!j{dIq5nQEUn;gMu=x4&s3kAkoS zJD&~Pqyyis#JhdCAa{6y%L2al_O `On|y z@s+lLb%6(&f6MQy`s(V^^7{V%g}bW76&hYdWz_%puy|vkJv+O9>DIRyi{x1sgQ5^L zA0@vp{EDdXglDW}#hRHD8NGMcpE>jX{@&kz-^=St)$afI@3GLcE2R^v|35t3DSSR6 zvi-vyJqL!1TOZus_4V7|-~GWsXWZ0H+zx6aFw8YORr2~8J6qfQth7lvHEZ_&XF9FV z_US^r+p+&=&YX^l7QYh4%pG!}=SY3l;`;v*kuy!!E@@1Q-}j}fF_A+Yv=n^*x_AHo z^LS5>OG`+QkKJR$<;J)pn(Nifd%IX~J=b2X`iVcHB5;G2#FgWzX?A;SXKz@d!p(oa z|NQ*_e;3!af4}~I|0AuY146%#o!x#QP4!1yo1)|o21b|tP4EBymT%{?oibV2)I?_P zWeL{B3?U)4zrX#yb#?oJYYkiaPVisYm1^WIm>~G;|NZ^9kN5xm_w(`P-P@OM&z*Yt z+CwpsC3QEy|DHc%6N9q$@80bEeRY42o;&yH`}+wp&zLzBeu>sxU$^()udm&kH!B%z za1;KkA;IeK;qmeL>F4(uY%b x7$?`7!nwG|1x(3)4#u;+uwZRDrwhOX6BYY zy?_7z`hR~8pRfoye~JYhj`QvQrX(bYi9eV0{kS+m#Nohv`~UWP{wpde^5nSvP5j~S znfyk6$HVXPJavD6GS5^`Oi7H4k1sE{7sFZlbvwWOyqh}=3$Nv{sU7?OWM1V_jw{E{ z965LV+wbrD&(E(nGW^JQaRNJsLX}8O{r`W=VcGg|HH@v2!ps{MNyt81>?p>>YLw!r zxk2vPg9J(2%LN~p)6(4R>b||7U;mG(tmNj5|A9uUxvy})JN=h^{u?Hp9}T^`pO?Rv zi`${FsD4pp%9F#zUgn>c79c`1#w J!@8Sei;*o zf|Qtp|Gb)8TV5VJ*v$X_{{Mpi|9+e2Hzb~w5!m1q7w6#D=kM!#<9hhrYhm-cyGu=& zC-COot=gS;cWdzSd3H5FUS3?xAHVMpQyQb0nVO`eovSOOvhvCW3cj+kh6zTpysHng zH5?V;=2qtCf4%zsf@JrZifhd_aocrtI-EJe@#ay}wU2*)9aUAEwmo_Jib+Bn)Sd_l zr=}+V{>{JZ%FH)*U+3@tUsuN->v;0y$zSj92k3$dr(dk{20Ghvc5Y2L_*2&UUCsZ0 zjg5`H6DP(xGz7c4&gks?`1<<&@9%1NRDS;Y?(X#Eezt3ts0a!znmZ-Mq$-Nhli|jW z+k1XK`&0S++nc@LpPx@>V=GHb3sCU(YT6}dU-xHk?eDu=v-P*pL$5_e*Fhg5BvSKzrX%A-*EH)_xtt< zvKp%ve*FFWyYJW1^zikUzJ0p(>C?ZDf30t>e*f=p`k%kw|6l)Z-_l~yn0o(0?ng!z zfvMd4HmA>@KIM%?!2`AlYtt9Jc%Gk}tgf!Dub^_~ x*rih6&f#Rf~#RpP#?~{DA{WS?gZ#=2+Z5efreL z&mW5!q6Ho}r2Y8wQr%#}`MI^*l5elgot;0|y1e?)k;glCPE%D4Tij?f6N5xl2TysK+l-Jc&P zk2 KGyK8;Rkr!{+?Jis{UbCp?`2G6*%uc<+!M-`NPku&39%%gYU;a;$RDi-}Pmj+t zCA`#@NW|~`mUZ$F_hogt^=s}_e0nlDa`PV-*PU+8MF$u+ bBSafB)wHv1a}M|Nr;r+iMFsxy`>HU+(|^&u@0AkEx)e1DL|v?!BGsqobpJImj$L zTrKy;pZOE_{OtYTbEZfCea(;m&)b(R<4{+>ou6>ID1!;SP5Jtzq7~c9w5`>{uU~mE zMJL#d?O6W*fBXLRuIEZlNtrT1e8tMr58T&J`rg}-c=)Bb{yv>3ku|HdOlD~t9NeD2 z|MRD9=jT?>@OM~{98}=5#?GL-=KuHay!RZ^l9EC$3cY<%Qv5Yg`|PbviHBF}$Jecm z5Y*B+V=1+SneX4fzw4Q9F&_Ky=-Ti7{}qyJo|ygmXZ+{y*KU8mbGvsx|9^k~|9^k} ze*JxZ_wVxeZ*C@wi2v`O**Uq}``DE)7gtWUudhje_~3{CuMVXQmWiz&zs}`9P!;Cs zI$`3(pZE7KjQYUHB2db;@6x5awzj)PLYAnX;oYz~_{Xp3-_?^|P5%GC{?D)PGaDTp zeJf60d+}<|+tp%n#pUz&r+ChoI`vJ)ItMoUx<8!U(R)i@TZ;Pb 7=cn(L z3!m=)zyJT=?;{5i5?61i%CAX&`R;0WUu9KY{C+#Hs9EJE3G8Bj{{8ijjh!&D@$t!@ z;rH$SCnY5P@V{BYxZw@6)G3acmWj##e?ETxZB`bWSlHtuOP*g}KX;C8bkO~VneETd zpN~(uBgT@m^3R{2&);2K&cmbg>(`?Pn`gFbF>okoiPQ+I&3W_k=F0R%M!A(MCUsqy zJ>TAQ?UuTKKaL(de988_zf)g*(C2e2R+zf?t9g5W?oH#dSh42L?$Xzi(&BqLxtUd$ z{)n3_e8Rrc)$+F(3y1foGcWE;owKL#^Rp9*wVN&&Fx3D16B;TS5-PZ__~Fyt>GN5i z{9Li&t7zDwzdyeQySQK9xiU51`|`}6#ryZypVwdiAmILw-~1PZicaaMr7hE6zwfU7 zCl?ow16M*my}z#*1B!{ItoQ%@ H56@0rzHt3LH}?Ge^XTZUjT@^L zS3bV^`TWnHw-4c>0hTAfKASpy?_PH2!&`iOmbfuj zGB9p9otdp}U3~3iHT%4bmX-e(zjO5e|L^b5kIq*P1l+mu#lGY3XY~xb>4HHMCce5< zf2N1O-=1CU`n-8sK0asu|Nk#56n$-jTo8kRgUH&Ph%GNJP1P=++srG|eD!|)|BtW# z*Qb0Dw)u4O#KDh$-^VNb;uLcXZf@K6=g(Qq6LFSTn`h5ozbotg@9+2BjorLIKTJPA z@A&i^Gk0t3`~8c#`huaspR^Q~HnV>ZS{IzRulvkVoNj9U=kNFBODew4xBn0Fz`^hD z>*vf#DczcypSSDlt7p6a>-dE1l@!{-`r~=O{C)fSpC7+UZ|LV1RAl~}p7!U D>Q736Tyn$y$b(o&vv>2Ck?@B96J`~MA3rHq);j?Vx8@BI6he&vOJ z#YKAxpT4R$Gcz&G;FzInU-Nfmnug?zv|m3?UOqcJzxPAT$45t}AMMsVvFfC|*zDRr zAEmPw{7p{$dH&S5zu(`_-|(xgK%xG3;;V%z&sJ4_xN!FK^ZEVL?3nM}_Vv2>_w(1& zUrV?DFE-#^wED uDNRG`)aT#dFRasNWa{|S^#0Y=@Z S za9Qx@r^nyd|L4i*m^k_IfoW$apE_k%_d`QN_w1Royyr@JST5}UKfnI}Hr|lrq-V1% zKfmF~?_U#F`}Oqo@;=2`bDZYfulwuBt)1Y>uJcvSw&KFHiPO^3Z_k|j_wV`n_B}l; z%s;IS6gL}Mx@Ry&cV%CXlh>_f+jH^x%as}@)C&slO)W3CXIh-LA$H@p{?3oLkH ;JXd*YCf@?A$o_=kxpj7tGUP`pc|jrnc;MxE^z8 z+1p!px8=>9Fz3$o`SyWR6J2=@B!%2_ICCNL>5YxQ!`JPRI=I-LXSVs>q@<_Y_=5g( z<-BY3wn|}nb HTPY~!|p|N4Rw+lzJyXd~Z*GeZm7Ziy!_q@JNc?hak=PlTm%rolvqQ}X6mm;TFW%q(pYN*yJ9y{v z{`&uK@7DHD4L)$g`{n2Ign56yOG`37o;0!WcpL9@K6ys1dsgM=-j%-oZ(#K2-}w*H zzN%&{Z%mDgHq5BfWWKGeyz{~DkB`q!^ hwN2)@{CZhJXJ5 ie{XV&kKOY`!{(e5bcFIMOU+2%UNX>KRU24BTJM#XT zS1 &de`=|NQ2ku^{xUO5@hkFM}7e-e+iz-~Ufb z>r?7eiNF7TeDrxfbAL~dgNcUQ_lBoN(wBd7itn?l6O&w;pP}*f_4WCQ0uLA%U7i(9 zi{5_k->=WHe&H<>yPqDP9xoC6xW3U~+w%AJ!k?f0+t<}QyP~yq|6eAtj0MtDZtmFm z?;l%QbimBtuYa4bIn4V1|G(tN$*Bnmb>F^x?l{Zy{ar1?G%p*cnEHsBb#?#y#ZH_# z(<5(RZ?`G&@Avib^Y8B6oqkeCFjdshS2sZ8fBU(I|Nr-YH~2iiK85#)gv5<^ckAt= zzD(eXh>Wzfkhpn6!oqT9Ma7MVxA`aHIJQg`p24jtAvx2)#UU#qV#cXcXUg8!Riqrg z8olk(D$c0=cK>85@;_~|y;1c4*`DHCQC)Lm{T5_C{C`;S7XwqrnWpz|Z@)iqsn@&X zGrw`hE3ws5W@*dnA4vQ?eunAQl)pbupE$yzbFs{SW=-YwyfQt#*E1qw&9~owFq_d} z_az^j1x@eo?#@>>%eJlk#VH|VvuMtQYlc;MKfiW|d+ka&yv)ryTGe>-m&oF}|9?I{ zeI35Q>Vo7F9i9F4|I<>Rv&cIBd(OY^BbT3`pIX|tZt*ys=(5T!I?9C!@7`ZrY=3`$ zdfo4Dk5`9>|9`mSG*k4!c2|~&jJiKRK7V~JuODB>dhPe~X#Y>IIk=@d{`_RR$z!c> z`sqL8;!S(@|9x0;R M@t4ee@A2I;ylkH5b^`I)DfU0H(akAHW+ z*Z==#Y-rebV9i&dlC-b&e2#o45}2kHcK!eVf76DBa|@Zux+5a%{wDolx0>DY_vBpb z>gB$*mA|*m>fP#GrKT=_U;e{C-!BgTKKHve^v<3A_s!1six;=MpEkP0D9+4m&3xFH zt?hS+W<~?6jQ;wDg$w`u{OsTV{LSX5CkJ(pfBybE{zK>c61^`HGj7bNtN&NJ>c@lc z%k{mz@5aQ02*AB+S69cU@bl^C^Y?oeFe Nk*2mbMxoh%g67$vv-Zn zj%B}|*SFc3UsvuHU%7Kf; 2VD tDn?FxY6sc=`CvP0IOw_xV1UuUo&JxlExU zPR6WKP@&<3&JPw=%SOrPuaD0^kaqX||Nk53eL29B^|$#*ee?CR|Jc*K&CCjYF(? zx3By2=J#>_`S e~d1eQW_ V zx_-d)o!hVfJ3T#qb=X|n+2sck7TL~L*zsnu`+V^ytZHV@?En9}pJDN_ VABf{Hcjwa>M4$K676k zI&x%3($SlfH{R!6W>Kj5@$cVX(jitxmo{8rzPs?)$v>Yzzt>i6kI&nkmA!aljq8L7 z0>}I1EzaMS))Mbpyi*wL? Bna4V@4F=YKe8`k;wlBzb@R zzgu@N=`328IcLU~zkh#U-kc^k-~76X?PLCoFMsX5cs3a9tNOutB0&0JdEtrE+#Q@B z_w4%q@b-E8726dLbK3=|i%a})`1AOGeZuA0AK%N{Mef|q&bO!j^DO_UXJ^mO)?XL< zbn@ii|9+p>-}0+-;=}vv_w)PhPe@2GG?cYno>0K}AyVj!aoUbgA2*(zRr)C9irL*x zzNTqH3wH9|TDvegCE;1x?_VE2Pd(Hu?|0|pH?xQx7vdvk2>Y)zZ{e~7U4^N%`2q8> zuB~g|ylqvNn^U>))yfaAPv`r*|Nr-o>s5P)0O!y4?8ndB*UyNEjE~t @9{y|9<~`e(j(8fkVNrrBB3aVq$c%q?C^9+thyk zA-rGazJ1T*^Ae4$clZDQ-|+R=ef#|mtoQfZ-`~f0_|K6ulH7l5o~~INAG3=kOVh~k zFN=8hiN~A|HecO$&-zbG`|R>B4bu)re%rXo=<7GDu8xAOTV<_;_xf(V6l3Hzx3l@@ z`|b8TA#ovxI83HZoci+WJooF_egEpdSScQ?`jk?xr*}=>{vXfj2do?lpSG`2Rgp3< zGKh_+6yBmHxyMM7xAyN(XOH-JW2 7^2E;0#@WAbuaCc9_diWk z eja$>~E| zZ_}+?`uX>-^>j`(FiJ{F|Ie%Q`` pH!Cj>d6Zj8n^yWPX2fP?5CHvcF%t5 zu75Q5dBxY-M@J6a+`QM)BBJi!ALE$AVhRl>?EBAO3%hs2 bogMM!Tr8IqIh;= Z+t8>I;3&zv%3}`Z~wDr1Y)s+7MYWy=nFJ0k11R)a+_ne}Dh}|NH*_;M38Z85t25 zzwgh#W4zB-8qc^9QNO3Qdd0dhO@kM|-`_v+`Otw|>=N nH3BU3=`u&7#bS79epuZb608E zr^@A)>*^0Q2UP6(eV|O3TYdW5BPZLtxvsJQFg7ssiincDe(gU?Sl0i4hEB>YjZ?dy zzWjMTTs(f- 2#hI-%`TO^$_%qX(j+eLo z|Nn1jT>tk=W_Z=9>*ep4ym_nVH|M~q)5=dD{d)Vmyy7a;k{dfB Rr3jMd5AAf)U=ZwsqYw!Q~`B6E| z%uLR*a>0Y|@dmN~C1*rP*4O?kUGwb3{PXcyzxTAZWYh`Yxa|E~ZqAJ#uf9o|<;6)d zyKBlyGe+`;@#(3hrCs>Kv%}!$_wV1Ii}T0b-}~X&yVm2+%NVwLytZ6*!}XJ=#zn=p ztgN?Yd3)*#Ur83SX9gN~+Q)xjHG14H|A&pOYH4cvE^EnMw|1tc=&JjsK6v_@@xI*W z|MtStpU?B#pZ_|S^;6XCIX7>(t-mSD^x$~E{efj}K0VWxFMl*iHT?RzeT7xuoBemc zH{kgEIoj((q(^~d=O)i0 _5Cd$SU41p{h!i1X |MGK66^6q>2%KlV>wDN|wgnz$(zkY2k{!ae( zr$@&!%i M_xUlqk8K7gx4>LY%bNP1Gmo(BuKfJh?X*Y{`-3|F`F4MAZcg8| zYuCDU>#oF}YdS6 -_NWNW3zFR_^ zn_t^lUwQ+}pWhFk@0YKy+yC?9=ZDYb%kBSG{+aoZKSNqWpMCrG`B6E0=1ra2)4;&K z;r`dx*W*v{RVl?Xz5dDjtxNbtTEZKh$W2e4ot-TuCB@0f=^)s^u;|OCg%1+Wekyu? z?%Z4TPv2&rElm`E$g+ORk?0x!ejaYvR4o!DWn@@vaDQg;*Jo=se=54P`T37aTdw^6 z{Q37$-r9b@{OK1=u72tfoD#|&tNG!%@xCku8y%&EnL5js{r+-k;_BOC@wZG~eqS&D z%CB%s-_9i}&Bp2TeCO|bA96Bra%SHFlV1G^|NpTbGoHLcqFv(g{QdRy%G;R#v9lUk zUp~8!`}_3yrlz5<=RdHEs^hi%@ccYzhRM*_I5NsBI?_{o`mFhupZ`2Kc>H*uO=ZFR z$I}=4@0YD*&|_C@R_fvDXp`4Am!93VsA1*KWs4^>1+3q+Nr0RCbHDrntxwlIxZN5U z79ILJskfKUJb&NK4G*tuys@7xcy2|=Psi209eEy2*L*fLvqk2Ao4faFDboz|<$j-8 z*WX +_%3a@VE<(a&l?uT zqwBZM-Dmx>WzL!(O{|T7{=8ZI^n?KGfp1T@=Pxi#5fZv{@Sv2V =c>Hkt zd^!7nfByaDSNQ&RcK-hQ|H7h=A3aVM7kV^d+B4QPZ3X5Nsi~>Iety ?ymqa{-F z=FOSY(^#KBEj%}~_)Eo&w69UzhViZ!^vn`w2 DMzkmDoWM2B5ef)|{t?6CoM$hNx&-ZssQ#VxoS|89H=;3%!$LjZ+Q!~xK z>pc^lI^D2jX{OD`g3Wn1BxX+7@b1FG)#^%iE3Zd(?Ku7S_viQahMeLNGYdA??fUl4 z@A*q}>-EwvwDn@eRxMSvimKr$W$@{GpsK&W;iSY2URBk~l+>RMy^d;XZUy`zTU5CY zF*NP3cU __sdLYV$L^yu4FpGp%v>^XE!s U}5_eRyc_&oFXwXj#-(_1aA zwJ%*cZ2s-c{_2$8Ii0=h;#n@}{{Qz+C7<)ketY|!EG$2kro`Rb@#W2_L`BwEJx9)2 zyJ7++D66=<{=2(g{oB2|ogdHjf8QUWvU8^Dj(^SNVgDM}ooQk<5;mA1`M=QMe|q}A zkME9mmzL{2fB(PiY3KCi|292MJawvR@lv* m*1XgDjvUf_u=`Wfm+lXcE%9XxvE CcaB|+!n2IlIfAr>Ct&G@!9?bpMU?a`}Othsonj@=kLG!_r?3g;Z1w~ zgvK(qx=u5ZwUK3QUh8SWwSlRDA!*^d!fDesvGc2*onK$~Y|G{FZTJ7~_{+x3%s$yh ze)=SaZjBcz+6mX+Jb$cj|E@OW#|Oy>u1_0^f3r!7ibUPKaiPXka>L~6 dyeg6FV8F!=86cW kCW)r-ZStwql=zYs`I zOuY5&8&`4K{MaoI{;d6KesAsmLkEtXU3KF8!XxL;oaVaq;rrL`{q6JX?*2XTf%k>G zn_%6&2PU@{C25 !Us;{d%w1Ve9Gq-KR z`PAF1g!Av!m>30ZHn{%l`}7O1N)x1i-IrI*J{`V2?~dgTuc+&tz1{tMb%Ns0*R9*A zt+B 5(v%4g1ce?wd6KepxqTz+ zmC4-IGa@3M`ZU=7v9JIC&*}9OCfRTA@6Vs|<=fWRe9gPwwkn!^yJf%skBY_9kKe!R z%LUqT`hNYb?zeAOZIs*nnR)XbJxklVIhbM5t}6Bf1^eo6l13S4PjN{-7ZZ7slAJ8( z`)F~3NCG%TY-R1~;bCHuaWL8N@6W%FS7%p?|5&Q>LxttvzkgkJzdnL)wZ6UBeSO2y zxU5apuBj~S#-Goxmv>A*CLpu>@Av=z@7F()^5XZOUswBF$Clg9MQ4)%CwDR9#f&C7 z*2N7HpxrFWN9NCuyYu(w*U2r*Y>c%xBz~7(JD*$UPF>CapAQZSr=_WJ8;f!l>WN*L zDstbUwpgjHy^-xx*T*7Gu9}*%=l}29_ngsL%$Y6H({zxBfhl9jo6CQ`{L$M{A(W89 z5*vB$gFNfTVq@K?O~>_j{C$7F{- B`CGBRs)&iL%ucB1G* zQ(1$p-S*F)SGO;IdT0ZC{r`WWzY3?%{3?8W_v!QJ{~ao=l6@v%xJ`6_PfJN+V8cF^ zse+%&%=diX`}hC(Bd5+CI(G1!`Y~Ol)29xeJ#&mxr}6*azkmPV|Deyv$dutCQgdL} zoAck#pE=Of|Mvd>`uiK_xqSHjeg6CV_RNy|>*~^f{Cs(T|NIZvWcc-e_syMtc~f{^ z)Z|rbReydKpQarC|8HvI)0C97 d?Wz@6X@2 zx8J|1u*fiB?FEOmfA2qd@@Y0>#H)i`c4cML-1*~|q^_^{^MpBqF@P((>BsNiuea~t zU#A(#&Y|6NFVOnW!TI~^zrVZr``_Q{$jC_9*t!1~1PT;{Pj7T!pUl|Ju<)IMneLD8 zU4nPoxvkc`JiorazOL^7hfj|uOUui})|Nhg@%QlQ3YRBGOW&mQ?6F`~{r~sp*$?#< z^O6_@9F&)=K7HcU$>01%Zj%|fnZGsfY82$LYZ8oD*XU(?A@J>)1!wR5|Kxq`|Ig3i zE;FRB@ciMwdAs}Oia) viCAszp8f(*JLV}!$#o<8{X=hg4~@%I(O1r}VdtCy(U@o%@~X;1GB zfB*fR+xqkWV)yxzjJC1;)U>wtjNkP|Dq{Zr+TSmq^Xp~Uvzw;u-~0FD v`71)SUWuaZy&<_Oi2;MR~u!`>!u$K3#F-$^VI=-*`bc z)Ph1pY?|1V;QHSW4qe}Vzv4*5#rK&h2|vEhcJ!K5`ZY;Bq5kRb@A)=&blm-IYQFsV zy#0N>$rtfI|Nee|y!^i6#^~v13d@S8{rvY=KkDH3U+0$F+yC#@dda^)zP|47hkqBh z`k#MaSNHY7^XuOqY(6fzljTUo5fPRpA*?A^B5&+;FaD;Xu 7}wazKK1_mijR(WON&1)9EJ}sR-FXq>hcSjC4JTBr5m@J|5 z_3dr_?Ba)eqwoCx`}_QvGd~_YoW5sMu&u6|+pik?x&=BNiAiZcKE1o&J$t?-tLqU{ zotN*o?%!W;yL-?3qgxlhmy7@X@1dx{MJ8?wu`oWi4dM6h?f>!odbygKm|S3_qNOgU zZZxOv>x*d*G!*nQ|0blR1(y4#KYXxnW9_Y(M?={XP5%UC9bWJTw25iU>X4w@ZZ47E ze@=a^bjs%tm(TyYM9XFoyUMT6<(oGMEMC&WFyZX6V=6i){`@{~pRiJ0=fsD%zxjXY z9+ re!zJ`T0mgUTcZ+3@2Ysa@fxhk@@>BX&$e?R>@ z_O6oOFE1%2EzIEi1oN*CrU#{@J!KU?QNJL$ribT^P1k`%0mlyfyZ)VDe*WG1lC s)9>Tgd78X#?qGHM{7pk4;+vt7(aN1m>;8Xu z^l_%{9w9HUGQs+K-iLhp4h+3Jn5US;Om37t%)b1cq0#1(GykxB3*ET()!~c}{}X?F z;he^CAeGbj_5b|$GxFc2{{HuHyJNw&j!BAl_k4JE@8@RisC|3uSLQrl{Zx*1m)z&? z>W9P+JI`wH>6v7u)>PDWy?pJC`kLqK^XK;{XIcI{3U<(hnt%V&(jH&=a^~NUFVE)w z?QWUM`%~p=+q*}g%v0EWCnu(ct